It's no secret that I think plugins are unnecessary. Unfortunately, they are also popular, largely due to to how easy it is to create a plugin and put it in your Rails project.
It's also easy to create a new gem. In 2006 Dr. Nic released the newgem gem. This was a great step in 2006, but it's 2008 now and it doesn't feel like we've moved much in the way of shared ruby code.
We're all to blame.
The vast majority of Ruby work is working with Rails. I do believe the next step for RubyGems is to play seamlessly with Rails. Since RubyGems are great for any Ruby code, it doesn't make sense to add Rails specifics to RubyGems. However, RubyGems could be more opinionated about how gems are structured. Right now, RubyGems library code can be structured in almost any way that you'd like. If the structure followed a stricter convention then auto gem loading code could be written for Rails.
I like newgem, but I think the features provided by newgem should actually be part of RubyGems itself. If the gem command could create a project skeleton it could be opinionated about the file structure without creating a larger barrier to entry.
There is another change that needs to happen. Everyone knows that you should vendor everything for your Rails projects. But, there's no single step to go from uninstalled gem to vendored gem. The path is generally to install it locally and them unpack it in whatever folder you've created that gets autoloaded.
I'm pretty sure a remote unpack is fairly simple, which is why we are all to blame. RubyGems is an open source project. Anyone can contribute patches to make it better.
Lastly, if gems become the standard way to share Ruby/Rails code then we'll need a few rake tasks added to Rails. For example, who wouldn't want a rake command to upgrade to a later version of a gem.
I'm hoping 2008 is the year that shared ruby code becomes synonymous with RubyGems and the year Rails Plugins become unnecessary.
Showing posts with label rails. Show all posts
Showing posts with label rails. Show all posts
Sunday, February 17, 2008
Saturday, February 02, 2008
Ruby: Did TDD make Ruby a viable option?
Some people ask if Test Driven Development (TDD) is what caused Ruby to become popular. Other people smarter than me claim that TDD is in fact the reason that dynamic languages are now viable options.
I'm sorry, but I disagree.
Can you write a non-trivial application in Ruby without tests and have confidence in it? No. Can you write a non-trivial application in Java without tests and have confidence in it? Again, no.
A compiler lets you know that some things are correct, but should not give you confidence that your application behaves as expected. A trivial example is, I need to know that the calculate_tax (that's calculateTax for you Java fans) method returns 56, not that the result is a Fixnum (or Integer in Java).
As much as it should be, TDD is not mainstream. There's no TDD Conference, but RailsConf sells out every year.
I would actually credit David Heinemeier Hansson with making Ruby viable by creating a framework that drove mass adoption. But, I bet of you looked at the majority of Rails applications you would find empty test folders (or only the generated tests, which are never run). I'm quite sure that's true because I expect the conferences to attract the best of the Ruby developers, and several of the people I talk to at those conferences "simply don't have time" to write tests.
I'm sorry, but I disagree.
Can you write a non-trivial application in Ruby without tests and have confidence in it? No. Can you write a non-trivial application in Java without tests and have confidence in it? Again, no.
A compiler lets you know that some things are correct, but should not give you confidence that your application behaves as expected. A trivial example is, I need to know that the calculate_tax (that's calculateTax for you Java fans) method returns 56, not that the result is a Fixnum (or Integer in Java).
As much as it should be, TDD is not mainstream. There's no TDD Conference, but RailsConf sells out every year.
I would actually credit David Heinemeier Hansson with making Ruby viable by creating a framework that drove mass adoption. But, I bet of you looked at the majority of Rails applications you would find empty test folders (or only the generated tests, which are never run). I'm quite sure that's true because I expect the conferences to attract the best of the Ruby developers, and several of the people I talk to at those conferences "simply don't have time" to write tests.
Saturday, January 26, 2008
Ruby/Rails Conferences
Today I was browsing the web for Ruby/Rails conferences to submit speaking proposals to. Unfortunately there doesn't seem to be a list of Ruby/Rails conferences anywhere (that I could find). Hopefully this list will save some time for anyone else looking for a similar list.U. S. Regional ConferencesWhat other Ruby/Rails conferences have I missed?
Saturday, January 12, 2008
Rails: Changing application.rb to application_controller.rb
Update: Koz killed the ticket for now so don't bother +1ing it. I've posted the idea to the Rails Core list already here so I guess we'll all have to live with the flaw for now.
Update #2, Koz et al are on board with making the change assuming the community doesn't come up with any upgrade blockers. Cross your fingers, we might see the change in the near future.
I've always been slightly annoyed that ApplicationController does not follow convention and is defined in application.rb, but I never bothered to do anything about it, until now.
I was recently asked to look over a fairly new codebase that was being actively worked on by someone new to Rails. In one of the controllers I found code similar to the following snippet.
The code functionally works, but I prefer to see environment specific logic in the environment specific files (development.rb, test.rb, and production.rb). I suggested that the developer set the caching logic in the development.rb file. Unfortunately, referencing a controller in development.rb will raise an error because ApplicationController is not yet available. This stems from the fact that
Is the inconsistency a blocker for Rails adoption? Absolutely not, but is it necessary? Again, absolutely not.
I wanted to patch* Rails and remove the inconsistency, so I started by creating a sample app to see what the change will break. Using Rails 2.0.2 I created a sample application and froze the 2.0.2 gems. Next I created a simple controller that would allow me to see what breaks when I change application.rb to application_controller.rb.
At this point it's time to make the file change and see what breaks. After making the filename change I restart my server (using
So after a bit of looking around I found that application.rb was explicitly being referenced in the following places:
and actionpack/lib/action_controller/dispatcher.rb
The special cases defined in these files are actually bypassed (invisibly to the user) when ApplicationController follows naming conventions.
railties/lib/console_with_helpers.rb
Making the change does break
railties/lib/test_help.rb
Line 1 explicitly requires application, again, the simple fix is to remove this line completely.
railties/lib/commands/performance/request.rb
Same as above, remove the explicit require on line 3 and everything just works.
Should you follow the instructions above? Maybe. If you never plan on changing your frozen version of rails then it might be a good idea. If you plan on changing your frozen version at some point but you think it's worth doing this work again at that point then it might also make sense. If you plan on changing your frozen version and you are worried that the change might cause issues in the future then you are probably better off living with the inconsistency for now.
I'm going to go ahead and make the change because I don't enjoy remembering the special case every time I want to Command+t and jump to the file.
Like I said before, I did the research so I could contribute back to the community by way of a Rails patch. I've put together a patch and submitted it, and it's gotten plenty of support, but it needs more, much more I suspect since Koz appears to have put it on hold. So, if you would also like to see the inconsistency removed please add a +1 to the ticket. (at http://dev.rubyonrails.org/ticket/10570)
* If you've ever wanted to submit a patch to Rails but not known where to start, I highly suggest starting at http://dev.rubyonrails.com.
Update #2, Koz et al are on board with making the change assuming the community doesn't come up with any upgrade blockers. Cross your fingers, we might see the change in the near future.
I've always been slightly annoyed that ApplicationController does not follow convention and is defined in application.rb, but I never bothered to do anything about it, until now.
I was recently asked to look over a fairly new codebase that was being actively worked on by someone new to Rails. In one of the controllers I found code similar to the following snippet.
if RAILS_ENV == 'development'
caches_page :index
end
The code functionally works, but I prefer to see environment specific logic in the environment specific files (development.rb, test.rb, and production.rb). I suggested that the developer set the caching logic in the development.rb file. Unfortunately, referencing a controller in development.rb will raise an error because ApplicationController is not yet available. This stems from the fact that
require 'application'
has not yet been executed and due to the naming inconsistency the class can not be auto-loaded. The fix is easy enough, you can require 'application'
before you reference a controller, but I wouldn't expect any Rails novice to know that.Is the inconsistency a blocker for Rails adoption? Absolutely not, but is it necessary? Again, absolutely not.
I wanted to patch* Rails and remove the inconsistency, so I started by creating a sample app to see what the change will break. Using Rails 2.0.2 I created a sample application and froze the 2.0.2 gems. Next I created a simple controller that would allow me to see what breaks when I change application.rb to application_controller.rb.
class HelloController < ApplicationController
def world
render :text => "hello world"
end
end
At this point it's time to make the file change and see what breaks. After making the filename change I restart my server (using
script/server
) and to my surprise, nothing breaks. I refresh my browser and everything just works. Well, that was much better than expected, but I must have broken something or I expect this change would have already been made.So after a bit of looking around I found that application.rb was explicitly being referenced in the following places:
- activesupport/lib/active_support/dependencies.rb
- actionpack/lib/action_controller/dispatcher.rb
- railties/lib/console_with_helpers.rb
- railties/lib/test_help.rb
- railties/lib/commands/performance/request.rb
and actionpack/lib/action_controller/dispatcher.rb
The special cases defined in these files are actually bypassed (invisibly to the user) when ApplicationController follows naming conventions.
railties/lib/console_with_helpers.rb
Making the change does break
script/console
. On line 19 'application' is explicitly required and since this file no longer exists an error is raised. The fix here is simple, remove the explicit require and let auto-loading take care of things.railties/lib/test_help.rb
Line 1 explicitly requires application, again, the simple fix is to remove this line completely.
railties/lib/commands/performance/request.rb
Same as above, remove the explicit require on line 3 and everything just works.
Should you follow the instructions above? Maybe. If you never plan on changing your frozen version of rails then it might be a good idea. If you plan on changing your frozen version at some point but you think it's worth doing this work again at that point then it might also make sense. If you plan on changing your frozen version and you are worried that the change might cause issues in the future then you are probably better off living with the inconsistency for now.
I'm going to go ahead and make the change because I don't enjoy remembering the special case every time I want to Command+t and jump to the file.
Like I said before, I did the research so I could contribute back to the community by way of a Rails patch. I've put together a patch and submitted it, and it's gotten plenty of support, but it needs more, much more I suspect since Koz appears to have put it on hold. So, if you would also like to see the inconsistency removed please add a +1 to the ticket. (at http://dev.rubyonrails.org/ticket/10570)
* If you've ever wanted to submit a patch to Rails but not known where to start, I highly suggest starting at http://dev.rubyonrails.com.
Tuesday, November 13, 2007
Rails: Enumerable#sum
Documentation
The Enumerable#sum method does exactly what you would expect: Sum the elements of the array.
Test
UsageCalculates a sum from the elements. Examples:
payments.sum { |p| p.price * p.tax_rate }
payments.sum(&:price)
This is instead of payments.inject { |sum, p| sum + p.price }
Also calculates sums without the use of a block:
[5, 15, 10].sum # => 30
The default identity (sum of an empty list) is zero. However, you can override this default:
[].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
The Enumerable#sum method does exactly what you would expect: Sum the elements of the array.
Test
require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'
unit_tests do
test "sum the numbers from the array" do
grades = [50, 55, 67, 62, 71, 89, 84, 85, 99]
assert_equal 662, grades.sum
end
end
Monday, November 12, 2007
Rails: Enumerable#group_by
Documentation
The Enumerable#group_by method is helpful for grouping elements of an Enumerable by an attribute or an arbitrary grouping. The documentation provides a good example of how to group by an attribute; however, the group_by method can be used logically group by anything returned from the block given to group_by.
Test
Collect an enumerable into sets, grouped by the result of a block. Useful, for example, for grouping records by date.Usage
e.g.latest_transcripts.group_by(&:day).each do |day, transcripts|
p "#{day} -> #{transcripts.map(&:class) * ', '}"
end
"2006-03-01 -> Transcript"
"2006-02-28 -> Transcript"
"2006-02-27 -> Transcript, Transcript"
"2006-02-26 -> Transcript, Transcript"
"2006-02-25 -> Transcript"
"2006-02-24 -> Transcript, Transcript"
"2006-02-23 -> Transcript"
The Enumerable#group_by method is helpful for grouping elements of an Enumerable by an attribute or an arbitrary grouping. The documentation provides a good example of how to group by an attribute; however, the group_by method can be used logically group by anything returned from the block given to group_by.
Test
require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'
unit_tests do
test "group by grades" do
grades = [50, 55, 60, 62, 71, 83, 84, 85, 99]
expected = {"A"=>[99], "B"=>[83, 84, 85], "C"=>[71], "D"=>[60, 62], "F"=>[50, 55]}
actual = grades.group_by do |grade|
case
when grade < 60 then "F"
when grade < 70 then "D"
when grade < 80 then "C"
when grade < 90 then "B"
else "A"
end
end
assert_equal expected, actual
end
end
Sunday, November 11, 2007
Rails: Enumerable#index_by
Documentation
I've used Enumerable#index_by for 2 different reasons recently. In one instance I needed faster access to find an element in an array. We found that pulling something out of a hash was faster than using the find method of array. We did a few benchmarks similar to the contrived examples below.
The benchmarks for our project revealed even larger gains, thus it made sense to convert our array to a hash and work with that instead of the array.
The other usage we found for index_by was to utilize the fact that index_by overwrites the value instead of appending (which group_by does). We had a list of scores and wanted to group them together. The group_by method could handle that case; however, we only wanted to keep track of the highest score per group. The test below shows an example where a list of grades can be grouped by grade letter and the highest score per grade letter.
Test
UsageConvert an enumerable to a hash. Examples:
people.index_by(&:login)
=> { "nextangle" =>, "chade-" => , ...}
people.index_by { |person| "#{person.first_name} #{person.last_name}" }
=> { "Chade- Fowlersburg-e" =>, "David Heinemeier Hansson" => , ...}
I've used Enumerable#index_by for 2 different reasons recently. In one instance I needed faster access to find an element in an array. We found that pulling something out of a hash was faster than using the find method of array. We did a few benchmarks similar to the contrived examples below.
array = [1..100].to_anote: we couldn't use the
hash = array.index_by { |element| element }
array_bm = Benchmark.measure do
array.find { |element| element = 99 }
end
hash_bm = Benchmark.measure do
hash.include? 99
end
p array_bm.real # => 1.382
p hash_bm.real # => 1.001
include?
method for our particular instance, so I left it out of the contrived example.The benchmarks for our project revealed even larger gains, thus it made sense to convert our array to a hash and work with that instead of the array.
The other usage we found for index_by was to utilize the fact that index_by overwrites the value instead of appending (which group_by does). We had a list of scores and wanted to group them together. The group_by method could handle that case; however, we only wanted to keep track of the highest score per group. The test below shows an example where a list of grades can be grouped by grade letter and the highest score per grade letter.
Test
require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'
unit_tests do
test "index by highest numeric per grade level" do
grades = [50, 55, 67, 62, 71, 89, 84, 85, 99]
expected = {"A"=>99, "B"=>89, "C"=>71, "D"=>67, "F"=>55}
actual = grades.sort.index_by do |grade|
case
when grade < 60 then "F"
when grade < 70 then "D"
when grade < 80 then "C"
when grade < 90 then "B"
else "A"
end
end
assert_equal expected, actual
end
end
Saturday, November 10, 2007
Rails: Integer#even?
Documentation
None
Usage
The Integer#even? method is helpful when adding zebra striping to views.
For example:
Test
None
Usage
The Integer#even? method is helpful when adding zebra striping to views.
For example:
<% @customers.each_with_index do |customer, index| %>
<div style="color:<%= index.even? ? "white" : "gray" %>">
<%= customer.name %>
</div>
<% end %>
Test
require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'
unit_tests do
test "10 is even?" do
assert_equal true, 10.even?
end
end
Friday, November 09, 2007
Rails: String#each_char
Documentation
The String#each_char method is nice for iterating through a string, one character at a time. I generally use regex for string manipulation; however, when context within the string matters, each_char is helpful.
Test
Yields a single-character string for each character in the string. When $KCODE = ‘UTF8’, multi-byte characters are yielded appropriately.Usage
The String#each_char method is nice for iterating through a string, one character at a time. I generally use regex for string manipulation; however, when context within the string matters, each_char is helpful.
Test
require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'
unit_tests do
test "each_char can be used to strip every other charater" do
strip, result = true, ""
"hello world".each_char do |char|
result << char if strip
strip = !strip
end
assert_equal "hlowrd", result
end
end
Thursday, November 08, 2007
Rails: Hash#diff
Documentation
None
Usage
The Hash#diff method is helpful for determining the difference between two hashes.
note: The receiver matches on both keys and values. If the pair is not matched it be returned. (for example: {:a => 1, :b => 3}.diff({:a=>2}) # => {:a=>1, :b=>3})
Test
None
Usage
The Hash#diff method is helpful for determining the difference between two hashes.
note: The receiver matches on both keys and values. If the pair is not matched it be returned. (for example: {:a => 1, :b => 3}.diff({:a=>2}) # => {:a=>1, :b=>3})
Test
require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'
unit_tests do
test "find the difference between two hashes" do
assert_equal({:b=>3}, {:a => 1, :b => 3}.diff({:a=>1}))
end
end
Wednesday, November 07, 2007
Rails: String#constantize
Documentation
Constantize is definitely the most used String method that I utilize while metaprogramming.
Test
Constantize tries to find a declared constant with the name specified in the string. It raises a NameError when the name is not in CamelCase or is not initialized.Usage
Examples
- "Module".constantize #=> Module
- "Class".constantize #=> Class
Constantize is definitely the most used String method that I utilize while metaprogramming.
Test
require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'
unit_tests do
class Klass
end
test "constantize a string representation of a class" do
assert_equal Klass, "Klass".constantize
end
end
Tuesday, November 06, 2007
Rails: String#classify
Documentation
The classify method is helpful when metaprogramming. I generally replace "strings".singularize.camelize with "strings".classify (and the classify implementation calls singularize and camelize for me).
Test
Create a class name from a table name like Rails does for table names to models. Note that this returns a string and not a Class. (To convert to an actual class follow classify with constantize.)Usage
Examples
- "egg_and_hams".classify #=> "EggAndHam"
- "post".classify #=> "Post"
The classify method is helpful when metaprogramming. I generally replace "strings".singularize.camelize with "strings".classify (and the classify implementation calls singularize and camelize for me).
Test
require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'
unit_tests do
test "singularize and camelize a string via classify" do
assert_equal "Name", "names".classify
end
end
Monday, November 05, 2007
Rails: String#singularize
Documentation
I generally use singularize when metaprogrammming to convert from a potentially plural version of a class name to something that I know can be constantized.
Test
The reverse of pluralize, returns the singular form of a word in a string.Usage
Examples
- "posts".singularize #=> "post"
- "octopi".singularize #=> "octopus"
- "sheep".singluarize #=> "sheep"
- "word".singluarize #=> "word"
- "the blue mailmen".singularize #=> "the blue mailman"
- "CamelOctopi".singularize #=> "CamelOctopus"
I generally use singularize when metaprogrammming to convert from a potentially plural version of a class name to something that I know can be constantized.
Test
require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'
unit_tests do
test "change plural word to singular word" do
assert_equal "class_name", "class_names".singularize
end
end
Sunday, November 04, 2007
Rails: String#pluralize
Documentation
I generally use pluralize for creating nice messages for users. The pluralize method can also be helpful for metaprogramming when converting from a class name to a table name.
Test
Returns the plural form of the word in the string.Usage
Examples
- "post".pluralize #=> "posts"
- "octopus".pluralize #=> "octopi"
- "sheep".pluralize #=> "sheep"
- "words".pluralize #=> "words"
- "the blue mailman".pluralize #=> "the blue mailmen"
- "CamelOctopus".pluralize #=> "CamelOctopi"
I generally use pluralize for creating nice messages for users. The pluralize method can also be helpful for metaprogramming when converting from a class name to a table name.
Test
require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'
unit_tests do
test "change singular word to plural word" do
assert_equal "names", "name".pluralize
end
end
Saturday, November 03, 2007
Rails: String#camelize
Documentation
I generally use camelize when metaprogrammming to convert from a underscored version of a class name.
Test
By default, camelize converts strings to UpperCamelCase. If the argument to camelize is set to ":lower" then camelize produces lowerCamelCase.Usage
camelize will also convert ’/’ to ’::’ which is useful for converting paths to namespaces
Examples
- "active_record".camelize #=> "ActiveRecord"
- "active_record".camelize(:lower) #=> "activeRecord"
- "active_record/errors".camelize #=> "ActiveRecord::Errors"
- "active_record/errors".camelize(:lower) #=> "activeRecord::Errors"
I generally use camelize when metaprogrammming to convert from a underscored version of a class name.
Test
require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'
unit_tests do
test "change underscored word to camelized word" do
assert_equal "AClassName", "a_class_name".camelize
end
end
Friday, November 02, 2007
Rails: Hash#assert_valid_keys
Documentation
None
Usage
Hash#assert_valid_keys gives you the ability to verify that each of the keys in the Hash are expected. For example, if you take an options Hash as an argument to a method you may want to validate that only expected options are passed as part of the hash; assert_valid_keys gives you the ability to list the keys you expect.
Test
None
Usage
Hash#assert_valid_keys gives you the ability to verify that each of the keys in the Hash are expected. For example, if you take an options Hash as an argument to a method you may want to validate that only expected options are passed as part of the hash; assert_valid_keys gives you the ability to list the keys you expect.
Test
require 'rubygems'
require 'active_support'
require 'test/unit'
require 'dust'
unit_tests do
test "raise argument error on unexpected key" do
assert_raises ArgumentError do
{:invalid_key => :a, :valid_key => :b}.assert_valid_keys(:valid_key)
end
end
end
Subscribe to:
Posts (Atom)