Saturday, March 24, 2007
Rails: ActiveRecord Unit Testing part II
Back in December I wrote about unit testing ActiveRecord models. Following that post, we started using that pattern to unit test our models. It quickly became obvious that the ActiveRecord::ConnectionAdapters::Column.new("digits", nil, "string", false) statements were largely static and we wrapped this behavior in a fluent interface.
This worked, but some developers on the team didn't like the (arguably) unnecessary additional syntax. These developers argued that since none of our Unit Tests hit the database we should simply override the new method and handle stubbing the columns method.
You can argue that adding magic to the new method could be confusing, but our team has the standard that no unit test can hit the database. Therefore, everyone is very aware that, despite the standard syntax, we are injecting our own column handling mechanism.
The code to make this work is fairly straightforward. The usual alias method gotchas apply so I chose to use an alternative. Adding the following code will allow you to create disconnected models in your unit tests simply by calling new.
This worked, but some developers on the team didn't like the (arguably) unnecessary additional syntax. These developers argued that since none of our Unit Tests hit the database we should simply override the new method and handle stubbing the columns method.
You can argue that adding magic to the new method could be confusing, but our team has the standard that no unit test can hit the database. Therefore, everyone is very aware that, despite the standard syntax, we are injecting our own column handling mechanism.
The code to make this work is fairly straightforward. The usual alias method gotchas apply so I chose to use an alternative. Adding the following code will allow you to create disconnected models in your unit tests simply by calling new.
class << ActiveRecord::Base
new_method = instance_method :initialize
define_method :initialize do |*attributes|
attributes = attributes.empty? ? nil : attributes.first
self.stubs(:columns).returns(no_db_columns(attributes))
new_method.bind(self).call attributes
end
def no_db_columns attributes
return [] if attributes.nil?
attributes.keys.collect do |attribute|
sql_type = case attributes[attribute].class
when " " then "integer"
when "Float" then "float"
when "Time" then "time"
when "Date" then "date"
when "String" then "string"
when "Object" then "boolean"
end
ActiveRecord::ConnectionAdapters::Column.new(attribute.to_s, nil, sql_type, false)
end
end
end
Labels: ActiveRecord, rails, RailsConf2007, ruby, unit testing
Friday, March 23, 2007
Ruby: === operator
Recently I was looking at creating a patch for Mocha that would allow you to specify a class as an argument. The feature was added to allow you to specify that an argument can be any instance of the class specified.
The === method of Object is defined as:
object.expects(:do_this).with(Fixnum, true, 99)To support this new feature I looked into the === method.
object.do_this(2, true, 99) # satisfies the expectation
The === method of Object is defined as:
Case Equality—For class Object, effectively the same as calling #==, but typically overridden by descendents to provide meaningful semantics in case statements.There's no mystery here, use === like ==. However, the === method of Module provides the behavior I'm looking for.
Case Equality—Returns true if anObject is an instance of mod or one of mod‘s descendents. Of limited use for modules, but can be used in case statements to classify objects by class.The documentation can be tested easily to ensure the behavior I'm looking for.
Fixnum === 2 #=> trueAn important thing to note is that 2 and Fixnum are not commutative.
2 === Fixnum #=> falseSo, using the === method I was able create the following patch that provides the behavior I was looking for.
Index: trunk/test/unit/expectation_test.rb
===================================================================
--- trunk/test/unit/expectation_test.rb (revision 109)
+++ trunk/test/unit/expectation_test.rb (working copy)
@@ -30,6 +30,16 @@
assert expectation.match?(:expected_method, 1, 2, 3)
end
+ def test_should_match_calls_to_same_method_with_expected_parameter_values_or_class
+ expectation = new_expectation.with(1, Fixnum, Object)
+ assert expectation.match?(:expected_method, 1, 2, 3)
+ end
+
+ def test_should_match_calls_to_same_method_with_expected_parameter_values_or_class
+ expectation = new_expectation.with(1, Fixnum, Object)
+ assert expectation.match?(:expected_method, 1, Fixnum, 3)
+ end
+
def test_should_match_calls_to_same_method_with_parameters_constrained_as_expected
expectation = new_expectation.with() {|x, y, z| x + y == z}
assert expectation.match?(:expected_method, 1, 2, 3)
Index: trunk/lib/mocha/expectation.rb
===================================================================
--- trunk/lib/mocha/expectation.rb (revision 109)
+++ trunk/lib/mocha/expectation.rb (working copy)
@@ -22,9 +22,6 @@
class InvalidExpectation < Exception; end
class AlwaysEqual
- def ==(other)
- true
- end
end
attr_reader :method_name, :backtrace
@@ -43,7 +40,15 @@
end
def match?(method_name, *arguments)
- (@method_name == method_name) and (@parameter_block ? @parameter_block.call(*arguments) : (@parameters == arguments))
+ return false unless @method_name == method_name
+ return @parameter_block.call(*arguments) unless @parameter_block.nil?
+ return true if @parameters.is_a? AlwaysEqual
+ return false unless @parameters.size == arguments.size
+ @parameters.inject([0, true]) do |result, arg|
+ result[1] &&= (arguments[result.first].is_a?(Module) ? arg == arguments[result.first] : arg === arguments[result.first])
+ result[0] += 1
+ result
+ end.last
end
# :startdoc:
Should Rails include Mocha?
I like the way the Rails integration tests currently depend on Mocha.
I've previously written that I think xUnit frameworks should include Mocks. Since Rails bakes in testing, I believe it should provide you with the full array of testing tools, including Mocks.
Thoughts?
# integration_test.rbIn fact, I stole the idea and changed it up a bit for sqldsl.
...
begin # rescue LoadError
require 'mocha'
...
rescue LoadError
$stderr.puts "Skipping integration tests. `gem install mocha` and try again."
end
unless File.directory? File.dirname(__FILE__) + '/../vendor/mocha-0.4.0/'However, now I'm wondering if Rails should depend on, and include Mocha.
raise "mocha 4.0 is required to run the test suite. create the 'vendor' directory as
a sibling of test and 'gem unpack mocha' in 'vendor'"
end
$:.unshift File.dirname(__FILE__) + '/../vendor/mocha-0.4.0/lib/'
require File.dirname(__FILE__) + '/../vendor/mocha-0.4.0/lib/mocha'
I've previously written that I think xUnit frameworks should include Mocks. Since Rails bakes in testing, I believe it should provide you with the full array of testing tools, including Mocks.
Thoughts?
Thursday, March 22, 2007
Rails: ActiveRecord Serialize method
On my current project we have a model with a few attributes that are instances. Generally, this is handled with a relationship (e.g. belongs_to). However, these attributes are not ActiveRecord::Base subclass instances. ActiveRecord handles this situation by providing the ActiveRecord::Base.serialize class method.
As a contrived example, imagine a UserAccount class that has a AuthorizationConfirmation instance as an attribute.
A simple test proves the expected behavior.
A quick peek in the database shows the instance as yaml.
As a contrived example, imagine a UserAccount class that has a AuthorizationConfirmation instance as an attribute.
class CreateModels < ActiveRecord::MigrationAs you can see in the above example, the UserAccount class has the attribute authorization_confirmation. The value stored in authorization_confirmation is expected to be an instance of the AuthorizationConfirmation class.
def self.up
create_table :user_accounts do |t|
t.column :authorization_confirmation, :string
end
end
def self.down
drop_table :user_accounts
end
end
class UserAccount < ActiveRecord::Base
serialize :authorization_confirmation, AuthorizationConfirmation
end
class AuthorizationConfirmation
attr_accessor :fingerprint, :key
end
A simple test proves the expected behavior.
require File.expand_path(File.dirname(__FILE__) + "/../test_helper")The test proves that the instance of AuthorizationConfirmation is saved and retrieved as expected.
class UserAccountTest < Test::Unit::TestCase
def test_authorization_confirmation_is_serialized_and_deserialized
account = UserAccount.new
account.authorization_confirmation = AuthorizationConfirmation.new
account.authorization_confirmation.fingerprint = "Xc1sseE"
account.authorization_confirmation.key = "gEteEQ"
account.save
retrieved = UserAccount.find account.id
assert_equal "Xc1sseE", retrieved.authorization_confirmation.fingerprint
assert_equal "gEteEQ", retrieved.authorization_confirmation.key
end
end
A quick peek in the database shows the instance as yaml.
id | authorization_confirmationNOTE: At the time of this writing any attribute that is serialized cannot be nil. Ticket 7293 should resolve this problem.
----+------+-------------------------------------------------------------------------------
6 | --- !ruby/object:AuthorizationConfirmation
fingerprint: Xc1sseE
key: gEteEQ
Labels: ActiveRecord, rails, ruby, serialize
Saturday, March 17, 2007
Ruby: sqldsl 1.4.0 released
This morning I released the 1.4.0 version of sqldsl. This release is mostly in response to the suggestions that sqldsl has recently gotten from the Ruby Community. The new version can be retrieved via "gem install sqldsl" as soon as the file propagates. For now, it can be downloaded from the download page.
Breaking Changes:
Table and column aliasing is now done with an 'as' method instead of using hashes. This change was necessary since column order is important and hashes are unordered.
The new syntax is the same, except it uses == instead of =.
Inner Joins are now also supported
Or conditions are surrounded by parenthesis.
Breaking Changes:
Table and column aliasing is now done with an 'as' method instead of using hashes. This change was necessary since column order is important and hashes are unordered.
irb> Select[:column1.as(:book), 1.as(:total), 'foo'.as(:constant)].to_sqlEquality in the where clause has been changed from a single equals to double equals. I personally preferred the single equals; however, it broke in scenarios where columns were not prefixed by table names. The previous solution did not work because ruby assigned a local variable instead of raising a method_missing and allowing the ReceiveAny class to handle the assignment.
=> "select column1 as book, 1 as total, 'foo' as constant"
irb> Select.all.from[:table1.as(:aliased_table_name)].to_sql
=> "select * from table1 aliased_table_name"
The new syntax is the same, except it uses == instead of =.
Select[:column1].from[:table1].where doNew Feature:
column1 == 99
end.to_sql
=> "select column1 from table1 where column1 = 99"
Inner Joins are now also supported
Select.all.from[:t1.as(:a)].inner_join[:t2.as(:b)].on doBug Fix:
a.id == b.id
end.inner_join[:t3.as(:c)].on do
b.id2 == c.id
end.to_sql
=> "select * from t1 a inner join t2 b on a.id = b.id inner join t3 c on b.id2 = c.id"
Or conditions are surrounded by parenthesis.
Select[:column1].from[:table1].where do
column1.equal 0
end.or do
column1 > 100
end.to_sql
=> "select column1 from table1 where column1 = 0 or (column1 > 100)"
Friday, March 16, 2007
Rails: Presenter Pattern
The default architecture for Ruby on Rails, Model View Controller, can begin to break down as Controllers become bloated and logic begins to creep into view templates. The Presenter pattern addresses this problem by adding another layer of abstraction: a class representation of the state of the view.
Presenter was inspired by the various GUI patterns documented by Martin Fowler.
How It Works
The Presenter pattern addresses bloated controllers and views containing logic in concert by creating a class representation of the state of the view. An architecture that uses the Presenter pattern provides view specific data as attributes of an instance of the Presenter. The Presenter's state is an aggregation of model and user entered data.
When To Use It
As non-trivial applications grow Controllers can approach sizes that compromise maintainability. Complex controller actions may require instantiation of multiple objects and take on the responsibility of aggregating data from various objects. A Presenter can encapsulate this aggregation behavior and leave the controller with more focused responsibilities. This also allows for testing of aggregation or calculation behavior without a dependency on setting up a Controller to a testable state.
Since views are templates they can also contain behavior. The most common behavior you encounter in a view is formatting; however, since the view is a template you will occasionally see computations or worse. Because this logic is stored in a template it can be problematic to test. Presenters address this problem by pulling all formatting, computation, and any additional behavior into a class that can be easily tested.
Presenters do add an additional layer, and thus more complexity. A presenter is not likely to be a automatic decision or standard. Presenters are generally introduced when actions are required to act upon various models or the data in the database needs to be manipulated in various ways before it is displayed in the view.
Example
The example used to demonstrate a usage of the Presenter pattern is a page that allows you to enter your address information, personal information, and user credentials.

To get to our example page (and the following page) we'll need to add a few routes to routes.rb.
Used appropriately, Presenters greatly benefit an application's architecture and maintainability
Presenter was inspired by the various GUI patterns documented by Martin Fowler.
How It Works
The Presenter pattern addresses bloated controllers and views containing logic in concert by creating a class representation of the state of the view. An architecture that uses the Presenter pattern provides view specific data as attributes of an instance of the Presenter. The Presenter's state is an aggregation of model and user entered data.
When To Use It
As non-trivial applications grow Controllers can approach sizes that compromise maintainability. Complex controller actions may require instantiation of multiple objects and take on the responsibility of aggregating data from various objects. A Presenter can encapsulate this aggregation behavior and leave the controller with more focused responsibilities. This also allows for testing of aggregation or calculation behavior without a dependency on setting up a Controller to a testable state.
Since views are templates they can also contain behavior. The most common behavior you encounter in a view is formatting; however, since the view is a template you will occasionally see computations or worse. Because this logic is stored in a template it can be problematic to test. Presenters address this problem by pulling all formatting, computation, and any additional behavior into a class that can be easily tested.
Presenters do add an additional layer, and thus more complexity. A presenter is not likely to be a automatic decision or standard. Presenters are generally introduced when actions are required to act upon various models or the data in the database needs to be manipulated in various ways before it is displayed in the view.
Example
The example used to demonstrate a usage of the Presenter pattern is a page that allows you to enter your address information, personal information, and user credentials.

To get to our example page (and the following page) we'll need to add a few routes to routes.rb.
ActionController::Routing::Routes.draw do |map|The example relies on three different models: address, user_account, user_credential. The migration for creating these tables is straightforward.
map.with_options :controller => 'order' do |route|
route.complete "complete", :action => "complete"
route.thank_you "thank_you", :action => "thank_you"
end
...
end
class CreateModels < ActiveRecord::MigrationThe models need not contain any behavior for our example.
def self.up
create_table :user_accounts do |t|
t.column :name, :string
end
create_table :addresses do |t|
t.column :line_1, :string
t.column :line_2, :string
t.column :city, :string
t.column :state, :string
t.column :zip_code, :string
end
create_table :user_credentials do |t|
t.column :username, :string
t.column :password, :string
end
end
def self.down
drop_table :user_accounts
drop_table :addresses
drop_table :user_credentials
end
end
class Address < ActiveRecord::BaseThe template for our example view is also straightforward, and this is one of the large benefits for using a presenter.
end
class UserAccount < ActiveRecord::Base
end
class UserCredential < ActiveRecord::Base
end
<% form_for :presenter do |form| %>And, the last example before we dive into the Presenter will be the controller. The controller is also simple, thus maintainable, due to the usage of the Presenter.
<table>
<tr><td colspan="2">Billing Information:</td></tr>
<tr>
<td>Name</td>
<td><%= form.text_field :name %></td>
</tr>
<tr>
<td>Address Line 1</td>
<td><%= form.text_field :line_1 %></td>
</tr>
<tr>
<td>Address Line 2</td>
<td><%= form.text_field :line_2 %></td>
</tr>
<tr>
<td>City</td>
<td><%= form.text_field :city %></td>
</tr>
<tr>
<td>State</td>
<td><%= form.text_field :state %></td>
</tr>
<tr>
<td>Zip Code</td>
<td><%= form.text_field :zip_code %></td>
</tr>
<tr><td colspan="2">Account Information:</td></tr>
<tr>
<td>Username</td>
<td><%= form.text_field :username %></td>
</tr>
<tr>
<td>Password</td>
<td><%= form.text_field :password %></td>
</tr>
</table>
<%= submit_tag "Complete Order" %>
<% end %>
class OrderController < ApplicationControllerFinally, the CompletePresenter aggregates all the data for the view.
def complete
@presenter = CompletePresenter.new(params[:presenter])
redirect_to thank_you_url if request.post? && @presenter.save
end
def thank_you
end
end
class CompletePresenter < PresenterThe CompletePresenter does inherit from Presenter, but only to get Forwardable behavior and a constructor that allows you to create an instance with attributes set from a hash.
def_delegators :user_account, :name, :name=
def_delegators :address, :line_1, :line_2, :city, :state, :zip_code
:line_1=, :line_2=, :city=, :state=, :zip_code=
def_delegators :user_credentials, :username, :password, :username=, :password=
def user_account
@user_account ||= UserAccount.new
end
def address
@address ||= Address.new
end
def user_credentials
@credentials ||= UserCredential.new
end
def save
user_account.save && address.save && user_credentials.save
end
end
class PresenterBy using the presenter an easily testable layer has been created. This additional layer can coordinate with the models that this view is responsible for. The added layer also allows the models to be tested independent of any controller behavior. The presenter also provides the ability for extension where other solutions prove inadequate. For example, in a real scenario, the models would also likely contain validations. The presenter provides a layer that can validate the various models and merge their errors collections to provide one error collection that the view can work with.
extend Forwardable
def initialize(params)
params.each_pair do |attribute, value|
self.send :"#{attribute}=", value
end unless params.nil?
end
end
Used appropriately, Presenters greatly benefit an application's architecture and maintainability
Labels: gui patterns, presenter, rails, ruby
Monday, March 12, 2007
Ruby: LocalJumpError workaround
I was recently working on some code very similar to the following distilled example.
In my codebase the method_with_additional_behavior is defined in a superclass, and the subclasses use this method to create methods with additional behavior. I (believe I) could have forced them to use lambda and gotten around the problem, but that wouldn't be very friendly. I could also define another method, such as "__#{method_name}__", and call the underscored method from the desired method, but that would have left my classes with an additional method that should never be called in isolation.
I solved the problem by defining a method, storing that method in a local variable and then overriding the newly defined method with my additional behavior and a call to the method stored in the local variable.
class FooExecuting the above code gives the following error.
attr_accessor :some_val
def self.method_with_additional_behavior(method_name, &block)
define_method method_name do
# do some stuff before calling block
instance_eval &block
# do some stuff after calling block
end
end
method_with_additional_behavior :foo do
return 0 if self.some_val.nil?
some_val * 2
end
end
Foo.new.foo
LocalJumpError: unexpected returnThe error occurs because the
return statement attempts to return from the method_with_additional_behavior. There are a few ways to solve this, but I also had some constraints that influenced my decision.In my codebase the method_with_additional_behavior is defined in a superclass, and the subclasses use this method to create methods with additional behavior. I (believe I) could have forced them to use lambda and gotten around the problem, but that wouldn't be very friendly. I could also define another method, such as "__#{method_name}__", and call the underscored method from the desired method, but that would have left my classes with an additional method that should never be called in isolation.
I solved the problem by defining a method, storing that method in a local variable and then overriding the newly defined method with my additional behavior and a call to the method stored in the local variable.
class Foo
attr_accessor :some_val
def self.method_with_additional_behavior(method_name, &block)
define_method method_name, &block
new_method = instance_method(method_name)
define_method method_name do
# do some stuff before calling block
new_method.bind(self).call
# do some stuff after calling block
end
end
method_with_additional_behavior :foo do
return 0 if self.some_val.nil?
some_val * 2
end
end
Foo.new.foo #=> 0
Labels: block, define_method, instance_eval, instance_method, lambda, LocalJumpError, metaprogramming
Friday, March 09, 2007
Ruby: Why sqldsl?
I recently wrote about introducing sqldsl into my current project. This led to a few conversations on what are the pros and cons of using sqldsl.
Cons:
Cons:
- Additional characters required in simple statements.
"select column1 from table1
where name = '#{person_name}'"
# becomes
Select[:column1].from[:table1].where do
name = person_name
end - Another library creates a larger barrier to entry when dealing with a foreign code base.
- Objects know how to quote themselves.
1.to_sql #=> "1"
"foo".to_sql #=> "'foo'"
:column.to_sql #=> "column"
[1, 'foo', :column].to_sql #=> "1, 'foo', column" - Syntax Highlighting
- Better than string concatenation to generate SQL statements.
"escaped_values = hash.values.collect { |value| sql_escape value }
insert into #{hash.keys.join(", ")} values #{escaped_values.join(", ")}"
# becomes
Insert.into[hash.keys.to_sql].values[hash.values.to_sql] - Much better support for conditional statement building.
"select column1 from table1, table2
where table1.column1 = table2.table1_id
#{"and table1.column2 >= #{quantity}" if quantity > 0}"
# becomes
Select[:column1].from[:table1, :table2].where do
table1.column1 = table2.table1_id
table1.column2 >= quantity if quantity > 0
end - Fail without requiring a database trip for referencing tables in the where statement that do not appear in the table list.
Select[:column1].from[table1].where do
table1.column1 = table2.column1
end #=> Raises an error stating that table2 does not exist in the from clause. - Open for extension where necessary. For example, ActiveRecord::Base.execute could be changed to take a block.
class ActiveRecord::Base
If this change were made, the sqldsl library could use the connection to create vendor specific sql. This allows for things like changing sql to be prepared statement compatible when the oracle adapter supports this in the future. The point is, as long as your sql is nothing more than a string it is not able to be easily modified. However, an object representation of your sql could be evaluated in various contexts to produce different results.
alias original_execute execute
def execute(arg, &block)
original_execute block.call.to_sql(connection)
end
end
Ruby: instance_eval and class_eval method definitions
In yesterday's entry on Cleaning up Controller Tests you can find the following code.
The above examples are from actual code, but the essence of the issue can be captured in a much simpler example.
Example 5 shows that using def in a class_eval defines an instance method on the receiver (Foo in our example). However, using def in an instance_eval defines an instance method on the singleton class of the receiver (the singleton class of Foo in our example).
This explains why example 3 does not work: the valid_person method was being defined on the singleton class of the controller test class and not as an instance method of the controller test class.
As I previously stated, using def in an instance_eval defines an instance method on the singleton class of the receiver. This also explains how using def in an instance_eval when the receiver is an instance of a class defines a method on that instance.
# example 1An earlier version of this code only used one call to the class_eval method.
...
test_class.class_eval do
define_method :setup do
...
end
end
test_class.class_eval &block
...
# example 2Example 2 works when your controller tests contain only test methods.
...
test_class.class_eval do
define_method :setup do
...
end
instance_eval &block
end
...
# example 3However, Example 2 stops working if your test class contains any helper methods.
controller_tests do
test "should add the params" do
assert_equal 4, Math.add(2,2)
end
test "should multiply the params" do
assert_equal 9, Math.multiply(3,3)
end
end
# example 4So, what's the difference between example 1 and example 2? The context in which the block is evaluated.
controller_tests do
test "should not save if first name is nil" do
person = valid_person
person.first_name = nil
assert_equal false, person.save
end
def valid_person
Person.new(...)
end
end
The above examples are from actual code, but the essence of the issue can be captured in a much simpler example.
# example 5As you can see in example 5, using def in an instance_eval is different from using def in a class_eval method call.
Foo = Class.new
Foo.class_eval do
def bar
"bar"
end
end
Foo.instance_eval do
def baz
"baz"
end
end
Foo.bar #=> undefined method ‘bar’ for Foo:Class
Foo.new.bar #=> "bar"
Foo.baz #=> "baz"
Foo.new.baz #=> undefined method ‘baz’ for #<Foo:0x7dce8>
Example 5 shows that using def in a class_eval defines an instance method on the receiver (Foo in our example). However, using def in an instance_eval defines an instance method on the singleton class of the receiver (the singleton class of Foo in our example).
This explains why example 3 does not work: the valid_person method was being defined on the singleton class of the controller test class and not as an instance method of the controller test class.
As I previously stated, using def in an instance_eval defines an instance method on the singleton class of the receiver. This also explains how using def in an instance_eval when the receiver is an instance of a class defines a method on that instance.
# example 6I've already explained where methods are stored in the various examples; however, the following example serves to prove my assertion.
Foo = Class.new
foo = Foo.new
foo.instance_eval do
def instance_bar
"instance_bar"
end
end
foo.instance_bar #=> "instance_bar"
# example 7How does define_method fit into this picture?
class Object
def singleton_class
class << self; self; end
end
end
Foo = Class.new
Foo.class_eval do
def instance_method_of_Foo; end
end
Foo.instance_eval do
def instance_method_of_Foos_singleton_class; end
end
Foo.instance_methods(false).inspect
#=> ["instance_method_of_Foo"]
Foo.singleton_class.instance_methods(false).inspect
#=> ["instance_method_of_Foos_singleton_class", "new", "superclass", "allocate"]
foo = Foo.new
foo.instance_eval do
def instance_method_of_foos_singleton_class; end
end
foo.singleton_class.instance_methods(false).inspect
#=> ["instance_method_of_Foo", "instance_method_of_foos_singleton_class"]
# example 8As you can see from example 8 define_method always defines an instance method on the target (Foo in our example). This makes sense because define_method is being call on the implicit self, which evaluates to the receiver (Foo) in our example.
Foo = Class.new
Foo.class_eval do
define_method :instance_method_of_Foo do end
end
Foo.instance_eval do
define_method :another_instance_method_of_Foo do end
end
Foo.instance_methods(false).inspect
#=> ["another_instance_method_of_Foo", "instance_method_of_Foo"]
# example 9By combining examples 5 and 8 I can create the following code that should no longer be surprising.
Foo.class_eval do
self #=> Foo
end
Foo.instance_eval do
self #=> Foo
end
class ObjectThanks to Ali Aghareza, Pat Farley, and Zak Tamsen for collaborating with me on this.
def singleton_class
class << self; self; end
end
end
Foo = Class.new
Foo.instance_eval do
def an_instance_method_of_the_singleton_class
end
define_method :an_instance_method do
end
end
Foo.instance_methods(false).inspect
#=> ["an_instance_method"]
Foo.singleton_class.instance_methods(false).inspect
#=> ["an_instance_method_of_the_singleton_class", "new", "superclass", "allocate"]
Labels: class_eval, def, define_method, instance_eval, metaprogramming, ruby
Conferences: Presenting at GoRuCo
I'm happy to announce that I will be presenting at Gotham Ruby Conference 2007. The conference is already sold out, but you still have time to add your name to the waiting list.
The topic I will be covering is Business Natural Language Ruby Systems.
The topic I will be covering is Business Natural Language Ruby Systems.
A Business Natural Language is a Domain Specific Language where subject matter experts use natural language to represent business logic....
Labels: Business Natural Language, conference, goruco, gotham ruby conference, ruby
Blog: New Url
My blog has moved to a new location.
For Viewing: http://blog.jayfields.com
RSS: http://blog.jayfields.com/rss.xml
Atom: http://blog.jayfields.com/atom.xml
Please update your links or subscriptions.
For Viewing: http://blog.jayfields.com
RSS: http://blog.jayfields.com/rss.xml
Atom: http://blog.jayfields.com/atom.xml
Please update your links or subscriptions.
Labels: blog
Thursday, March 08, 2007
Rails: Clean up Controller Tests
Traditional Rails Controller tests begin with code very similar to the following code.
require File.dirname(__FILE__) + '/../test_helper'If you use generators you may not notice or care about all this boiler plate code; however, if you generally create your files from scratch you may be interested in the following method that I add to Object (in my test_helper.rb).
# Re-raise errors caught by the controller.
class SomeController; def rescue_action(e) raise e end; end
class SomeControllerTest < Test::Unit::TestCase
def setup
@controller = SomeController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
end
class ObjectThe above code does basically the same thing, but it allows me to define my controller tests in a much cleaner way.
def controller_tests(&block)
full_path_file_name = eval "__FILE__", block.binding
test_name = File.basename(full_path_file_name, ".rb")
controller_name = test_name.chomp("_test")
require controller_name
controller = controller_name.camelize.constantize
controller.class_eval do
def rescue_action(e)
raise e
end
end
test_class = eval "module Functionals; class #{test_name.camelize} < Test::Unit::TestCase; self; end; end"
test_class.class_eval do
define_method :setup do
@controller = controller.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
end
end
test_class.class_eval &block
end
end
require File.dirname(__FILE__) + '/../test_helper'This change also provides the benefit of automatic test class renames when the test file name changes.
controller_tests do
test "should display order number index" do
get :index
...
end
test "should create new order on new" do
get :new
...
end
end
Labels: controller, rails, ruby, test
Friday, March 02, 2007
Ruby: sqldsl new version
I recently put sqldsl to work on my current project. Part of putting it into our codebase was extending it to support some additional syntax. The new syntax support was extracted and put into the 1.2.2 release of sqldsl.
The 1.2.2 release adds a few minor features and a significant change.
Significant:
The where block now allows you to specify conditions using traditional ruby syntax.
Minor:
The not_null method was added to the options available in where conditions.
The 1.2.2 release adds a few minor features and a significant change.
Significant:
The where block now allows you to specify conditions using traditional ruby syntax.
Select[:column1].from[:table1, :table2].where doThis change creates very nice looking code in the where block, but I'm not entirely sure it's a step in the right direction. While it looks great in the above example it also allows you to use variables, which can be confusing.
table1.column1 >= 1
table1.column2 <= 20
table1.column3 = table2.column1
table1.column4 = 'foo'
end
some_val = 1In the above example column1 and some_val look very similar, but create very different output in the generated sql string. The alternative is to use the existing equal syntax.
Select[:column1].from[:table1].where do
column1 = some_val
end.to_sql #=> "select column1 from table1 where column1 = 1
Select[:column1].from[:table1].where doIn the previous example it is very clear that column1 is a column and some_val is a variable. The problem with this solution is that it begins to fall down when you consider <=, >=, etc. To support conditions with these operators we would have needed to implement the following (arguably ugly) syntax.
equal :column1, some_val
end
Select[:column1].from[:table1].where doWhen faced with this decision we opted for the example that simply allowed ruby syntax.
less_than_equal :column1, some_val
end
Minor:
The not_null method was added to the options available in where conditions.
Select[:column1].from[:table1].where doThe is_not_in method was added to String, Numeric, and Symbol.
not_null :column2
end.to_sql #=> "select column1 from table1 where column2 is not null"
Select[:column1].from[:table1].where doTable and column aliasing was also added in this release.
:column1.is_not_in do
Select[:column1].from[:table2]
end
end.to_sql #=> "select column1 from table1 where column1 not in (select column1 from table2)"
Select[:column1 => :total].from[:table1 => :results].to_sql #=> "select column1 as total from table1 as results"sqldsl 1.2.2 is available via a gem install.
> gem install sqldsl

