Given a model:
class User
include Toy::Store
attribute :name
end
Can I use ActiveModel::Observer to build an observer? I remove the ActiveRecord framework, as I am not using it in my Rails app.
Adding an observer like:
class UserObserver < ActiveModel::Observer
def after_save(model)
puts "Hello!"
end
end
does not seem to work. Including the observer in the application configuration does not work, because the ActiveRecord framework is removed.
I also wanted to use Observers with Toy::Store too. It turns out that the normal object lifecycle events, like create, save, update, and delete that are observable in Rails are observable because of ActiveRecord. Toy Store objects are ActiveModel objects and don't have the same hooks. I did a deep dive on the subject in a blog post on Why Toy Store Doesn't Work With Observers.
But, good news, it's still possible to use observers with toy store, it's just up to you to implement them. I also did a blog post on How to Use Observers With Toy::Store
, but here's the gist of it: your Toy Object, in this case User, must include ActiveModel::Observing and must fire the event when it's appropriate for that model:
class User
include Toy::Store
attribute :name
after_save :notify_observers_save_occured
private
def notify_observers_save_occured
self.class.notify_observers(:after_save, self)
end
end
You can only observe ActiveModel descendants. They don't have to be ActiveRecord objects though as you can read in Yehuda Katz's article:
ActiveModel makes any ruby object feel like ActiveRecord
Related
I am going through a co-workers code and am not able to find a single tutorial where this has been used. Can someone point me to some resources where this has been used. This has made code very clean but I haven't found any reference to it. This is only part of this class. It includes other some more methods.
class Manager
include ActiveModel::Model
include ActiveModel::Associations
attr_accessor :application_id, :user_id, :user_application_id,.........
belongs_to :application
belongs_to :user_application
belongs_to :user .. more belongs .......
# This method is necessary to enable this ActiveModel Class to be used in views along with Form helpers
def self._reflect_on_association(association) #:nodoc:
_reflections[association.to_sym]
end
def []=(attr, value)
self.send("#{attr}=", value)
end
def [](attr)
multi_attribute_ids = [:some_ids.to_s, :someid2.to_s]
return if multi_attribute_ids.include?(attr)
self.send(attr)
end
def applicant_name
end
-- some more methods
end
What would be the use of such a "manager". What are the two methods that are using self.send doing here. Is this a common pattern in rails.
Yes, with the introduction of ActiveModel in Rails 3, it has become an increasingly common pattern to use domain objects (called a manager in this case) that are not backed by an actual database table but which look and feel like models.
Even though ActiveModel makes it particularly convenient to pick and choose Rails model features to be incorporated into arbitrary classes, this pattern is something Rails pioneers have been encouraging since a long time.
As has been illustrated clearly in the example you posted, this pattern allows us to define virtual models and virtual associations which can easily take advantage of form helpers and other rails niceties written assuming model objects.
I have a rails app that like so many rails apps, has users. In my user model i have password and salt columns, among others. Is there any way i can protect these from appearing when i do for example debug #user or when i render JSON?
Of course i could just make sure to omit these every time i use it, but is there a way i can make really sure that they don't show up anywhere?
Maybe they could be private fields? They are only needed in my User model and my Session controller, but i could make a method in User that compares a given password to the correct one and then only have the variables accessible in the ´User´ module. Would this help, or would they still be rendered in those two places ( and others )?
Very good question, there are several things to consider:
Are you looking to "privatise" the values in the model or in the Rails Core?
Will you need to access these attributes in any specific circumstances? (IE are they always private?)
How will the private methods be populated? Will they ever change?
I have to be honest in saying I don't know the answer. Since I'm interested, I did some research. Here are some resources:
Is there a way to make Rails ActiveRecord attributes private?
Hiding an attribute in an ActiveRecord model
ActiveRecord protects your privates
The consensus seems to be that if you take the Rails model, and apply the logic that every time you populate it, its attributes become instance methods of the, you can begin to privatise those methods within the model itself.
For example...
#app/models/user.rb
class User < ActiveRecord::Base
private :password, :password=
private :salt, :salt=
end
This seems to give you the ability to make certain methods private in your model, which will answer half your question. However, it still leaves the possibility of ActiveRecord pulling the record each time, which could be a danger.
I had a look into this, and found that there are certain ways you can manipulate ActiveRecord to prevent it pulling unwanted data:
ActiveRecord : Hide column while returning object
This resource recommends the use of active_record_serializers. This appears specifically for JSON, but is more along the right track (IE the ability to define which data we return from ActiveRecord queries).
#serializer
class UserSerializer < ActiveModel::Serializer
attributes :username, :created_at
end
#controller
render json: #user, serializer: UserSerializer
There was another suggestion of ActiveRecord Lazy Attributes - stipulating to ActiveRecord which attributes to load:
#app/models/user.rb
class User < ActiveRecord::Base
attr_lazy :data
end
Finally, you always have good ol' default_scope:
#app/models/user.rb
class User < ActiveRecord::Base
default_scope select([:id, :username])
end
I'm trying to have a rails model observe another object (which acts as a subject). I saw that there is an update method (which has different semantics) in ActiveRecord::Base, but in order to use the Observable module, I need to implement an update method in my model so that I can be notified of changes to the subject.
Any thoughts on how this could be achieved?
You probably want to use a regular Observer which will receive event callbacks when something happens to the observed model.
Why do you need to encapsulate your observer functionality into another model?
You're better off putting the events/callbacks in your observer and calling any needed functionality as a helper method on the other model instead of making your model an observer.
EDIT: Adding example code
class User < ActiveRecord::Base
end
class UserObserver < ActiveRecord::Observer
observe :user
def after_save(user)
MyHelperClass.do_some_stuff_for_user(user)
end
end
class MyHelperClass
def self.do_some_stuff_for_user(user)
puts "OMG I just found out #{user.name} was saved so I can do stuff"
end
end
It appears that you can override the default update that comes with ActiveRecord, so that it can receive notifications from subjects (assuming you have mixed in Observable). The procedure for doing something like this is in the book "Pro ActiveRecord" published by APress (Chap. 5, "Bonus Features").
It involves the use of alias_method / alias_method_chain with some metaprogramming involved...
I haven't tried this out personally yet, but just leaving a note here in case anyone else is intested.
I am writing a Ruby on Rails app and I want a method to be called every time the database is modified. Is it possible to do this without inserting a method call in every location where the database is modified?
I like KandadaBooggu's answer but if you did not want to monkey with AR you might be able to do this with an Observer.
class AllObserver < ActiveRecord::Observer
observe :model_a, :model_b
def after_save(record)
logger.info("CREATED #{record.class}")
end
def after_update(record)
logger.info("UPDATED #{record.class}")
end
end
Just add the models that you want to observer. In this example it will log updates to ModelA and ModelB
Depends on the database. Many databases have very powerful stored procedure languages that can, among other things, invoke web services.
You could have a trigger on the important database tables call a ruby web service that calls your method.
Or you can have triggers that update an event table, and then have a process that watches for changes on that table and then fires the method.
There's likely some meta-programming magic that you might be able to use to tweak your ruby code to invoke the change as well.
All sorts of options.
If you want to log all models:
Monkey patch the ActiveRecord::Base class.
class ActiveRecord::Base
after_save :log_something
after_destroy :log_something
private
def log_something
end
end
For a specific model:
class User < ActiveRecord::Base
after_save :log_something
after_destroy :log_something
private
def log_something
end
end
Have you considered using: after_update or before_update in ActiveRecord:
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
I'm working on a Rails app that will serve as an authentication system for other Rails apps through Rails's ActiveResource functionality.
The authentication app has an ActiveRecord model called User. The client app has an ActiveResource model called User. I know that in the client app I can do stuff like user.save and it will perform a PUT operation using XML over HTTP.
But what if I want to put into my client app's User model has_many :contacts or something like that (contacts being an ActiveRecord model in the client app)? Then I'd want to do stuff like get all the Contacts that belong to some User.
Is that possible?
(I noticed that a similar question was asked, but there weren't many responses.)
Short answer: no. The classes involved in has_many, belongs_to, has_and_belongs_to_many live in ActiveRecord and build SQL queries to make the associations work.
That said, you can make it look like there is an association, you just have to write your own methods. Which was the up-voted answer on that question you linked to.
So, add a column to your Contact model that is user_id or whatever key you need to pass to your User.find ActiveResource model and you could part of the association contract like this:
class User < ActiveResource::Base
# boilerplate ActiveResource url stuff
def contacts
Contact.find(:all, :conditions => { :user_id => self.id })
end
end
class Contact < ActiveRecord::Model
def user
User.find_by_user_id(self.user_id)
end
end
There's a lot more you get from has_many for free, but that's the gist of it.
This question is ancient, but I ran across this recently:
http://yetimedia.tumblr.com/post/35233051627/activeresource-is-dead-long-live-activeresource
From the post:
Basic support for associations: has_many, has_one, belongs_to now can be used in ActiveResource models.
There are some other improvements noted in the posting that may warrant a second look into ActiveResource even though it has been removed from Rails 4.0.