Flowdock

Recent notes

RSS feed
January 14, 2009
6 thanks

File open permissions

Usage: File.open path, flags, [permissions]

Flags (bitmasks)

Access:

File::RDONLY

Read-only

File::WRONLY

Write-only

File::RDWR

Read and write

If the file exists:

File::TRUNC

Truncate

File::APPEND

Append

File::EXCL

Fail

If the file doesn’t exist:

File::CREAT

Create

Flags (strings)

r

File::RDONLY

r+

File::RDWR

w

File::WRONLY|File::TRUNC|File::CREAT

a

File::WRONLY|File::APPEND|File::CREAT

Examples

File.open path, File::RDONLY
File.open path, 'w'
File.open path, File::WRONLY|File::TRUNC|File::CREAT
File.open path, File::WRONLY|File::TRUNC|File::CREAT, '0666'
January 13, 2009
2 thanks

Easier Universal Partials

@hosiawak provides a great example of how to use polymorphic_path to produce universal partials. You can actually simply his example even more with the following:

<%= link_to 'Edit', edit_polymorphic_path(obj) %>
<%= link_to 'Delete', obj, :method => :delete %>

So the things to note are that in addition to polymorphic_path, Rails also provides an “edit_” version just like on your resources so you can use that instead of specifying the action specifically. The second thing to remember is if you pass just the raw object as the path then then rails will automatically wrap it in a call to polymorphic_path.

January 13, 2009
6 thanks

Universal partial

polymorphic_url is very useful if you want to create an universal partial that works for more than 1 type of object passed to it.

For example in you sidebar you might have a _sidebar.html.erb partial that’s supposed to display links to “Edit” and “Delete” actions. You can write it in such a way that it can be reused for different types of objects (in the example below we pass either a Post or a Note).

your_template.html.erb

<%= render :partial => 'shared/sidebar', :locals => { :obj => Post.new -%>

other_template.html.erb

<%= render :partial => 'shared/sidebar', :locals => { :obj => Note.new -%>

_sidebar.html.erb

<%= link_to "Edit", polymorhpic_url(obj, :action => 'edit') -%>
<%= link_to "Delete", polymorphic_url(obj), :method => :delete -%>
January 12, 2009
2 thanks

A word of warning

This method returns the supplied string with double quotes around it if it has a space in it, otherwise leaves it alone.

This is useful when sending paths to the shell or any similar operations. Here is a small demonstration of how it works and why it would be useful:

def cat(term)
  puts "cat #{term}"
end

def cat_q(term)
  puts "cat #{term.quote}"
end

# No difference without whitespace
cat   "hello.txt"       #=> cat hello.txt    -- OK!
cat_q "hello.txt"       #=> cat hello.txt    -- OK!

# With whitespace
cat   "hello world.txt" #=> cat hello world.txt    -- Error
                        #   trying to find "hello" or "world.txt"

cat_q "hello world.txt" #=> cat "hello world.txt"   -- OK!

Watch out if you want to do things securely, since this method does not escape quotes that are already there!

cat_q 'Peter "Smiley" McGraw.txt' #=> cat "Peter "Smiley" McGraw.txt" -- ERROR!

It is possible to insert fork bombs and other dangerous stuff by taking advantage of this, and that might take down the whole machine, steal data or delete data depending on the rights of the user executing the script.

The insecurity might also transfer into other areas in case you are not using this method for the shell, but for something else.

Please do note that most of these problems can be avoided if you just split the arguments in Ruby when doing system calls, which is easier to do most of the time.

quoted_files = file_list.collect { |f| f.quote }  
system("grep #{search.quote} #{quoted_files.join(' ')}")

system("grep", search, *file_list) # Much easier!

So, in summary: You should only use this method if you know what you’re doing! It’s neither secure nor needed in most (but not all) cases.

January 9, 2009
3 thanks

Adding params to generated url

Whenever you want to append custom parameters to a to be generated url it might be necessary to stop url_for from escaping.

url_for(:action => 'some_action', :custom1 => 'some_value', :custom2 => 'some_value', :escape => false)

If the escape => false option is not passed the generated url contains &amp; instead of the correct &-sign.

January 9, 2009 - (v2.2.1)
0 thanks

Dynamic labels

Code example

<%=label_tag 'category_'+cat.title, cat.title%>
January 8, 2009
10 thanks

Refactoring excessive code for selects

@garg: It is not recommended to have excessive code in the views. You should refactor your code a bit.

<%= f.select(:manufacturer_id, Manufacturer.find(:all).collect {|u| [u.name, u.id]}, :prompt => 'Select') %>

could be changed to this:

# in app/helpers/manufacturer_helper.rb
def manufacturers_for_select
  Manufacturer.all.collect { |m| [m.name, m.id] }
end

# in the view
<%= f.select(:manufacturer_id, manufacturers_for_select, :prompt => 'Select') %>

I would look into collection_select though:

<%= f.collection_select(:manufacturer_id, Manufacturer.all, :id, :name, :prompt => 'Select') %>

It’s much more clean and you don’t have to define a helper for it to be readable (altough it’s still quite long).

If you have to do this often, you should define a FormBuilder extension, so you get methods like f.manufacturer_select:

<%= f.manufacturer_select(:manufacturer_id, Manufacturer.all) %>

IMO, most projects should have a custom form builder anyway, so the addition would be very small. This is my personal opinion, so you don’t have to listen to it. :-)

January 8, 2009
5 thanks

Watch out for syntax errors

Watch out when you are using returning with hashes. If you would write code like

def foo(bars)
  returning {} do |map|
    bars.each { |bar| map[bar.first] = bar }
  end
end

you will get a syntax error since it looks like you tried to supply two blocks! Instead you should write it with parenthesis around the hash:

def foo(bars)
  returning({}) do |map|
    bars.each { |bar| map[bar.first] = bar }
  end
end
January 6, 2009
0 thanks

Alternative force initial value

@Bounga - Thanks for the suggestion. Didn’t know about that method. An alternative method I have used is just to assign the default value to the object in your controller. For example your “new” action might now look like:

def new
  @ecard = Ecard.new params[:ecard]
  @ecard.sender ||= 'contact@host.com'
end
January 6, 2009
1 thank

Replace allowed tags/attributes

The docs above state how to add and remove tags from the default list. But what if you just want to replace the entire list with a list of your own? You can easily do that with the following code:

ActionView::Base.sanitized_allowed_tags.replace %w(strong em b i hr br ul ol li blockquote)
ActionView::Base.sanitized_allowed_attributes.replace %w(href)

Note that if you put this in your initialization block you must use the config.after_initialize hack (to override the default that will be set) but if you put it in an initializer (i.e. a file in the initializers directory) that code is executed after Rails initialization so no need to use any hack. Just use the code above.

January 6, 2009
5 thanks

Default allowed tags and attributes

I found it a bit hard to find the default tags and attributes in the docs.

As of Rails 2.2.2 they are:

Tags

del, dd, h3, address, big, sub, tt, a, ul, h4, cite, dfn, h5, small, kbd, code,
b, ins, img, h6, sup, pre, strong, blockquote, acronym, dt, br, p, div, samp,
li, ol, var, em, h1, i, abbr, h2, span, hr

Attributes

name, href, cite, class, title, src, xml:lang, height, datetime, alt, abbr, width

Getting the latest list

You can query for this list yourself with the following code on the console:

>> puts helper.sanitized_allowed_tags.to_a * ", "
... will output tag list ...
>> puts helper.sanitized_allowed_attributes.to_a * ", "
... will output attribute list ...

The same principal can probably be applied to sanitize_css.

January 2, 2009 - (v2.0.0 - v2.2.1)
3 thanks

What to use instead

For versions 2.0+, use ActiveRecord::Base::sanitize_sql_array

January 2, 2009
2 thanks

current_action? and current_controller?

I use them in link_unless_current_controller helper.

def current_action?(options)
  url_string = CGI.escapeHTML(url_for(options))
  params = ActionController::Routing::Routes.recognize_path(url_string, :method => :get)
  params[:controller] == @controller.controller_name && params[:action] == @controller.action_name
end

def current_controller?(options)
  url_string = CGI.escapeHTML(url_for(options))
  params = ActionController::Routing::Routes.recognize_path(url_string, :method => :get)
  params[:controller] == @controller.controller_name
end
December 29, 2008 - (v2.2.1)
1 thank

Deprecation warning for using options without hash

As of Rails 2.2.0, truncate gives a Deprecation warning if you don’t use a hash for the options. Ex:

The old way

truncate(project.description, 100, "... Read More")

The warning

DEPRECATION WARNING: truncate takes an option hash instead of separate length and omission arguments.

The new way

truncate(project.description, :ommision => "... Read More", :length => 100)
December 28, 2008
1 thank

i think that's a bit better resourceful approach...

Code example

auto_discovery_link_tag :atom, formatted_movies_url(:atom), :title=>'New movies'
December 24, 2008 - (>= v2.2.1)
5 thanks

uninitialized constant ActionView::Base::CompiledTemplates::TimeZone

If you get this error, you need to use ActiveSupport::TimeZone.us_zones instead of TimeZone.us_zones.

Example:

<%= form.time_zone_select(:time_zone, ActiveSupport::TimeZone.us_zones) %>
December 23, 2008
0 thanks

Entries are not standalone!

The entry Pathnames consist solely of leaf filenames, so they’re not directly useable in filesystem operations like “open” or “directory?”. To create a useable Pathname, append the entry onto the directory you’re iterating over.

Also keep in mind that the iteration includes the magic entries “.” and “..”, which you probably want to skip. (Is this true on Windows too?)

The #children method doesn’t have either of these issues, although it’s slightly less efficient since it creates an Array of Pathnames up-front instead of yielding one at a time.

December 23, 2008
0 thanks

Incorrectly named option

The :add_month_number option should be :add_month_numbers

December 18, 2008 - (v2.0.0 - v2.2.1)
0 thanks

Belongs_to, Has_many association

I have a belongs_to, has_many association between manufacturer and modelname. (manufacturer has many modelnames).

In the new modelname page, I have a drop down menu that lists all the manufacturers, so I choose eg Dell, and then in modelname.name field I enter inspiron or what ever. To create that drop down I used:

<%= f.select(:manufacturer_id, Manufacturer.find(:all).collect {|u| [u.name, u.id]}, :prompt => 'Select') %>

The reason that works was explained by fcheung and rsl on #rubyonrails. Thanks:

the form builder basically calls the method whose docs you have read, inserting the appropriate first argument

The object is referenced internally when you call f.whatever. in any of those tags, the object is omitted when used on a form block variable

December 16, 2008 - (v1.0.0 - v2.2.1)
11 thanks

Force initial value

If you want to force an initial value for your text_field which is normally based on your object attribute value, you can use :

text_field :ecard, :sender, :value => 'contact@host.com'
December 12, 2008
7 thanks

acts_as_state_machine named scopes

If you are using the acts_as_state_machine plugin, this will generate all named scopes for your various states.

Place it after the acts_as_state_machine and state declarations.

class Task < ActiveRecord::Base
  acts_as_state_machine :initial => :waiting
  state :waiting
  state :running
  state :finished    

  states.each { |s| named_scope s, :conditions => { :state => s.to_s } }
end

Then doing a Task.waiting will return the corresponding tasks.

December 11, 2008
3 thanks

with resources

/products

current_page?(products_path)

# => true
December 11, 2008 - (>= v1.0.0)
5 thanks

Calling migrations within migrations

It’s very occasionally a wise strategy to call migrations from within other migrations. This is typically done when you are adding a migration that deletes a now-obsolete table.

Let’s say one night when you were drunk or otherwise not thinking straight you did something like this:

class CreateExGirlfriendTexts < ActiveRecord::Migration
  def self(dot)up
    create_table :ex_girlfriend_texts { |t| ... }
  end

  def self(dot)down
    drop_table :ex_girlfriend_texts
  end
end

Oops! You could add this for your “undo” migration the next morning:

class FixDrunkMistake < ActiveRecord::Migration
  def self(dot)up
    CreateExGirlfriendTexts.down
  end

  def self(dot)down
    CreateExGirlfriendTexts.up
  end
end

Now, in the event you decide you really did like that table, you can always get it back easily. Keep in mind this will be made more complicated if your table is modified over multiple transactions.

December 11, 2008
6 thanks

Accessing aggregate methods with :group

You can access aggregate methods (such as SUM, COUNT, etc.) when using a JOIN and GROUP BY query by simply naming the aggregate columns and calling them as methods on the returned objects:

hits_by_page = WebpageHit.all({
  :select => "webpages.*, COUNT(webpage_hit.id) AS view_count",
  :joins => :webpage,
  :group => "webpages.id"
})
homepage_hits = hits_by_page[homepage.id].view_count

The view_count method is added to the Webpage model by this call. Note, however, that this method returns a string, and is not typecasted by Rails.

December 10, 2008
0 thanks

Example

my_instance.connection.clear_query_cache

December 6, 2008
4 thanks

Array expansion in blocks

The syntax can be improved as changing the second parameter of the block (values) and using an array of two variables instead, which will be used by Ruby as the key and value of “array”.

array = [['A', 'a'], ['B', 'b'], ['C', 'c']]

hash = array.inject({}) do |memo, (key, value)|
  memo[key] = value
  memo
end

hash
# => {'A' => 'a', 'B' => 'b', 'C' => 'c'}
December 6, 2008
0 thanks

:cache_path strangeness

I’ve noticed that using an example like this one shown in the docs has some issues with URI escaping:

caches_action :feed, :cache_path => Proc.new { |controller|
     controller.params[:user_id] ?
       controller.send(:user_list_url, controller.params[:user_id], controller.params[:id]) :
       controller.send(:list_url, controller.params[:id]) }
 end

When I do this, the :myroute_url methods return URLs with escaped parameters, as one would expect, but for some reason Rails unescapes these strings by the time it uses them to store the cache blob. This is messing up my cache expiration routines, because they keep track of the escaped versions of the urls that are returned by the routing methods, not the unescaped versions. It would be easier if Rails didn’t do any magic to the string and simply used exactly what you pass to cache_path.