-
Notifications
You must be signed in to change notification settings - Fork 33
Features and Recipes
Since we are doing database replication, it's important to distinguish that Promiscuous operates at the ActiveModel API, and thus makes no difference between the model methods and the actual persisted attributes. We call virtual attributes computed attributes that are published. For example:
class User
include Promiscuous::Publisher
field :first_name, :last_name
publish :full_name, :use => [:first_name, :last_name]
def full_name
"#{first_name} #{last_name}"
end
end
Any attributes that respond to .to_json
can be published, including array and
hashes. We do not recommend using deeply nested structures without using
embedded documents though.
On the subscriber side, Promiscuous invokes the setter corresponding to the subscribed attribute.
Note that you must declare which fields are dependent by passing in the list of dependent fields using the :use option as show above. If you do not do this then changes will not be published if the virtual attribute changes.
In some situations, publishing data models that are not persisted to the database can be very useful:
# Publisher side
class UserEvent
include Promiscuous::Publisher::Model::Ephemeral
attr_accessor :user_id, :event_name
publish :user_id, :event_name
end
UserEvent.create(:user_id => 123, :event_name => :registered)
# Subscriber side
class UserEvent
include Promiscuous::Subscriber::Model::Observer
attr_accessor :user_id, :event_name
subscribe :user_id, :event_name
after_create do
Mailer.send_email(:member_id => user_id, :type => :sign_up)
end
end
Promiscuous allows you to mix persisted models with ephemeral and observers. For example, one would use a mailer application that observes the user state attribute to send the appropriate email.
When publishing a model, the class hierarchy is also published to allow the subscriber to map classes. For example:
# Publisher side
class User
include Promiscuous::Publisher
publish :name, :email
end
class Member < User
publish :points
end
class Admin < User
publish :role
end
# Subscriber side
class User
include Promiscuous::Subscriber
subscribe :name, :email
end
class Member < User
subscribe :points
end
Notice that the Admin model is not subscribed. When the subscriber receives an admin model, the subscriber looks for its nearest parent, which is the User model, and treat the received admin as a user. Promiscuous follows polymorphism rules by traversing the published inheritance chain to find the subscriber's subclass. This can be quite handy to collapse a tree of subclasses at a given node in the hierarchy tree.
Promiscuous supports embedded documents (mongoid only feature):
class Address
include Mongoid::Document
include Promiscuous::Publisher
publish :street_name, :zipcode
end
class User
include Mongoid::Document
include Promiscuous::Publisher
embeds_many :addresses
publish :name, :email, :addresses
end
When mixing databases in the system, the primary key formats may not be compatible. Using a foreign key with a different column can be handy in this case. Example:
# Publisher side
class User
include Mongoid::Document
include Promiscuous::Publisher
publish :name, :email
end
# Subscriber side
class User < ActiveRecord::Base
include Promiscuous::Subscriber
subscribe :name, :email, :foreign_key => :crowdtap_id
end
By default, Promiscuous assumes that the model names are the same on the
publisher and subscriber but this can be overridden with the :as
argument.
# Publisher side
class User
include Promiscuous::Publisher
publish :name, :email, :as => :Member
end
# Subscriber side
class CrowdtapMember
include Promiscuous::Subscriber
subscribe :name, :email, :as => :Member
end
When using polymorphism, Promiscuous will attempt to map an object to the lowest possible class in its descendant chain. This opens up the possibility to collapse a hierarchy in a subscriber if desired:
# Publisher side
class User
include Promiscuous::Publisher
publish :name, :email
end
class Member < User
publish :points
end
class Admin < User
publish :role
end
# Subscriber side
class User
include Promiscuous::Subscriber
subscribe :name, :email, :role, :points
end
An Admin instance on the publisher will be subscribed as a User on the subscriber and still map
the points
attribute correctly.
There are two ways to define the publishers and subscribers attributes:
- directly in the model as shown in the examples above 2) in a separate file. In big applications, code organization is crucial. Depending on your philosophy, you can pick one way or the other.
class User
include Promiscuous::Publisher
publish :name
publish :email
end
# Or more succinctly:
class User
include Promiscuous::Publisher
publish :name, :email
end
With Mongoid, you can wrap all the field declarations in a publish/subscribe block. We recommand this syntax for subscribers as it serve as documentation.
class User
include Mongoid::Document
include Promiscuous::Publisher
publish do
field :name
field :email
belongs_to :group
end
field :password
end
Promiscuous definitions are automatically reloaded at every requests in development mode on the publisher side (TODO: reload for subscribers as well).
In some ways, the configuration file is the analogous of the
./config/routes.rb
file when comparing promiscuous and rails controllers.
Promiscuous will load any of the following files to find your Promiscuous definitions:
- ./config/promiscuous.rb
- ./config/publishers.rb
- ./config/subscribers.rb
Use Promiscuous.define
as shown below. Note that you must specify the
published/subscribed model name pluralized:
Promiscuous.define do
publish :user do
attributes :name, :email
end
end