I have a legacy table with a column for the last update timestamp.
Now I do want to tell my model that the rails attribute updated_at is mapped to the legacy column.
alias_attribute :updated_at, :lastcall
Now I can access the column but it's not getting updated when i update the object.
So how can I use the rails timestamps with an legacy column?
Best,
P
Try to add this as well, which will alias the setter method.
alias_attribute :updated_at=, :lastcall=
I don't know if there's a 'proper' way of doing it, but you could do it with a before_save or before_update filter on the model.
class LegacyModel < ActiveRecord::Base
before_update :update_lastcall
private
def update_lastcall
self.lastcall = Time.now
end
end
If you don't want to get the model messy you could put it into an Observer.
I'd also like to draw your attention to this, if your timestamp column names are site-wide (as mine are). I didn't want to clutter up my models, and fortunately, you can monkey-patch ActiveRecord::Timestamp. I placed the below into a dir named lib/rails_ext/active_record.rb (I'm an organization freak) and called it with a require 'rails_ext/active_record' declaration in one of my initializers in config/initializers/.
module ActiveRecord
module Timestamp
private
def timestamp_attributes_for_update #:nodoc:
[:modified_time, :updated_at, :updated_on, :modified_at]
end
def timestamp_attributes_for_create #:nodoc:
[:created_date, :created_at, :created_on]
end
end
end
My custom attributes ended up being :modified_time and :created_date. You'd specify your :lastcall column in one of those (timestamp_attributes_for_update, I'm assuming). No mucking with your models required.
Related
I have created a new model in my app - PaypalOrder. Now in one of the methods of this model I want to be able to access current_order object. Where Order is an existing model. How can I do this in ruby on rails?
I read about associations, but they seem a bit complicated.
EDIT:
The problem with using associations is that not every Order will have a corresponding PaypalOrder. But whenever there is a PaypalOrder I want to access Order. How can I setup this association
what about:
class PaypalOrder
belongs_to :order
end
?
you need an "order_id" column in paypal_orders table
and that's it
you then create a PaypalOrder with
def some_action
current_order = Order.find(some_id)
paypal_order = PaypalOrder.new(order: current_order)
#do what you want with paypal_order
end
if you don't have the order_id do
bundle exec rails g migration AddUserToPaypalOrder
and the change method
add_column :paypal_orders, :user, :references
or
add_column :paypal_orders, :user_id, :integer
The way to go is to use concerns, it works like this:
Model:
# app/models/PayPayOrder.rb
class PayPalOrder < BaseModel
# concerns
include MyMultipleNeededMethods
...
def now_i_use_the_concern_method
concern_method
end
...
end
Concern:
# app/models/concerns/MyMultipleNeededMethods.rb
module MyMultipleNeededMethods
extend ActiveSupport::Concern
def concern_method
puts "refactored like a boss"
end
...
end
Never ever try to cross reference methods this way. Use the given rails framework, its awesom ;-)
Hm... current_order and curren_user and usually current_s are tight to the session. So they can be accessed only by the controller. Since the models are handling business-domain logic they shouldn't access these objects...
I have the following model (sort_timestamp is a datetime):
class Post < ActiveRecord::Base
[snip attr_accessible]
acts_as_nested_set
after_create :set_sort_timestamp
private
def set_sort_timestamp
self.sort_timestamp = self.created_at
end
end
I'm using https://github.com/collectiveidea/awesome_nested_set . This code doesn't set sort_timestamp. What am I doing wrong?
Unless I'm missing the point of what you're doing here, you're probably looking for before_create if you'd like it to save when the row is created. Otherwise you'll have to add self.save to the method, but that will cause extra database calls, so before_create might be the better option.
(Basically, the flow of what you were doing before was that the model would be created, saved to the database, and then the object would modify its attribute sort_timestamp to be created_at; this is after your database commit, and only performed in memory (so not persisted, unless you were persisting it in another way later in the code).
EDIT: Actually, this probably won't work because created_at probably won't be set before the record is created. A few options:
1) Add self.save to end of your method with after_create
2) Use Time.now if the times sort_timestamp and created_at don't have to be exactly the same.
or, 3) Try adding default value to migration: How to use created_at value as default in Rails
Rails automatically inserts values for columns like created_at and updated_at. Can i configure rails in such a way that it updates more columns. For example all my tables have a column called user holding the currentuser value, can I default rails ti insert user for any database change?
You could try using the before_save function in your model, unless I've misunderstood the question.
before_save :defaults
def defaults
#some stuff to set your defaults
end
Yes, you could use a before_filter in the model, e.g.
before_update :set_value
def set_value
self.value = "hello"
end
You can use ActiveRecord callbacks to trigger logic when changing states, such as before saving an object to the database. The created_at and updated_at columns in automatically updated when either an object is created (before_create), or updated (before_save). You can define your own callbacks using the class methods defined in the ActiveRecord::Callbacks namespace. An example would be
# app/models/example.rb
class Example < ActiveRecord::Base
before_save :do_something
def do_something
self.value = value
end
end
If you are specifically wanting to record the user that created, updated, or deleted a record, you can save some work and use the Userstamps Rails plugin to automatically record the user. This plugin is located at https://github.com/delynn/userstamp
# app/models/example.rb
class Example < ActiveRecord::Base
model_stamper
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
include Userstamp
end
You will need to add the userstamps columns onto each of your models on which you want to record user actions.
More information on ActiveRecord callbacks can be found here: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
Information on timestamps can be found here:
ActiveRecord timestamps: http://api.rubyonrails.org/classes/ActiveRecord/Timestamp.html
When do you use attr_reader/attr_writer/attr_accessor in Rails models?
Never, unless you have specific need for it. Automatic database-backed accessors are created for you, so you don't need to worry.
Any attr_accessors you do create will change the relevant #attr on the rails object, but this will be lost when the object is destroyed, unless you stick it back in the database. Sometimes you do want this behavior, but it's unusual in a rails app.
Now in ruby, it's a different story, and you end up using these very frequently. But I'd be surprised if you need them in rails---especially initially.
attr_accessor can be used for values you don't want to store in the database directly and that will only exist for the life of the object (e.g. passwords).
attr_reader can be used as one of several alternatives to doing something like this:
def instance_value
"my value"
end
Rails models are just ruby classes that inherit from ActiveRecord::Base. ActiveRecord employs attr_accessors to define getters and setters for the column names that refer to the ruby class's table. It's important to note that this is just for persistence; the models are still just ruby classes.
attr_accessor :foo is simply a shortcut for the following:
def foo=(var)
#foo = var
end
def foo
#foo
end
attr_reader :foo is simply a shortcut for the following:
def foo
#foo
end
attr_writer :foo is a shortcut for the following:
def foo=(var)
#foo = var
end
attr_accessor is a shortcut for the getter and setter while attr_reader is the shortcut for the getter and attr_writer is a shortcut for just the setter.
In rails, ActiveRecord uses these getters and setters in a convenient way to read and write values to the database. BUT, the database is just the persistence layer. You should be free to use attr_accessor and attr_reader as you would any other ruby class to properly compose your business logic. As you need to get and set attributes of your objects outside of what you need to persist to the database, use the attr_s accordingly.
More info:
http://apidock.com/ruby/Module/attr_accessor
http://www.rubyist.net/~slagell/ruby/accessors.html
What is attr_accessor in Ruby?
If you are using it to validate the acceptance of the terms_of_service, you should really consider using validates :terms_of_service, :acceptance => true. It will create a virtual attribute and is much more concise.
http://guides.rubyonrails.org/active_record_validations.html#acceptance.
One example is to have a number of options stored in one serialized column. Form builder would complain if you try to have a text field for one of these options. You can use attr_accessor to fake it, and then in the update action save it in the serialized column.
Is it possible in ActiveRecord to customize/override the name of an attribute so that it does not match the column name in the database?
My specific case involves a legacy column, "revision", that I can't remove at this time. The column name conflicts with acts_as_audited. Which of course errors out the legacy code that I need until my migrations are complete.
My desired solution would be to override the attribute name for this column, and update the few areas that call it. Thus allowing the legacy column to live alongside acts_as_audited.
I haven't used acts_as_audited, but I'm assuming its implementation is overriding the accessor for that column. In that case, you should be able to just do something like this:
class ActiveRecord::Base
def self.name_column(column_name, new_name)
define_method(new_name) {read_attribute column_name}
define_method("#{new_name}=") {|value| write_attribute column_name, value}
define_method("#{new_name}?") {attribute_present? column_name}
end
end
These will directly access the column named in column_name without going through the overridden accessor.
Oh, bonus duplication-destroying metaprogramming answer:
class ActiveRecord::Base
def self.name_column(column_name, new_name)
{ '' => :read_attribute,
'=' => :write_attribute,
'?' => :attribute_present? }.each do |suffix,method|
define_method("#{new_name}#{suffix}") {|*args| send method, column_name, *args}
end
end
end
Just because I like to show how it can be done.
Create a migration to rename the column from revision to whatever-you-want.
Then you can declare an attr_accessor :revision and use it without the need to map the attribute to a database field.