autosave option in rails
The documentation for has_many for autosave option says:
[:autosave] If true, always save the associated objects or destroy them if marked for destruction, when saving the parent object. If false, never save or destroy the associated objects. By default, only save associated objects that are new records.
That’s not very clear. Let’s jump into code to see where autosave makes a difference.
has_many association
I have a simple has_many association.
create_table :cars, :force => true do |t| t.string :name end create_table :brakes, :force => true do |t| t.string :name t.integer :car_id end class Car < ActiveRecord::Base has_many :brakes end
with no autosave
Given below are x cases where autosave is not declared.
class Car < ActiveRecord::Base
has_many :brakes
def self.lab
car = Car.new(:name => 'honda')
car.brakes.build(:name => 'b1')
car.save
car.reload
puts car.brakes.inspect #=> [#<Brake id: 25, name: "b1", car_id: 23>]
end
end
class Car < ActiveRecord::Base
has_many :brakes#, :autosave => true
def self.lab
car = Car.create(:name => 'honda')
car.brakes.build(:name => 'b1')
car.save
car.reload
puts car.brakes.inspect #=> [#<Brake id: 26, name: "b1", car_id: 24>]
end
end
class Car < ActiveRecord::Base
has_many :brakes#, :autosave => true
def self.lab
car = Car.create(:name => 'honda')
car.brakes.create(:name => 'b1')
car.save
car.reload
puts car.brakes.inspect #=> [#<Brake id: 27, name: "b1", car_id: 25>]
end
end
I am not sure what new record, the doc was referring to. Here I have three different examples of parent and child combination and in all of three cases everything works as expected without autosave option declaration.
with_autosave option
If I try to manipulate an in memory association object then that works only with autosave option. Here is an example.
class Car < ActiveRecord::Base
has_many :brakes, :autosave => true
def self.lab
car = Car.create(:name => 'honda')
car.brakes.create(:name => 'b1')
car.save
car.reload
car.brakes[0].name = 'b2'
car.save
car.reload
puts car.brakes.inspect #=> [#<Brake id: 27, name: "b2", car_id: 25>]
end
end
In the above code I am manipulating the in-memory instance of brakes. And that works only with autosave turned on. In the above code if I do not declare autosave option then the final result will have b1 instead of b2.
So it is clear that if you are manipulating in-memory instance of associated record then use autosave option.
Wait there is more fun with validation
Now I am going to add validation that brake must have name. First let’s try with autosave turned on.
class Brake < ActiveRecord::Base
belongs_to :car
validates_presence_of :name
end
class Car < ActiveRecord::Base
has_many :brakes, :autosave => true
def self.lab
car = Car.create(:name => 'honda')
car.brakes.create(:name => nil)
car.save
car.reload
puts car.brakes.inspect #=> []
end
end
Final result is empty because I did not pass name for brake. All is good. Let’s check if I get any error message.
class Brake < ActiveRecord::Base
belongs_to :car
validates_presence_of :name
end
class Car < ActiveRecord::Base
has_many :brakes, :autosave => true
def self.lab
car = Car.create(:name => 'honda')
car.brakes.create(:name => nil)
car.save
car.reload
puts car.brakes.inspect #=> []
puts car.errors.full_messages.inspect #=> ["Brakes name can't be blank"]
end
end
We got the error message. Everything looks great.
Now let’s try validation without any autosave declaration
class Brake < ActiveRecord::Base
belongs_to :car
validates_presence_of :name
end
class Car < ActiveRecord::Base
has_many :brakes
def self.lab
car = Car.create(:name => 'honda')
car.brakes.create(:name => nil)
car.save
car.reload
puts car.brakes.inspect #=>
puts car.errors.full_messages.inspect #=> ["Brakes is invalid"]
end
end
The error message is slightly different but other than that everything else looks good.
When I introduce validation error then in both the cases (with autosave and without autosave) the full transaction is rolledback. It mean no change occurrs in parent if any child has validation error. So far so good.
In has_one association things are not consistent
I have a has_one association here.
class Capital < ActiveRecord::Base
belongs_to :state
validates_presence_of :name
end
class Country < ActiveRecord::Base
has_one :capital
def self.lab
country = Country.new(:name => 'india')
country.build_capital(:name => nil)
country.save
puts country.inspect #=> #<Country id: 41, name: "india">
puts country.errors.full_messages.inspect #=> []
end
end
class Capital < ActiveRecord::Base
belongs_to :state
validates_presence_of :name
end
class Country < ActiveRecord::Base
has_one :capital, :autosave => true
def self.lab
country = Country.new(:name => 'india')
country.build_capital(:name => nil)
country.save
puts country.inspect #=> #<Country id: nil, name: "india">
puts country.errors.full_messages.inspect #=> ["Capital name can't be blank"]
end
end
Notice that with has_one association if the child record fails with validation error then parent record is not saved if autosave is used. If autosave is not declared then parent record gets saved and child record does not get saved. Also there is no error message.
This is in contradiction with the behavior exhibited with has_many association.
Besides this issue I found that even in has_one association autosave helps save in-memory association change. Otherwise if there are no validation issue then autosave declaration and not declaration of autosave behave same.
autosave option
Before I venture to fix the bug , I guess I will have to ask the core team what it means to have autosave option. At time time I am not sure whether has_one or has_many is exhibiting the right behavior with autosave.
I can’t recall right now but there was ticket on Lighthouse about setting autosave option as true by default in all association.
I have created a ticket #5339 for this issue on lighhouse.
instance_exec , changing self and params
Following code will print 99 as the output.
class Klass
def initialize
@secret = 99
end
end
puts Klass.new.instance_eval { @secret }
Nothing great there. However try passing a paramter to instance_eval .
puts Klass.new.instance_eval(self) { @secret }
You will get following error.
wrong number of arguments (1 for 0)
So instance_eval does not allow you to pass parameters to a block.
How to get around to the restriction that instance_eval does not accept parameters
instance_exec was added to ruby 1.9 and it allows you to pass parameters to a proc. This feature has been backported to ruby 1.8.7 so we don’t really need ruby 1.9 to test this feature. Try this.
class Klass
def initialize
@secret = 99
end
end
puts Klass.new.instance_exec('secret') { |t| eval"@#{t}" }
Above code works. So now we can pass parameters to block. Good.
Changing value of self
Another feature of instance_exec is that it changes the value of self. To illustrate that I need to give a longer example.
module Kernel
def singleton_class
class << self
self
end
end
end
class Human
proc = lambda { puts 'proc says my class is ' + self.name.to_s }
singleton_class.instance_eval do
define_method(:lab) do
proc.call
end
end
end
class Developer < Human
end
Human.lab # class is Human
Developer.lab # class is Human ; oops
Notice that in that above case Developer.lab says “Human”. And that is the right answer from ruby perspective. However that is not what I intended. ruby stores the binding of the proc in the context it was created and hence it rightly reports that self is “Human” even though it is being called by Developer.
Go to http://facets.rubyforge.org/apidoc/api/core/index.html and look for instance_exec method. The doc says
Evaluate the block with the given arguments within the context of this object, so self is set to the method receiver.
It means that instance_exec evaluates self in a new context. Now try the same code with instance_exec .
module Kernel
def singleton_class
class << self
self
end
end
end
class Human
proc = lambda { puts 'proc says my class is ' + self.name.to_s }
singleton_class.instance_eval do
define_method(:lab) do
self.instance_exec &proc
end
end
end
class Developer < Human
end
Human.lab # class is Human
Developer.lab # class is Developer
In this case Developer.lab says Developer and not Human.
You can also checkout this page which has much more detailed explanation of instance_exec and also emphasizes that instance_exec does pass a new value of self .
instance_exec is so useful that ActiveSupport needs it. And since ruby 1.8.6 does not have it ActiveSupport has code to support it.
I came across instance_exec issue while resolving #4507 rails ticket . The final solution did not need instance_exec but I learned a bit about it.
Multi level STI in development mode
If you are in development mode and if you have multi level STI then you might get unexpected result.
class CreateUser < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.string :name
t.string :type
end
end
def self.down
drop_table :users
end
end
class User < ActiveRecord::Base
end
class Agent < User
end
class SuperAgent < Agent
end
Now in console try this.
> Agent.find_by_name 'agent'
SELECT "users".* FROM "users" WHERE ("users"."type" = 'Agent')
AND ("users"."name" = 'agent') LIMIT 1
> SuperAgent # just load this class
> Agent.find_by_name 'agent'
SELECT "users".* FROM "users" WHERE (("users"."type" = 'Agent' OR
"users"."type" = 'SuperAgent')) AND ("users"."name" = 'agent') LIMIT 1
Notice that when SuperAgent is loaded then the generated sql query is different from the query generated when SuperAgent was not loaded. Since in development all the classes are loaded lazily you might get the first sql query. For the same code in production you will get second sql query.
Solution
There are many solutions. However I think it is best to let others know that if first level of STI is loaded then load all subclassess too. That can be done like this.
class Agent < User end require_dependecy 'super_agent'
$LOADED_FEATURES and require, load, require_dependency
Rails developers know that in development mode classes are loaded on demand. In production mode all the classes are loaded as part of bootstrapping the system. Also in development mode classes are reloaded every single time page is refreshed.
In order to reload the class, Rails first has to unload . That unloading is done something like this.
# unload User class Objet.send(:remove_const, :User)
However a class might have other constants and they need to be unloaded too. Before you unload those constants you need to know all the constants that are defined in the class that is being loaded. Long story short rails keep track of every single constant that is loaded when it loads User or UserController.
Dependecy mechanism is not perfect
Sometimes dependecy mechanism by rails lets a few things fall through the crack. Try following case.
require 'open-uri'
class UsersController < ApplicationController
def index
open("http://www.ruby-lang.org/") {|f| }
render :text => 'hello'
end
end
Start the server in development mode and visit http://localhost:3000/users . First time every thing will come up fine. Now refresh the page. This time you should get an exception uninitialized constant OpenURI .
So what’s going on.
After the page is served the very first time then at the end of response rails will unload all the constants that were autoloaded including UsersController. However while unloading UsersContorller rails will also unload OpenURI.
When the page is refreshed then UsersController will be loaded and require ‘open-uri’ will be called. However that require will return false.
Why require returns false
Try the following test case in irb.
# step 1
irb(main):002:0> require 'ostruct'
=> true
#step 2
irb(main):005:0* Object.send(:remove_const, :OpenStruct)
=> OpenStruct
# step 3
# ensure that OpenStruct is truly removed
irb(main):006:0> Object.send(:remove_const, :OpenStruct)
NameError: constant Object::OpenStruct not defined
from (irb):6:in `remove_const'
from (irb):6:in `send'
from (irb):6
# step 4
irb(main):007:0> require 'ostruct'
=> false
# step 5
irb(main):009:0> OpenStruct.new
NameError: uninitialized constant OpenStruct
from (irb):9
Notice that in the above case in step 4 require returns false. ‘require’ checks against $LOADED_FEATURES. When OpenStruct was removed then it was not removed from $LOADED_FEATURES and hence ruby thought ostruct is already loaded.
How to get around to this issue.
require loads only once. However load loads every single time. In stead of ‘require’, ‘load’ could be used in this case.
irb(main):001:0> load 'ostruct.rb' => true irb(main):002:0> OpenStruct.new => #<OpenStruct>
Back to the original problem
In our rails application refresh of the page is failing. To get around to that issue use require_dependency instead of require. require_dependency is a rails thing. Under the hood rails does the same trick we did in the previous step. Rails calls kernel.load to load the constants that would fail if require were used.
I am not seeing hoptoad messages. Now I know why.
Following code has been tested with Rails 2.3.5 .
Every one knows for sure that hoptoad notifier sends exception messages to server in production environment. Between ‘development’ and ‘production’ there could be a number of environments. Some of these would have settings closer to ‘development’ environment and some would have setting closely matching the settings of ‘production’ environment.
When you have many environments and when an exception occurrs, one is not really sure if that message is getting logged at hoptoad or not. Here is a run down of which messages will get logged and why.
It alls starts with rails
When an exception occurs while rendering a page then action_controller catches the exception. Following logic is evaluated to decide if user should see an error page with full stack trace or ‘we are sorry something went wrong’ message.
if consider_all_requests_local || local_request? rescue_action_locally(exception) else rescue_action_in_public(exception) end
Let’s look at first part consider_all_requests_local . Open ~/config/environments/development.rb and ~/config/environemtns/production.rb .
# ~/config/environments/development.rb config.action_controller.consider_all_requests_local = true # ~/config/environments/production.rb config.action_controller.consider_all_requests_local = false
As you can see in development mode all requests are local. Be careful with what you put in your intermediary environments.
If you want to override that value then you can do like this.
#~/app/contollers/application_controller.rb ActionController::Base.consider_all_requests_local = true
The second part of the equation was local_request? .
Rails has following code for that method.
LOCALHOST = '127.0.0.1'.freeze def local_request? request.remote_addr == LOCALHOST && request.remote_ip == LOCALHOST end
As you can see all requests coming from 127.0.0.1 are considered local even if RAILS_ENV is ‘production’. For testing purpose you can override this value like this.
#~/app/contollers/application_controller.rb def local_request? false end
Hoptoad has access to exception now what
If consider_all_request_local is false and if request is not local then hoptoad will get access to exception thanks to alias_method_chain.
def self.included(base) base.send(:alias_method, :rescue_action_in_public_without_hoptoad, :rescue_action_in_public) base.send(:alias_method, :rescue_action_in_public, :rescue_action_in_public_with_hoptoad) end
In rescue_action_in_public_with_hoptoad there is a call to notify_or_ignore like this.
unless hoptoad_ignore_user_agent? HoptoadNotifier.notify_or_ignore(exception, hoptoad_request_data) end
For majority of us there is no special handling for a particular user_agent .
def notify_or_ignore(exception, opts = {})
notice = build_notice_for(exception, opts)
send_notice(notice) unless notice.ignore?
end
Hoptoad defines following methods as ignorable by default and you won’t get notifications for following types of exceptions.
IGNORE_DEFAULT = ['ActiveRecord::RecordNotFound',
'ActionController::RoutingError',
'ActionController::InvalidAuthenticityToken',
'CGI::Session::CookieStore::TamperedWithCookie',
'ActionController::UnknownAction']
Next hop is method send_notice .
def send_notice(notice)
if configuration.public?
sender.send_to_hoptoad(notice.to_xml)
end
end
configuration.public? is defined like this.
@development_environments = %w(development test cucumber) def public? !development_environments.include?(environment_name) end
As you can see if the Rails.env is development or test or cucumber the exception will not be reported to hoptoad server.
Logging from initializers and plugings
Following code has been tested with Rails 2.3.5 .
If you put log messages in rails initializers then they do not show up in console.
# ~/config/initializers/session_store.rb Rails.logger.info "I am within initializer"
Start the server and you won’t see the message that was put in initializer.
ruby script/server
The weird thing is that log message did not show up but the code did not blow up either. Some would say that using RAILS_DEFAULT_LOGGER.info would solve the problem. That is not true.
This problem is not restricted to initializers only. If you put in some log messages in init.rb of a plugin then also you will find that those log message do not appear on console.
logs are going to development.log
When rails starts server then it calls initialize_logger . This is where Rails.logger is mapped to RAILS_DEFAULT_LOGGER. Since rails loads gems, plugins and initializers after invoking initialize_logger, one can use Rails.logger in gems, plugins and in initializers.
Since rails is rack compliant application, while initializing rails script/server introduces a rack middleware called LogTailer
use Rails::Rack::LogTailer unless options[:detach]
As the name suggests, the job of this middleware is to tail the log of development.log .
If you put in log messages in plugin or in initializers then those log messages go to development.log . After the initialization is done then script/server starts tailing the log file.
However tail only reports the new log messages. Log messages already written to development.log are not reported to console. This explains why you did not see log messages on console.
Also note that log messages are always written to development.log . On console we see the tail of that log.
A quick solution
One solution to this problem is to use puts but if you are debugging something on server then those messages would not appear on log. If you use Rails.logger then those messages would not appear on your console. A quick solution is to show all the existing messages from the log file at the end of initialization process.
Add following code in an initializer and you will see log messages from initializers and plugins.
# ~/config/initializers/zzz_log_output_to_console.rb
File.open(Rails.root.join('log', 'development.log'), "r") do |logfile|
while (line = logfile.gets)
puts line
end
end
Rails loads all initializers file in alphabetical order. Since I want all the accumulated messages on the console, I want my initializer to be last one to execute. Hence the name starts with z.
List of only the elements that contains
I was toying with the simple list filter plugin and ended up with this markup.
<div id="lab">
<ul id="list">
<li><a href="">USA</a></li>
<ul>
<p>
<a href=''>USA</a>
</p>
</div>
I want to get all links that contains the word USA. Simple enough. jQuery supports contains selector.
$(":contains('USA')");
Above query results in following items.
[html, body#body, div#lab, ul#list, li, a, ul, p, a]
That is because contains looks for given string under all the descendants.
has method to rescue
jQuery has has method which returns the list of elements which have a descendant which has the given string.
b = $('*').has(":contains('USA')");
Above query results in following items.
[html, body#body, div#lab, ul#list, li, ul, p]
Final result
a = $(":contains('USA')");
b = $('*').has(":contains('USA')");
c = a.not(b) ;
console.log(c);
Above query results in following items.
[a, a]
Singleton function in JavaScript
Recently I was discussed with a friend how to create a singleton function in JavaScript. I am putting the same information here in case it might help someone understand JavaScript better.
Creating an Object
Simplest solution is creating an instance of the object.
var Logger = function(path) {
this.path = path;
};
l1 = new Logger('/home');
console.log(l1);
l2 = new Logger('/dev');
console.log(l2);
console.log(l1 === l2);
Above solution works. However l2 is a new instance of Logger .
Singleton solution using a global variable
window.global_logger = null;
var Logger = function(path) {
if (global_logger) {
console.log('global logger already present');
} else {
this.path = path;
window.global_logger = this;
}
return window.global_logger;
};
l1 = new Logger('/home');
console.log(l1);
l2 = new Logger('/dev');
console.log(l2);
console.log(l1 === l2);
Above solution works. However this solution relies on creating a global variable. To the extent possible it is best to avoid polluting global namespace.
Single solution without polluting global namespace
var Logger = function() {
var _instance;
return function(path) {
if (_instance) {
console.log('an instance is already present');
} else {
this.path = path;
_instance = this;
}
return _instance;
}
} (); //note that it is self invoking function
var l1 = new Logger('/root');
console.log(l1);
var l2 = new Logger('/dev');
console.log(l2);
console.log(l1 === l2);
This solution does not pollute global namespace.
nodejs just the right tool for ...
Recenly I moved my blog from kite_runner to jekyll . I wanted to know how many links that I referred in my previous posts were dead.
I wrote the code in nodejs and all it took was 84 lines . However the best part is that the running the code takes only 4 minues thanks to nodejs.
Here is what the code does.
- get a full list of articles in _posts directory .
- scan each of the articles looking for any links .
- make a GET request to each of those links and print the response return code .
Because of nodejs step2 is totally asynchronous and 200 files read simultaneously. Similarly when it comes to making a GET request to url there is no time being wasted in waiting to get the response because nodejs handles everything asynchronously.
I can’t image how much time it will take to get the result if it is written in ruby. Of couse you can do multi-threading but that’s a lot of work.
It seems nodejs is just the right tool if you are interacting with other services that might not be fast.
exception2db - Rails plugin to store exceptions in database
I love hoptoad. I especially like the ability to catch exceptions in rake tasks and have those exceptions logged at hoptoad using hoptoad_notifier.
Currently I am working on a government project where hoptoad can’t be used. Rather than writing code that captures all the information about exception, I am letting hoptoad_notifier do all the heavy lifting. Instead of sending the information to hoptoad this plugin ensures that information is written to a database. Since this plugin relies on hoptoad_notifier, you get to catch exception as if you are using hoptoad. Except that exceptions will be logged into a database.
README has more information about this plugin. This plugin has been tested with Rails 2.3.5 . Source code is on github
What about exception_logger
I looked at exception_logger which has over 200 people watching it. However it has not been updated in almost 2 years. Since I could not find alternative, I decided to write one from scratch and I built it on top of hoptoad_notifier.