Notes posted by autonomous

RSS feed
January 27, 2011
1 thank

Dont reject! on the yielded batch

If you remove any values from the batch, the while loop in find_in_batches breaks even if there are additional batches:

People.count # => 3000

People.find_in_batches do |peeps|
  peeps.reject!(&:bad?)
  # ... more operations on peeps
  puts 'Tick'
end

Running the above code, you’ll only see Tick once. Rather use:

People.find_in_batches do |peeps|
  peeps = peeps.reject(&:bad?)
  # ... more operations on peeps
  puts 'Tick'
end

You should see Tick outputted 3 times

October 2, 2009
2 thanks

Making ActiveRecord models readonly

To force an ActiveRecord model to be read only you can do something along these lines:

class DelicateInfo < ActiveRecord::Base

 def readonly?
  true
 end

end

When you try to save the model it will raise an ActiveRecord::ReadOnlyRecord exception:

info = DelicateInfo.first
info.save # => ActiveRecord::ReadOnlyRecord

Note, however, that destroy and delete will still work on the model unless you intercept those calls

October 1, 2009
0 thanks

ActiveResource validation is a little different

Given the following model on the remote end:

class Person < ActiveRecord::Base
 validates_presence_of :first_name, :last_name, :email
end

And this ActiveResource on the client end:

class Person < ActiveResource::Base
 self.site = "http://api.people.com:3000/" 
end

Validation messages will only be returned after an attempted save call on the client end - eg:

person = Person.new( :first_name => 'Billy', :emails => "william@anotherplanet.co.za" )
person.valid?                 # => true
person.errors.full_messages   # => []
person.save                   # => false
person.valid?                 # => false
person.errors.full_messages   # => ["Last name can't be empty"]

In ActiveResource::Base it is suggested that you can perform client site validation with something like this:

class Person < ActiveResource::Base
 self.site = "http://api.people.com:3000/"
 protected
  def validate
   errors.add("last name", "can't be empty") if last_name.blank?
  end
end
September 12, 2008
24 thanks

Readable strftime

%a - The abbreviated weekday name (“Sun”)

%A - The full weekday name (“Sunday”)

%b - The abbreviated month name (“Jan”)

%B - The full month name (“January”)

%c - The preferred local date and time representation

%d - Day of the month (01..31) %H - Hour of the day, 24-hour clock (00..23)

%I - Hour of the day, 12-hour clock (01..12)

%j - Day of the year (001..366)

%m - Month of the year (01..12) %M - Minute of the hour (00..59)

%p - Meridian indicator (“AM” or “PM”)

%S - Second of the minute (00..60)

%U - Week number of the current year, starting with the first Sunday as the first day of the first week (00..53)

%W - Week number of the current year, starting with the first Monday as the first day of the first week (00..53)

%w - Day of the week (Sunday is 0, 0..6)

%x - Preferred representation for the date alone, no time

%X - Preferred representation for the time alone, no date

%y - Year without a century (00..99) %Y - Year with century

%Z - Time zone name %% - Literal “%” character t = Time.now t.strftime(“Printed on %m/%d/%Y”) #=> “Printed on 04/09/2003” t.strftime(“at %I:%M%p”) #=> “at 08:56AM”

August 18, 2008
0 thanks

Last element of an array alternative

You can also access the last element of an array with -1

[ "w", "x", "y", "z" ][-1]  #=> "z"
July 30, 2008
0 thanks

Set time zone in before filter

To set your time zone you could create a before_filter in your application.rb controller

class ApplicationController < ActionController::Base

 before_filter :set_timezone

 def set_timezone
   Time.zone = 'GMT'
 end

end
July 30, 2008
7 thanks

Using gmail SMTP server to send mail

First you would need to sign up with Google Apps, which is a very painless process:

http://www.google.com/a/cpanel/domain/new

Next you need to install a plugin that will allow ActionMailer to make a secure connection to google:

script/plugin install git://github.com/caritos/action_mailer_tls.git

We need this due to transport layer security used by google.

Lastly all you need to do is place this in your environment.rb file and modify it to your settings:

ActionMailer::Base.smtp_settings = {
 :address => "smtp.gmail.com",
 :port => 587,
 :domain => "your.domain_at_google.com",
 :authentication => :plain,
 :user_name => "google_username",
 :password => "password"
}
July 28, 2008
15 thanks

Friendlier error message example

The default error messages can be a bit stale and off putting. Try somethings like this:

error_messages_for(
  :user, 
  :header_message => "Oops - We couldn't save your user!", 
  :message => "The following fields were a bit of a problem:", 
  :header_tag => :h1
)

You can also use error_messages_for as follows

<%  form_for User.new do |f| %>
  <%=  f.error_messages :header_message => "..." %>
<%  end  %>
July 23, 2008
14 thanks

Pass id collections with check box tags

It can be useful to pass a collection of ids to a controller, especially in the case of a has_many relationship, for example:

User has_many Roles

In your view you could have something like:

<ul>
  <% @roles.each do |role| %>
      <li>
        <%= check_box_tag 'role_ids[]', role.id -%>
        <%= h role.name -%>
      </li>
  <% end %>
</ul>

Note the square brackets after role_ids - this is important for passing a collection through to the controller.

If you place this in a form and submit it, you can expect to see a param passed into the controller that looks like:

"role_ids"=>["1", "2", "3"]
July 22, 2008
19 thanks

select_tag with options_for_select example

An example of using options_for_select with select_tag

select_tag 'user_id', options_for_select(@users.collect{ |u| [u.name, u.id] })

This would generate something like:

<select id="user_id" name="user_id">
  <option value="1">Brad</option>
  <option value="2">Angie</option>
  <option value="3">Jenny</option>
</select>
July 22, 2008
19 thanks

Nested resources in form_for

If you like doing things RESTfully and have a model relationship like:

Post_ has many Comments_

Then you can construct a form_for within your view to mirror this relationship when creating comments:

form_for [@post, @comment] do |f|
  ...
end

You also need to make sure your routes reflect this relationship:

map.resources :post, :has_many => [:comments]
July 21, 2008
14 thanks

helper method to partial

concat can be useful for rendering a block to a partial from a helper:

def block_to_partial(partial_name, options = {}, &block)
  options.merge!(:body => capture(&block))
  concat(render(:partial => partial_name, :locals => options), block.binding)
end

This would be particularly useful if you had some partial to help you out with rounded corners, for example. So, in your helper:

def rounded_corners &block
  block_to_partial("shared/rounded_corners", {}, &block)
end

In your view you could have something like:

<% rounded_corners do -%>
     This text is surrounded by rounded corners
<% end -%>

You would have to create some partial in

app/views/shared/rounded_corners.html.erb

And it would look something like:

<div class='c1'>
  <div class=c2>
    .
    .
    .
    <%= body -%>
  </div>
</div>
July 20, 2008
10 thanks

Generate an observer

Generating an observer from the command line follows the usual pattern:

script/generate observer audit

This will create a model called:

app/models/audit_observer.rb