Saturday, April 15, 2006
Hacking IntelliJ for syntax highlighting
Currently, IntelliJ does not provide support for Ruby. This is quite unfortunate in my opinion since I'm primarily developing in Ruby these days. However, Obie and I found a trick/hack for providing custom syntax highlighting via regular expressions.
IntelliJ provides the ability to set up custom TODO markers by specifying a regular expression. When a custom TODO match is found the style attributes of the text matched are altered by the definition of the TODO. The available style attributes you can change include color, bold, italics, and many more. To create a custom TODO, click CTRL + ALT + S, then click R.
Creating patterns for Ruby range from simple to impossible. I have about 10 defined, such as coloring symbols red
The last part of the hack requires setting the block comment. IntelliJ only performs TODO matching on comments. Therefore, you have to set the begin block comment in your File Types (CTRL + ALT + S, K, Ruby) to something that you are willing to put at the top of every file you wish to be highlighted. We considered using 'require' but in the end decided on '#rh'. For block commenting to correctly work you will need to specify an end block comment also. For this we chose '#/rh'; however, this never appears in our code. Because we want the entire file to appear as a comment, you never need to include the end block comment text.
Hopefully the IntelliJ team is working to include Ruby support. Until then, I hope this hack can provide you some value. Here's what developing Ruby in IntelliJ looks like to me:
IntelliJ provides the ability to set up custom TODO markers by specifying a regular expression. When a custom TODO match is found the style attributes of the text matched are altered by the definition of the TODO. The available style attributes you can change include color, bold, italics, and many more. To create a custom TODO, click CTRL + ALT + S, then click R.
Creating patterns for Ruby range from simple to impossible. I have about 10 defined, such as coloring symbols red
:[a-z][A-Za-z]*\w+ and making classes bold ([A-Z]+[a-z]+)+. Problems occur because any part of the match will be affected. Therefore, checking for ',true' can be problematic. It's impossible to highlight some things without highlighting a comma or parenthesis. Remember, this is just a hack after all.The last part of the hack requires setting the block comment. IntelliJ only performs TODO matching on comments. Therefore, you have to set the begin block comment in your File Types (CTRL + ALT + S, K, Ruby) to something that you are willing to put at the top of every file you wish to be highlighted. We considered using 'require' but in the end decided on '#rh'. For block commenting to correctly work you will need to specify an end block comment also. For this we chose '#/rh'; however, this never appears in our code. Because we want the entire file to appear as a comment, you never need to include the end block comment text.
Hopefully the IntelliJ team is working to include Ruby support. Until then, I hope this hack can provide you some value. Here's what developing Ruby in IntelliJ looks like to me:
Thursday, April 13, 2006
Ruby Initialization Chain Module
The Ruby Initialization Chain module comes from my current project where I was using the Initialization Chain pattern. The Initialization Chain module allows you to call setter methods that set a value and return the instance. This chaining of method calls allows you to initialize an object with one line of code.
Code and Tests:
foo = Foo.new.set_bar("baz).set_cat("dog")If you were to define the setter methods, they would all follow the same basic patterndef bar=(value)The Initialization Chain module assumes you will follow this pattern and removes the need to define them.
@bar = value
end
def set_bar(value)
bar=value
end
Code and Tests:
module InitializationChain
alias :pre_init_chain_method_missing :method_missing
def method_missing(sym, *args, &block)
if matches_set_and_writer_exists?(sym)
self.send convert_set_to_setter(sym), *args, &block
return self
end
pre_init_chain_method_missing(sym, *args, &block)
end
def matches_set_and_writer_exists?(sym)
matches_set?(sym) && writer_exists?(sym)
end
def match_regex
/^set_/
end
def convert_set_to_setter(sym)
"#{sym.to_s.sub(match_regex,'')}=".to_sym
end
def writer_exists?(sym)
self.respond_to?(convert_set_to_setter(sym))
end
def matches_set?(sym)
sym.to_s =~ match_regex
end
end
class Foo
include InitializationChain
attr_accessor :bar
def cat
@cat
end
def cat=(value)
@cat=value
end
end
require 'test/unit'
class InitializationChainTest < Test::Unit::TestCase
def test_matches_set
assert(Foo.new.matches_set?(:set_foo))
end
def test_writer_exists
assert(Foo.new.writer_exists?(:bar))
end
def test_add_dynamic_set_for_attr
foo = Foo.new.set_bar("baz")
assert_equal "baz", foo.bar
end
def test_add_dynamic_set_for_defined_setter
foo = Foo.new.set_cat("dog")
assert_equal "dog", foo.cat
end
end
Tuesday, April 04, 2006
Execute Ruby Tests in IntelliJ
IntelliJ is a great IDE. Unfortunatley, the support for Ruby is lacking. However, using the External Tools you can set up IntelliJ to run your Ruby TestUnit tests. (Jeremy Stell-Smith had the original idea, Obie Fernandez and I tweaked it a bit to get the following directions.)
NOTE: If you put a breakpoint in a ruby file and execute that file the breakpoint will be hit and you will drop into irb in the Run window of IntelliJ.
Additional suggestions.
- Open the settings dialog. (Ctrl + Alt + S)
- Click External Tools. (P)
- Add. (Alt + A)
- Set the name to whatever you want.
- put "ruby" in the Program text box.
- put "$FileDirRelativeToProjectRoot$\$FileName$" in the Parameters text box.
- put "$ProjectFileDir$" in the Working directory text box.
- click OK.
NOTE: If you put a breakpoint in a ruby file and execute that file the breakpoint will be hit and you will drop into irb in the Run window of IntelliJ.
Additional suggestions.
- Create a Keymap for quickly running the tests without the need for a context menu.
- Create an external program that checks valid Ruby syntax. This can be done by adding an additional external program and setting the Program to "ruby", Parameters to "-c $FileName$", and the Working directory to "$FileDir$"
Rails schema cloning
While recently working through an Oracle schema cloning issue we were using
However, even after making the above change in
After a brief look at the list generated by
This is easy enough to remember once you learn, but I'm not sure that it's the most intuitive option. To me,
If it's truly necessary to have a task that generates specifically
rake db:test:clone to clone the database. We noticed that the cloning was not working correctly; therefore, we needed to go back to dumping the schema to development_structure.sql. This is easily done by changing config.active_record.schema_format = :sql in environment.rb.However, even after making the above change in
environment.rb, rake db:test:clone kept creating a schema.rb file and not a development_structure.sql. This surprised us, because running rake did create a development_structure.sql and did not create a schema.rb.After a brief look at the list generated by
rake -T, we noticed the rake test:db:clone_structure task. rake test:db:clone_structure does generate development_structure.sql.This is easy enough to remember once you learn, but I'm not sure that it's the most intuitive option. To me,
rake db:test:clone is very generic and should behave similar to rake by reading the environment.rb file and creating the expected file.If it's truly necessary to have a task that generates specifically
schema.rb or development_structure.sql they could be name more explicitly as test:db:clone_using_ruby and test:db:clone_using_sql, or something along those lines. Though, I have to wonder if anyone is actually setting their environment.rb to one option and running rake with the other option.
Migration Class and File naming
When running
Unfortunately, I missed the simple mistake. I had renamed the class to CreateFoosTable, because in Rails table names are plural.
Match the file name with the class name and everything works fine. A simple mistake that hopefully now we can both easily avoid in the future.
rake migrate I recently faced this output:Not the most informative message in the world. Running with trace didn't really help me out either. The error I made, on it's own, is fairly easy to spot, but I was also trying to load records of type Foo in this migration. Because I was trying to load records, I thought perhaps the error was occurring in the local definition of the Foo class.
C:\work>rake migrate
(in C:/work)
rake aborted!
uninitialized constant CreateFooTable
(See full trace by running task with --trace)
C:\work>
Unfortunately, I missed the simple mistake. I had renamed the class to CreateFoosTable, because in Rails table names are plural.
Match the file name with the class name and everything works fine. A simple mistake that hopefully now we can both easily avoid in the future.




