To understand this we will create a Model Employee with single field “name“, we will then experiment with its object in rails console
$ rails g model employee name:string create db/migrate/20130119195707_create_tests.rb create app/models/test.rb
The content of the model and migration file look like this
#model class Employee < ActiveRecord::Base end #migration class CreateEmployees < ActiveRecord::Migration def change create_table :employees do |t| t.string :name end end end
Run the migration
$ rake db:migrate
So now we have a Employee model which map to employees table with a single field name .Now let us experiment out with the employee object in the console.
Note : If you make any change in file, reload the console, before trying out your changes, otherwise it will not reflect
1.9.2-p290 :003 > reload! Reloading... => true 1.9.2-p290 :038 > employee=Employee.new # creating instance i,e object of Employee => #<Employee id: nil, name: nil> 1.9.2-p290 :002 > employee.name => nil #output is o.k as we have not set any name yet 1.9.2-p290 :003 > employee.name = "Arun" => "Arun" # employee name set to Arun 1.9.2-p290 :004 > employee.name => "Arun" # employee name is correctly output Now, let us try to set age of employee 1.9.2-p290 :004 > employee.age = 23 NoMethodError: undefined method `age=' for #<Employee:0x97aac48> let us try to see age of employee 1.9.2-p290 :004 > employee.age = 23 NoMethodError: undefined method `age' for #<Employee:0x97aac48>
What, is this , why we are not able to see or set age in the same way as we are doing for employee name. You may thought, the error is because we have name filed in employees table but not the age filed.But you see it is not a database error but undefined method `age=’ error. O.k so they are looking for method age= and age, let us define them
class Employee < ActiveRecord::Base def age= val @age = val # age set to val end def age @age # age is returned i,e read end #let us define some other method to try them in console #we want secret to be only set on object but no one to read them def secret= val @secret = val end #we want employee code to be only read from object no one can set it def employee_code @employee_code end end
Now, let us checkout different operation on console
1.9.2-p290 :038 > employee=Employee.new # creating instance i,e object of Employee => #<Employee id: nil, name: nil> 1.9.2-p290 :004 > employee.age=23 => 23 1.9.2-p290 :005 > employee.age => 23 1.9.2-p290 :007 > employee.secret = "xyzw" => "xyzw" 1.9.2-p290 :008 > employee.secret 1.9.2-p290 :009 > employee.employee_code => nil 1.9.2-p290 :010 > employee.employee_code = 1234 NoMethodError: undefined method `employee_code=' for #<Employee:0x9c7e6ac>
So, we can see that any one can set and read age from the object, but for security reason they can only set secret but can’t read and in case of employee_code they can only read it but not set it.
But, still we can’t see any name method in our model then how it is working and from where it is coming, Actually, ruby do a lot of things for you from behind the scene, It has defined name and name= method internally for you along with a large no of other methods which you are not even aware of….you better know them as it will reduce your line of code. all methods available to any object can be listed by calling methods on it
1.9.2-p290 :011 > employee.methods => [:age=, :age, :secret=, :employee_code, :name, :name=, :name_before_type_cast, :name?, :name_changed?, :name_change, :name_will_change!, :name_was, :reset_name! ,............so on.......]
So, you can see a no of methods being created for name filed, you can try all of them in console, as for name field these methods are created for all the fields in the corresponding table of a model….great… so what about any attributes which we do not have in table but just want on a object after its creation…well you have attr_reader attr_writer attr_accessor for that.
Let us cut down our model code to three line. This will internally generate all the methods, you have defined above
class Employee < ActiveRecord::Base attr_accessor :age # make age writable and redable attr_writer :secret #make secret only writable attr_reader :employee_code # make it only readable end
If you again check in console, you will find your required method there
1.9.2-p290 :011 > employee.methods
=> [:age=, :age, :secret=, :employee_code, :name, :name=, :name_before_type_cast, :name?, :name_changed?,
:name_change, :name_will_change!, :name_was, :reset_name!,…………so on…….]