Posts Tagged ruby on rails

Careful with @results.delete method in Rails 3. You may not be working with an Array

I was working in a method that needed to remove an element from the resultset that was returned when I called this:

@widgets = Widget.active  #active is just a scope I created in the Widget model
@widgets.delete(some_widget)

I would expect this to delete the object from the Array and my view would not render that particular element in the collection. But I found this element being deleted from the database. How is that possible??? It turns out that this collection was still an ActiveRecord::Relation object and not an Array. Since the new query API lazy loads the queries, they are not executed until they get to the view… and not turned into an Array object until they get to the view. I was trying to remove an element from the Array in my controller. To figure this out, I just put a debug statement to return the class of @widgets.

p @widgets.class
#=>ActiveRecord::Relation

In order to force the Relation into an Array, you can call the “all” function which will eager load it in the controller.

@widgets = Widget.active.all  #call all to eager load
@widgets.delete(some_widget)

This time, “some_widget” was only removed from the @widgets Array and not deleted in my database.

Tags: , , ,

Snow Leopard upgrade pain…

So I decided to upgrade to Snow Leopard last week in order to use the iOS SDK and crank out some iPhone/iPad apps.  As with any major upgrade I was expecting a few minor problems, but it really wreaked havoc on my Rails development environment.  The first problem was with MySQL.  The previously installed version did not work at all after the upgrade.  I could not get the server to start.  I downloaded the newest DMG of mysql server and installed it on my Mac.  I chose 32 bit MySQL, since I mistakenly thought my Mac was 32 bit after I ran the “uname -a” command and it showed i386.  So after some experimenting and further research, I downloaded the 64 bit DMG, since my Mac is actually 64 bit capable (if your processor info says Intel Core 2 Duo, then you are 64 bit).  In order to keep my existing data from the crapped out MySQL server (prior install for Mac 10.5), I had to stop the running server, delete the contents of the mysql data directory, copy my old data from the old data dir, and restart the server.  This fixed my database problems.  The next odd thing was my C Compiler.  I tried to install the mysql gem and received this error:

Building native extensions.  This could take a while…
ERROR:  Error installing mysql:
ERROR: Failed to build gem native extension.

/Users/me/.rvm/rubies/ruby-1.9.2-rc2/bin/ruby extconf.rb
checking for mysql_query() in -lmysqlclient… /Users/me/.rvm/rubies/ruby-1.9.2-rc2/lib/ruby/1.9.1/mkmf.rb:368:in `try_do’: The complier failed to generate an executable file. (RuntimeError)
You have to install development tools first.
from /Users/me/.rvm/rubies/ruby-1.9.2-rc2/lib/ruby/1.9.1/mkmf.rb:435:in `try_link0′

Looking at the mkmf.log, showed me that it was having problems with the readline library.  Apparently the system readline installed with Snow Leopard does not play well with the ruby install.  So  I ran the following and RVM was able to use the correct readline library:

rvm package install readline
rvm install 1.9.2 -C --with-readline-dir=$HOME/.rvm/usr

This solved the readline problem, but another problem appeared after this:

ld: in /usr/local/lib/libxml2.2.dylib, file was built for i386 which is not the architecture being linked (x86_64)

There were are also problems with SQLite and libxslt.  I found this post which solved all 3 of these problems. Thanks Mark!

After correcting these problems, Ruby 1.9.2 installed fine with RVM and I was able to install the mysql gem without errors.

Tags: , , ,

Using the collection parameter in a Ruby on Rails partial

Here is one my brother showed me the other day that I was not aware of. More RTFM and less Cowboy Coding.. I know, I know.. its just my nature. So here is the use case: You have a collection of objects that you want rendered in a partial. The way I used to do it is like this:

#controller code
@widgets = Widget.all

# view code
<%= render :partial=>"widgets/widget" %>

#partial code
<% @widgets.each do |widget| %>
  This widget is named <%=widget.name %> 

<% end %>

Now that is kinda ugly doing the iteration in the partial like that. Now for a cleaner way. You pass the @widgets to the partial with the :collection hash.

#controller code
@widgets = Widget.all

# view code
<%= render :partial=>"widgets/widget", :collection=>@widgets %>

#partial code
  This widget is named <%=widget.name %> 

This code will execute the partial multiple times.. once for each widget in the widget collection. That looks damn pretty. One caveat, though.. In the partial, the name of the local variable needs to be the same as the name of the partial. So if you have widgets/some_widget, the code would look like this:

  This widget is named <%=some_widget.name %> 

If you want to use another variable name you just add the :as hash, like this:

<%= render :partial=>"widgets/some_widget", :collection=>@widgets, :as=>:widget %>

Then you can use widget as the local variable name instead of some_widget. Got it? Good.

Tags:

star symbol in front of Ruby def argument

you may have seen something like this and wondered what the star symbol inside the argument parenthesis was:

def some_method(*args)
  #cowboy code goes here
end

The star symbol in front of args means that this method can accept an unlimited number of arguments. This is commonly used in Ruby on Rails when extracting options from the method arguments:

options = args.extract_options!

extract_options! is an ActiveSupport method that pulls out the option hashes from the method arguments.

Tags: ,

Ruby on Rails dynamic finder – find_or_create method

Here is another one I was not aware of. I knew you could use dynamic finders to look for one or more attributes, but I did not realize you could do a find and create at the same time (well.. not really both of them at once, but you know what I mean)

Blog.find_or_create_by_title("Some Blog")

If there is a blog with title = “Some Blog”, then it will pull this blog out of the table. If the blog does not exist, a new Blog is created with the title “Some Blog”. Alternately you can use find_or_initialize_by_name and it will not save the object to the database immediately.. it will be available in the scope of your code, just as if you called blog = Blog.new(:title=>”Some Blog”)

This could come in handy in certain situations and save you a line of code. Daddy like

Tags: ,

Overriding Rails ActiveRecord object getter/setter fields

Lets say you have a widget model in Ruby on Rails.  Your database table contains a field called “name”.  Let’s say you want to add something to this field in your model.  You need a specialized def in order to do this.. you need to use write_attribute and read_attribute methods.

Here is an example where I override the Rails ActiveRecord model attribute setter. This will append a test string onto the name:

class Widget < ActiveRecord::Base
  def name=(name)
    write_attribute(:name,"#{name} - test")
  end
end

You can also do this with the read_attribute method and it works the opposite way… instead of operating on the setter, you operate on the getter.

Tags:

SQL IN – Do it in Rails find using an array

I thought this was pretty cool and a little more elegant than using a string, which I have been doing before I saw this. In SQL you can do a command like so:

SELECT * FROM widgets WHERE id IN (1,2,3,4,5);

I was doing this before:

Widget.find(:all,:conditions=>"id in (1,2,3,4,5)")

Instead of doing this you can use an array instead of a string:

Widget.find(:all,:conditions=> {:id => [1,2,3,4,5])

That looks better. Guess I should RTFM next time.

Tags:

Ruby on Rails error – Missing Attribute: attribute_name – ActiveRecord::MissingAttributeError

ActiveRecord::MissingAttributeError in MyController#my_action
.....
missing attribute: my_attribute

So I got this error message today after I added a column to my database.  WTF??  The column exists.. why can’t ActiveRecord see it?  oh.. maybe because I am stupid.  In my find command I was using the :select option

@widgets = Widget.find(:all, :select=>"id,name")

ActiveRecord can’t see any other attributes aside from what you specify in the :select option, so I just needed to add my attribute to the :select option

@widgets = Widget.find(:all, :select=>"id,name,my_attribute")

Tags: ,

Alternating CSS classes with Rails

Lets say you have a list of widgets.  You want to display them in a <div> with alternating background colors like this.  Using Rails you can do this by creating 2 CSS classes and alternating them for each element.  Note: If you are lucky enough to develop for users with CSS3 compatible browsers you may want to look at nth-child selector as recommended by Spazzy in the comments section of this post.

test
test2
test3

Ruby on Rails provides an easy way to alternate between 2 different CSS styles using the “cycle” view helper.  Basically it lets you enter 2 options and every time the cycle helper is called it displays alternating options that you specify.  Here is the code in action:

<% for widget in @widgets %>
<%=widget.name%>
<% end %>

So each iteration is different.  If you had 3 widgets this code would spit out the following HTML

<div class=”some-class-even”>test</div>

<div class=”some-class-odd”>test2</div>

<div class=”some-class-even”>test2</div>

Tags: ,

Using named_scope for object filtering

UPDATE (7/29/2010) – If using Rails 3, then use “scope” instead of named scope.  Check out the Rails 3 query API also.

One of the cool new features in Ruby on Rails is the named_scope.  You can put a named_scope in your model as a way to do a customized find by just calling a name.  Here is an example of a simple named_scope.  This named scope gets all the widgets that are blue.  You can call it like this: Widget.blues

class Widget < ActiveRecord::Base
  named_scope :blues, :conditions=> "color='blue'"
end

Thats pretty cool huh? Very DRY if you are going to be using this quite a bit. Here is a more complex example where you can insert a parameter using lambda.

  named_scope :above_price, lambda { |*args| {:conditions => ["price>?",args.first] } }

  #get all widgets with price above $25
  Widget.above_price(25)

Even cooler…
Now here is something I like even better that I was not expecting, but it seems to work. I am able to chain these together to filter out different widgets based on criteria. This is useful in an application that needs to allow users to filter out certain content based on criteria. Lets say you have a list full of widgets of all kinds, but the user only wants to see widgets that are blue and cost more than $25. You can add some check boxes and inputs to your view with a submit button and let the user choose different criteria so they can find the widget they are looking for. Then you can filter out the widgets with named scopes in your controller

widgets = Widget.all
if params[:is_blue]
  widgets = widgets.blues
end
if params[:above_price]
  widgets = widgets.above_price(params[:above_price])
end
#now you would have all the blue widgets above whatever price the user entered in the field "above_price"

#If I remember correctly, you can also call them by chaining them together if you need to
widgets = widgets.blue.above_price(25)

I think this is a pretty elegant solution for doing object filtering in Rails. Go named_scope! You crazy!

Tags: ,