Rail dirty and date changes - ruby-on-rails

Have a column in orders called closed_date which is a DateTime field.
Using Dirty. trying to do if self.closed_date_changed? but it's not working. Do I have to do something special for tracking changes with Date Time fields?
EDIT
Using Rails 3.0.3, Ruby 1.9.2p136
Code in orders controller
def update
#order = Order.find(params[:id])
if #order.update_attributes(params[:order])
#order.close_order
end
end
end
In Model
include ActiveModel::Dirty
def close_order
if self.closed?
if self.closed_date_changed?
self.items.each do |item|
item.update_attribute(:created_at, self.closed_date)
end
end
else
self.update_attributes(:closed_date => Time.now, :closed => true)
self.items.each do |item|
item.update_attribute(:created_at => Time.now)
item.schedule_any_tasks
end
end
end
end

I think you mean something like:
def save_changes
if closed_date_changed?
# do something like save the modified data to a table
else
# do anything else
end
end
And the most important, don't forget to call this method on a before_save(update) callback.
Because the changes only remains while the actual record isn't saved.
Hope it helps!

def start_date_changed?
return true if self.start_date != self.start_date_was
return false
end
I have use was which checks for the value change..
Thanx

Related

Updating rails model's tags with null id issue

I have a rails modle like this
class Article < ActiveRecord::Base
has_many :tags
def all_tags=(keys)
self.tags = keys.split(',').map do |key|
Tag.where(article_id: id, key: key.strip).first_or_create!
end
end
def all_tags
tags.map(&:key).join(', ')
end
end
Basically what I want to do is to allow user set tags on it, it would look like this in controller
def create
#article = article(article_params)
if # article.persisted?
redirect_to article s_path
else
render 'new'
end
end
However, as in def all_tags=(keys), article.id is not present yet. So I got error like this
PG::NotNullViolation: ERROR: null value in column "article_id" violates not-null constraint
Here is the question, how to ensure article is persisted before all_tags got update?
When you use Model.where(conditions).first_or_create!, Active Record tries to insert into database a new record for Model if can't found one.
In your case, since you are setting the attributes before save the model, then the create launch an exception.
To fix the issue just change first_or_create with first_or_initialize
Yes you can do validation something like
validates :your_method, on: :update
def your_method
{with logic not be null}
end

Rails gem multiple NOSQL datastores

I'm creating my own gem and I want to enable user to save data to multiple NOSQL data stores. How can I make this happen? Where should I place the necessary files?
I've done the same thing in my gem. I think you have created an App folder in your gem/engine. Create another folder called "backend" and create classes for each datastore. For my case I created a seperate for Mongo and Redis
module Memberfier
class RedisStore
def initialize(redis)
#redis = redis
end
def keys
#redis.keys
end
def []=(key, value)
value = nil if value.blank?
#redis[key] = ActiveSupport::JSON.encode(value)
end
def [](key)
#redis[key]
end
def clear_database
#redis.keys.clone.each {|key| #redis.del key }
end
end
end
module Memberfier
class MongoStore
def initialize(collection)
#collection = collection
end
def keys
#collection.distinct :_id
end
def []=(key, value)
value = nil if value.blank?
collection.update({:_id => key},
{'$set' => {:value => ActiveSupport::JSON.encode(value)}},
{:upsert => true, :safe => true})
end
def [](key)
if document = collection.find_one(:_id => key)
document["value"]
else
nil
end
end
def destroy_entry(key)
#collection.remove({:_id => key})
end
def searchable?
true
end
def clear_database
collection.drop
end
private
def collection; #collection; end
end
end
You may have already seen one of Uncle Bob's presentations on application architecture. If not, it's here. I'd recommend having a single boundary object that select models inherit from. That boundary object could have multiple CRUD methods such as find, create, delete. That boundary object could inherit from whatever NOSQL adapter you configure. Example/source: http://hawkins.io/2014/01/pesistence_with_repository_and_query_patterns/

Rails: Run callback only for CRUD operation?

I'm in Rails 3. Here's my code for a method which creates Update records in response to certain attributes being changed on a model called Candidate:
before_save :check_changed, on: [:update]
def check_changed
tracked_attributes = ["period_contributions", "total_contributions",
"period_expenditures", "total_expenditures",
"debts_and_loans", "cash_on_hand",
"major_endorsements",
"rating_afl_cio",
"rating_cal_tax",
"rating_cc",
"rating_eqca",
"rating_lcv",
"rating_now"]
changes.each do |key, value|
if tracked_attributes.include?(key)
Update.create(:candidate_id => self.id, :attribute_name => key,
:new_value => value[1], :old_value => value[0])
end
end
end
The issue is that I have some rake tasks I'm running to do batch updates to the data, which end up triggering this callback unintentionally. I'd like for it only to run when a Candidate is updated from within the admin tool aka CRUD interface. Any advice on the best way to do this?
I will only use callbacks when it is something that always needs to happen, no matter the source. Magically skipping or including them normally leads to pain down the road.
My suggestion is to create a different method on the model that does the check and use that for the crud actions.
class Candidate
#...
def check_changed_and_update(attributes)
check_changed
update(attributes)
end
private
def check_changed
tracked_attributes = ["period_contributions", "total_contributions",
"period_expenditures", "total_expenditures",
"debts_and_loans", "cash_on_hand",
"major_endorsements",
"rating_afl_cio",
"rating_cal_tax",
"rating_cc",
"rating_eqca",
"rating_lcv",
"rating_now"]
changes.each do |key, value|
if tracked_attributes.include?(key)
Update.create(:candidate_id => self.id, :attribute_name => key,
:new_value => value[1], :old_value => value[0])
end
end
end
end
Then in the controller for candidate just change update to check_changed_and_update:
class CanidateController < ApplicationController
def update
#...
respond_to do |format|
if #candidate.check_changed_and_update(canidate_params)
format.html { redirect_to #candidate, notice: 'Candidate was successfully updated.' }
else
format.html { render action: 'edit' }
end
end
end
This has an added bonus of making it obvious what is going to happen when update is called.
Now you can just use the normal active record api in your rake tasks.

Rails - how to enforce one method call per method, like controllers do with `render`

So in Rails, you get an error if you try to call render multiple times within a controller action.
I have another Ruby class that I'm writing, and I'd like to try to do something similar (make sure that my own respond_with method is only called once.
So for example, this would be fine:
def my_method
if (my_value == true)
...
respond_with(:a, :b, :c)
else
...
respond_with(:x, :y, :z)
end
end
But this would raise an error if my_value == 4
def my_method
if (my_value >= 4)
...
respond_with(:a, :b, :c)
end
if (my_value <= 4)
...
respond_with(:d, :e, :f)
else
...
respond_with(:x, :y, :z)
end
end
Any thoughts on how to best accomplish that?
class MyBaseClass
def respond_with(arguments)
if #rendered
raise DoubleRenderError #or whatever
end
#rendered = true
#whatever the respond_with function should do
end
end
Here's one way I could think of - in your Ruby class, define a #responded attribute by doing attr_accessor :responded. In your respond_with method, add the following lines:
def respond_with
raise DoubleRenderError if self.responded
# do stuff
self.responded = true
end
The above code should raise an error if you call respond_with twice on the same object.

How to remove API logic from view in Rails Way?

I would like to remove this logic:
Suitcase::Hotel.find(id: hotel.id).images.first.url
from view.
https://gist.github.com/2719479
I dont have model Hotel. I get this url via API using Suitcase gem.
Problem is because
hotel is from #hotels = Suitcase::Hotel.find(location: "%#{headed}%") and API recevie me images only if do Suitcase::Hotel.find(id: hotel.id)
If Suitcase::Hotel.find(id: hotel.id).images.first.url works then i would guess hotel.images.first.url will work too if hotel is an hotel instance.
Is adding:
#hotel = Suitcase::Hotel.find(id: hotel.id)
to #show action doesn't work?
EDIT:
In that case make an helper:
def hotel_image_url(hotel)
Suitcase::Hotel.find(id: hotel.id).images.first.url
end
But as I can see here you can simply write in controller:
#hotels_data = Suitcase::Hotel.find(ids: #hotels.map(&:id))
Or to be more elegant add to your model (or create decorator (it's better option)):
def photo
Suitcase::Hotel.find(id: self.id).images.first.url
end
I think this should work, not sure about the second option
class Search < ActiveRecord::Base
attr_accessible :headed, :children, :localization, :arriving_date, :leaving_date, :rooms, :adults
def hotels
#hotels ||= find_hotels
end
private
def find_hotels
return unless headed.present?
#hotels = Suitcase::Hotel.find(location: "%#{headed}%")
#hotels.each do |hotel|
def hotel.image_url
Suitcase::Hotel.find(id: hotel.id).images.first.url
end
end
end
end
# or this, but I'm not sure if this works
#hotels.each do |hotel|
image_url = Suitcase::Hotel.find(id: hotel.id).images.first.url
def hotel.image_url
image_url
end
end

Resources