Notes posted to Ruby on Rails
RSS feedbe aware that this writes to tmp/cache
Its supposed to be http caching, but Rails will actually cache the response to whatever you specified as the cache store, *as well*, but only if you specify :public => true. The default is filestore so it will try to write to tmp/cache.
Only a problem if you don’t have the proper permissions set, in that scenario your apache/nginx logs could fill up very quickly with “permission denied errors”
Full explanation is here http://blog.tonycode.com/archives/418
increment_by_sql for PG
Note, if you’re using the code below for incrementing by SQL with a Postgres database, it’s not going to like the backticks. Just remove them:
def increment_with_sql!(attribute, by = 1) raise ArgumentError("Invalid attribute: #{attribute}") unless attribute_names.include?(attribute.to_s) original_value_sql = "CASE WHEN #{attribute} IS NULL THEN 0 ELSE #{attribute} END" self.class.update_all("#{attribute} = #{original_value_sql} + #{by.to_i}", "id = #{id}") reload end
Use concat insted of joining collection explicitely
concat method will be useful to join the collection object from looping conditions.
arr = ["a", "b", "c"] content_tag(:ul, :class => 'a class') do arr.each do |item| concat content_tag(:li, item) end
And this will generate the html as shown below
<ul class="a class"> <li>a</li> <li>b</li> <li>c</li> </ul>
When dealing with has_many through
The non-repeating primary key id must be used with find_in_batches.
-
User has many things
-
User has many socks through things
-
Sock has many things
-
Sock has many users through things
For the sake of argument, assume the first user has two socks and all other users have one sock. There are 1000 users in total and 1001 socks in total.
User.joins(:socks).count => 1001 agg = [] # Incorrect User.joins(:socks).find_in_batches{|g| agg += g} agg.count => 1000 Sock.joins(:users).count => 1001 agg = [] # Correct Sock.joins(:users).find_in_batches{|g| agg += g} agg.count => 1001
Out of date for Rails 3.
This is out of date for Rails 3; instead see ActiveRecord::Persistence#update_attributes
Beware - May cause performance issues
A serialized attribute will always be updated during save, even if it was not changed. (A rails 3 commit explains why: http://github.com/rails/rails/issues/8328#issuecomment-10756812)
Guard save calls with a changed? check to prevent issues.
class Product < ActiveRecord::Base serialize :product_data end
bad
product = Product.first product.save
good
product = Product.first product.save if product.changed?
Exceptions raised within are ignored.
From http://guides.rubyonrails.org/active_record_validations_callbacks.html
The after_commit and after_rollback callbacks are guaranteed to be called for all models created, updated, or destroyed within a transaction block. If any exceptions are raised within one of these callbacks, they will be ignored so that they don’t interfere with the other callbacks. As such, if your callback code could raise an exception, you’ll need to rescue it and handle it appropriately within the callback.
Default field order
If you want to set an app wide default order for the fields (rather than passing :order each time), use the locale file.
eg. edit config/locale/en.yml to include:
en: date: order: - :day - :month - :year
Checking content_for
There’s a much simpler way to check if content exists or not, and it’s been provided as example in docs since 05.2010:
module StorageHelper def stored_content content_for(:storage) || "Your storage is empty" end end
But this behavior was broken with SafeBuffer. You’ll be able to use it again when this issue (github.com/rails/rails/issues/9360) will be fixed and merged with master.
It also works with strings
It works with strings:
ActiveRecord::Base.connection.column_exists?('users', 'id')
Which is helpful if you need to specify the database/schema to use:
ActiveRecord::Base.connection.column_exists?('secondary.users', 'id')
conditional rescue from does not seem working on Rails 3.2.11
Be careful, conditional rescue_from does not work in Rails 3.2.11
The purpose of this method
This method keeps track of whether a hidden id field needs to be created when the form builder is generating fields for a nested model. It gets set to true by #hidden_field when that method is used to create a field named ‘id’.
Here’s an example of what this can be useful for: http://railsforum.com/viewtopic.php?id=39640
Your scope cannot be called 'locked'
-
see: http://github.com/rails/rails/pull/6985
class Article < ActiveRecord::Base scope :locked, where(... end
will cause intermittent problems of the type
undefined method 'locked' for #<Class:0x007fdab3025298>
Use something like ‘access_locked’ instead
Other Example
##
class Exam cattr_reader :code, :description, :points, :instance_reader => false @@code = "EXM" @@description = "Sent Exam" @@points = 1000 end
In this case it’s possible to use
Exam.code # => EXM Exam.description # => Sent Exam Exam.points # => 1000
Works also with Mongoid
What works for Active Record, also works for Mongoid:
de: mongoid: attributes: picture: explanation: Description
Use sqlite3, not sqlite
Note that typically if you want to connect to an SQLite database the adapter would be “sqlite3”; not “sqlite” as depicted in the documentation above. Just using the term “sqlite” might result in the error message: “database configuration specifies nonexistent sqlite adapter”
Using an unobtrusive Ajax (UJS) :onchange call to the controller#action
An :onchange call can be made using the Rails 3 jquery-ujs helper in the form:
check_box_tag( name, value, checked, html_and_other_options)
For example:
select_tag( "my_tag_id", entity.id, class: "progress bar update_msg", disabled: disabled? data: { remote: true, url: url_for( action: :my_controller_action, id: my_id) // application symbols progress_bar: "progress_bar_div_id", update: "message_div_id" } )
The jquery_ujs looks for data-remote and data-url. These can be spelled-out or placed in the data hash. The url must be formed as select_tag does not call url_for, unlike some of the other related tags. Values for application symbols can also be passed through. jQuery triggers will work on the Ajax events that occur. This generates the following:
<input class="progress_bar update_msg" data-progress-bar="progress_bar_div_id" data-remote="true" data-update="message_div_id" data-url="/my_controller/my_controller_action/my_id" id="my_tag_id" name="my_tag_id" type="checkbox" value="4"/>
In this example, by tying into the events the program makes visible an existing hidden progress bar while awaiting a server response, and then displays a div containing a message returned by the server and hides the progress bar. If the div contains a class= for notice or error, then they will fade out.
$(".layout") .on('ajax:beforeSend', ".progress_bar", function(){ // get id of element to make visible var progress_bar_id = '#' + this.getAttribute('data-progress-bar'); $(progress_bar_id).show(); }) .on('ajax:complete', ".progress_bar", function(){ // get id of element to hide var progress_bar_id = '#' + this.getAttribute('data-progress-bar'); $(progress_bar_id).hide(); }) .on('ajax:complete', ".update_msg", function(evt, xhr, options){ // get id of element to contain message var update = this.getAttribute('data-update'); $("#" + update).replaceWith($(xhr.responseText).attr("id", update)); // cause responses with these classes to fade away... $('.notice').fadeOut(2500); $('.error').fadeOut(8000); });
for rails >= 3
template_root= is deprecated, use prepend_view_path instead
source: ActionMailer/DeprecatedApi/ClassMethods/template_root
Some documentation available in RailsGuides
The Rails engines getting started guide discusses the usage of isolate_namespace: http://edgeguides.rubyonrails.org/engines.html
Minor edit of pluralize_without_count
patrickberkeley’s method works great. I corrected the grammar a bit for inflection (the singular error).
application_helper.rb
def pluralize_without_count(count, noun, text = nil) if count != 0 count == 1 ? "an #{noun}#{text}" : "#{noun.pluralize}#{text}" end end
This should work in much older versions of Rails also.
Helper Methods inside of a Controller
When needing to include a helper method inside of a controller, instead of including the ENTIRE helper module, simply use this method like so:
module ApplicationHelper def fancy_helper(str) str.titleize end end class MyController < ApplicationController def index @title = view_context.fancy_helper "dogs are awesome" end end
Changing time/date separators
@hayafirst — it is possible to remove that “:” or change it to something else. Just pass `:time_separator` option. Inspect ActionView::Helpers::DateTimeSelector#separator method for more.
Using an unobtrusive Ajax (UJS) :onchange call to the controller#action
An :onchange call can be made using the Rails 3 jquery-ujs helper in the form:
select_tag( name, option_tags, misc_html_options, html_5_data-stuff)
For example:
select_tag( "my_tag_id", get_ids(@entity), class: "progress_and_message", data: { remote: true, url: url_for( action: :my_controller_action, id: my_id) // application symbols progress_bar: "progress_bar_div_id", update: "message_div_id" } )
The jquery_ujs looks for data-remote and data-url. These can be spelled out or placed in the data hash. The url must be formed as select_tag does not call url_for, unlike some of the other related tags. Values for application symbols can also be passed through. jQuery triggers will work on the Ajax events that occur. This generates the following:
<select class="progress_and_message" data-progress-bar="progress_bar_div_id" data-remote="true" data-update="message_div_id" data-url="/my_controller/my_controller_action/my_id" id="my_tag_id" name="my_tag_id"><option value=etc...></option>
For example, tying into the events in this case the program makes visible an existing hidden progress bar while awaiting a server response, and then displays a div containing a message returned by the server and hides the progress bar. If the div contains a class= for notice or error, then they will fade out.
$(".layout") .on('ajax:beforeSend', ".progress_and_message", function(){ // get id of element to make visible var progress_bar_id = '#' + this.getAttribute('data-progress-bar'); $(progress_bar_id).show(); }) .on('ajax:complete', ".progress_and_message", function(evt, xhr, options){ // get id of element to contain message and to hide var update = this.getAttribute('data-update'); var progress_bar_id = '#' + this.getAttribute('data-progress-bar'); $("#" + update).replaceWith($(xhr.responseText).attr("id", update)); $(progress_bar_id).hide(); // cause responses with these classes to fade away... $('.notice').fadeOut(2500); $('.error').fadeOut(8000); });
Example
NOTE: you pass all the keys and values in one long list:
fruit = ActiveSupport::OrderedHash[ 'apple', 'Apple', 'banana', 'Banana', 'kiwi', 'Kiwi fruit', ] fruit.keys => ["apple", "banana", "kiwi"]
Now called class_attribute in Rails 3.2.x
See github.com/novafabrica/make_exportable/pull/4
you will be redirect to signin, when you want to create post before signin
code
def test_should_signin_first_before_add_post get "/admin/posts/new" follow_redirect! end
post user authentication info to sessions create action
post_via_redirect(“sessions”, {:user=>{:email=> user.email, :password => user.password}})
Beware: virtual attributes are ignored
Even though validations are called, virtual attributes are ignored.