How to get changed values on save - ruby-on-rails

Trying to get previous values from the object on save. Think of this scenario:
#object = {:name => 'Dan', :occupation => 'student'}
#object[:occupation] = 'Full time employee'
#object.value_was[:occupation] # => 'student'
I hope it is understandable enough that there is no method value_was. More over I would like to do the same on model objects:
#student = Student.find(1)
#student.occupation = 'Full time employee'
#student.save
#student.value_was(:occupation) # => 'student'
Any help will be appreciated.
That would be really helpful

ActiveModel includes support for "dirty field marking", which preserves before and after states for changed fields.
You can use #student.occupation_was to get the previous value of occupation, and #student.occupation_changed? to get whether the value has changed or not.
This only works BEFORE the save, as saving resets the changed states of the values. However, you could capture this data in a before_save callback if you need to use it after a record has been saved. You can preserve all of the changes by duplicating #changed_attributes in a before_save, for example, then query on them.

Related

How to validate input which depends on checkbox

I recently started with rails.
I have one task here, but I don't know what is best practice and how to do it.
I have form with checkbox currency? and input currency_code.
Only value currency_code will be stored in database.
What I need:
1. If checkbox currency? is TRUE => currency_code must be filled
2. If checkbox currency? is FALSE => currency_code will be reset to nil
How to validate 1. if currency? is not in database (table column) so model does not know about this?
In case 2. Where I should check this and reset value currency_code to nil?
For case 2. I have this in my controller, but I don't like. I think that there must be a better solution.
def data_params
parameters = params.require(:data).permit(
:currency_code
)
parameters[:currency_code] = nil unless params[:data][:currency].to_bool
parameters
end
The attribute 'currency?' which is not part of the model is called virtual attributes. In Rails, we can use virtual attributes by setting
attr_accessor :currency?
in your corresponding_model.rb.
Now you can use this 'currency?' attribute in your form like other model attributes.
For case 2, the plcaement for this kind of data validations is to validate them in the view before even coming into the model. you must use jquery / javascript or any script of your choice. Here I provide jQuery snippet.
If you are new for using jquery in rails app, follow this https://github.com/rails/jquery-rails
In your form_page.html.erb add ids to the html elements.
<%= f.check_box :currency?, id: 'currency_checkbox' %>
and
<%= f.text_field :currency_code, id: 'currency_code' %>
The jquery snippet
curreny_checkbox = $('#curreny_checkbox')
currency_code = $('#currency_code')
$(currency_checkbox).on('change', function(){
if(this.checked)
currency_code.val('2')
else
currency_code.val('')
})
In your controller, you can simply assign the values
def create
// other codes //
#obj.currency_code = params[:currency_code]
end
If you want to validate input on model level you should store the currency? in the database.
Still let say for some unexplained reasons you don't want to store currency? in you database(which I won't suggest you in any case). You can define a method in model.
def set_currency_if_required is_currency_required
self.currency_code = nil unless is_currency_required
end
in controller
def create
model = Model.new(data_params)
model.set_currency_if_required(params[:data][:currency].to_bool)
model.save
end
...
def data_params
parameters = params.require(:data).permit(
:currency_code
)
end
this way you will have better readability and a clear idea about whats going on with the code.

Check to See if A Particular Active Record Object Is In A Particular 'Scope'

Currently on save I am trying to check to see if a recorded falls into a particular 'scope'. This 'scope' really is just some saved arguments for a .where call. Also with this 'scope' I am only ever checking values of the object, not ever how it relates to other objects in the database, so querying the database will always be over kill if that makes sense.
I have only been able to come up with the below solution
begin
result = self.class.where(scope).find(self.id)
rescue
result = false
end
The issue with this is that I have to query the database even though I already have the record, and I have to run this not only before save but after save to check the values it was and the values it will be after save, because there is no way to query the database for the updated version if it hasn't been saved.
There can be a number of these checks so I would like to avoid having to do it twice, and also having to query the database that many times, even if ultimately I am just looking something up by id.
The only other solution I have been able to think of would be to have a method that some how translates the where call into a proc that return a boolean when passed an object. The only issue with that is translating it would some how have to work with the active record adapter being used, which seems like a whole project to its own. So does anyone know of some way to do this, or of a gem that would help?
PS I getting the 'scope' from cache so I can't save it as a proc because you can't put procs into the cache with Rails.
first you can improve your first solution a bit
result = self.class.where(scope).exists?(self.id)
if you don't want to check the database, why don't you just check if your object's attributes has the values of the scope? if your scope is
class.where(:attr1 => value1, :attr2 => value2, :attr3 => value3)
then you can do
result = self.attr1 == value1 and self.attr2 == value2 and self.attr3 == value3
If your scopes are simple, you probably want to avoid code duplication. My solution allows you to call model.active? to know if an instance belongs to the scope, and Model.active to find all records matching the scope. model.active? doesn't involve any database queries.
consider adding this to config/initializers/scope_and_method.rb:
require 'active_record/named_scope'
module ActiveRecord::NamedScope::ClassMethods
def scope_and_method field, *values
field = field.to_sym
values.each do |value|
named_scope value.to_sym, :conditions => {field => value}
define_method "#{value}?" do
send(field.to_sym) == value
end
end
end
end
Usage:
scope_and_method :state, 'active', 'inactive'
Works as if it was:
named_scope :active, :conditions => {:state => 'active'}
named_scope :inactive, :conditions => {:state => 'inactive'}
def active?
state == 'active'
end
def inactive?
state == 'inactive'
end
This is a solution for Rails 2.3. This needs a very small tuning for Rails 3 and 4. (named_scope -> scope) I will check it soon.

Finding a particular attribute's value from a model in Rails

I'm trying to find the value of a particular model's attribute in rails. Here's what my code in the User controller's 'create' method looks like:
#user = User.find(1, :select => :money)
existing_money = #user
puts "#{existing_money}"
In my Heroku logs I'll see a variant of the following output instead of the :money integer for that particular user (with :id 1)
#<User:0x00000004e7cbc0>
Any thoughts? Thanks!
#user = User.find(1, :select => :money)
You are setting the #user instance variable with an object that has only one value, namely the money value. For now, all this does is save you a few bytes, by leaving off things like id, email, and any other columns you have in that table. It does however still return an object with attributes, the only difference is your object has only one attribute to call.
existing_money = #user
Given that #user is still an object with a single attribute, you now save this object in the existing_money local variable. What you probably want to do is *only store the money attribute in this variable`.
So you'd need this:
existing_money = #user.money
puts "#{existing_money}"
After the above change, this puts statement should return the attribute value, not the object encapsulating the attribute.
As existing_money is just the object you are seeing the object's ID.
As you want the money attribute you have to reference that too.
puts "#{existing_money.money}"

Updating records in a database using the form_tag

In my view page, i am using form_tag to create a form which will pass a string of ids from a hidden field to the controller code.
In my controller code, i am looping through an array of ids to update each record containing that id in the Expression table. But the code below does not seem to work.
I would really appreciate it if somebody could give me some suggestion regarding what is wrong with the code below.
def update_expression
#emi_ids_array = params[:emi_ids].split(/,/)
#sub_id = params[:sub_id]
#emi_ids_array.each do |emi_id|
#existing_exp = Expression.find(:first, :conditions => [ "EXT_EMI_ID = ? and EXT_SUB_FK = ?", emi_id, #sub_id])
#expression = #existing_exp.update_attributes(
:EXT_SUB_FK => #sub_id,
:EXT_PRESENCE => "present",
:EXT_STRENGTH => "weak",
:EXT_EMI_ID => emi_id
)
end
end
Try converting the array of ID's (and the sub_id) to integers.
Is it the finding of the object that fails, or the update? Output the #expression.errors after the update call to see if there are any validations failing.
Is there a reason for all the instance variables? You don't need the #'s if the variable doesn't go beyond that method. Also the #expression item seems superfluous, you're just duplicating the #existing_exp object, you don't need to put the return into a new object, especially if it's replaced each time the loop runs anyway.
Found a temporary solution. 'update_attributes' does not seem to work, so i opted for 'update_all' attribute
Expression.update_all({:EXT_PRESENCE => "present", :EXT_STRENGTH => "weak"},['EXT_EMI_ID = ? and EXT_SUB_FK = ?', emi_id, #sub_id])
Hopefully, it might be useful to someone else

Rails 3 check if attribute changed

Need to check if a block of attributes has changed before update in Rails 3.
street1, street2, city, state, zipcode
I know I could use something like
if #user.street1 != params[:user][:street1]
then do something....
end
But that piece of code will be REALLY long. Is there a cleaner way?
Check out ActiveModel::Dirty (available on all models by default). The documentation is really good, but it lets you do things such as:
#user.street1_changed? # => true/false
This is how I solved the problem of checking for changes in multiple attributes.
attrs = ["street1", "street2", "city", "state", "zipcode"]
if (#user.changed & attrs).any?
then do something....
end
The changed method returns an array of the attributes changed for that object.
Both #user.changed and attrs are arrays so I can get the intersection (see ary & other ary method). The result of the intersection is an array. By calling any? on the array, I get true if there is at least one intersection.
Also very useful, the changed_attributes method returns a hash of the attributes with their original values and the changes returns a hash of the attributes with their original and new values (in an array).
You can check APIDock for which versions supported these methods.
http://apidock.com/rails/ActiveModel/Dirty
For rails 5.1+ callbacks
As of Ruby on Rails 5.1, the attribute_changed? and attribute_was ActiveRecord methods will be deprecated
Use saved_change_to_attribute? instead of attribute_changed?
#user.saved_change_to_street1? # => true/false
More examples here
ActiveModel::Dirty didn't work for me because the #model.update_attributes() hid the changes. So this is how I detected changes it in an update method in a controller:
def update
#model = Model.find(params[:id])
detect_changes
if #model.update_attributes(params[:model])
do_stuff if attr_changed?
end
end
private
def detect_changes
#changed = []
#changed << :attr if #model.attr != params[:model][:attr]
end
def attr_changed?
#changed.include :attr
end
If you're trying to detect a lot of attribute changes it could get messy though. Probably shouldn't do this in a controller, but meh.
Above answers are better but yet for knowledge we have another approch as well,
Lets 'catagory' column value changed for an object (#design),
#design.changes.has_key?('catagory')
The .changes will return a hash with key as column's name and values as a array with two values [old_value, new_value] for each columns. For example catagory for above is changed from 'ABC' to 'XYZ' of #design,
#design.changes # => {}
#design.catagory = 'XYZ'
#design.changes # => { 'catagory' => ['ABC', 'XYZ'] }
For references change in ROR

Resources