Module: Receiver
- Defined in:
- lib/gorillib/receiver.rb,
lib/gorillib/receiver/tree_diff.rb,
lib/gorillib/receiver/validations.rb,
lib/gorillib/receiver/acts_as_hash.rb,
lib/gorillib/receiver/acts_as_loadable.rb,
lib/gorillib/receiver/active_model_shim.rb
Overview
Receiver lets you describe complex (even recursive!) actively-typed data models that
-
are creatable or assignable from static data structures
-
perform efficient type conversion when assigning from a data structure,
-
but with nothing in the way of normal assignment or instantiation
-
and no requirements on the initializer
class Tweet include Receiver rcvr_accessor :id, Integer rcvr_accessor :user_id, Integer rcvr_accessor :created_at, Time end p Tweet.receive(:id => "7", :user_id => 9, :created_at => "20101231010203" ) # => #<Tweet @id=7, @user_id=9, @created_at=2010-12-31 07:02:03 UTC>
You can override receive behavior in a straightforward and predictable way:
class TwitterUser
include Receiver
rcvr_accessor :id, Integer
rcvr_accessor :screen_name, String
rcvr_accessor :follower_ids, Array, :of => Integer
# accumulate unique follower ids
def receive_follower_ids(arr)
@follower_ids = (@follower_ids||[]) + arr.map(&:to_i)
@follower_ids.uniq!
end
end
The receiver pattern works naturally with inheritance:
class TweetWithUser < Tweet
rcvr_accessor :user, TwitterUser
after_receive do |hsh|
self.user_id = self.user.id if self.user
end
end
p TweetWithUser.receive(:id => 8675309, :created_at => "20101231010203", :user => { :id => 24601, :screen_name => 'bob', :follower_ids => [1, 8, 3, 4] })
=> #<TweetWithUser @id=8675309, @created_at=2010-12-31 07:02:03 UTC, @user=#<TwitterUser @id=24601, @screen_name="bob", @follower_ids=[1, 8, 3, 4]>, @user_id=24601>
TweetWithUser was able to add another receiver, applicable only to itself and its subclasses.
The receive method works well with sparse data – you can accumulate attributes without trampling formerly set values:
tw = Tweet.receive(:id => "7", :user_id => 9 )
p tw
# => #<Tweet @id=7, @user_id=9>
tw.receive!(:created_at => "20101231010203" )
p tw
# => #<Tweet @id=7, @user_id=9, @created_at=2010-12-31 07:02:03 UTC>
Note the distinction between an explicit nil field and a missing field:
tw.receive!(:user_id => nil, :created_at => "20090506070809" )
p tw
# => #<Tweet @id=7, @user_id=nil, @created_at=2009-05-06 12:08:09 UTC>
There are helpers for default and required attributes:
class Foo
include Receiver
rcvr_accessor :is_reqd, String, :required => true
rcvr_accessor :also_reqd, String, :required => true
rcvr_accessor :has_default, String, :default => 'hello'
end
foo_obj = Foo.receive(:is_reqd => "hi")
# => #<Foo:0x00000100bd9740 @is_reqd="hi" @has_default="hello">
foo_obj.missing_attrs
# => [:also_reqd]
Defined Under Namespace
Modules: ActiveModelShim, ActsAsHash, ActsAsLoadable, ClassMethods
Constant Summary collapse
- RECEIVER_BODIES =
{}
- TYPE_ALIASES =
{ :null => NilClass, :boolean => Boolean, :string => String, :bytes => String, :symbol => Symbol, :int => Integer, :integer => Integer, :long => Integer, :time => Time, :date => Date, :float => Float, :double => Float, :hash => Hash, :map => Hash, :array => Array, }
Class Method Summary collapse
-
.included(base) ⇒ Object
set up receiver attributes, and bring in methods from the ClassMethods module at class-level.
Instance Method Summary collapse
-
#attr_set?(attr) ⇒ Boolean
true if the attr is a receiver variable and it has been set.
-
#missing_attrs ⇒ Object
returns a list of required but missing attributes.
-
#receive!(hsh = {}) ⇒ Object
modify object in place with new typecast values.
- #to_tuple ⇒ Object
- #tree_diff(other) ⇒ Object
-
#validation_errors ⇒ Object
An array of strings describing any ways this fails validation.
Class Method Details
.included(base) ⇒ Object
set up receiver attributes, and bring in methods from the ClassMethods module at class-level
388 389 390 391 392 393 394 395 396 397 398 399 400 |
# File 'lib/gorillib/receiver.rb', line 388 def self.included base base.class_eval do unless method_defined?(:receiver_attrs) class_attribute :receiver_attrs class_attribute :receiver_attr_names class_attribute :after_receivers self.receiver_attrs = {} # info about the attr self.receiver_attr_names = [] # ordered set of attr names self.after_receivers = [] # blocks to execute following receive! extend ClassMethods end end end |
Instance Method Details
#attr_set?(attr) ⇒ Boolean
true if the attr is a receiver variable and it has been set
144 145 146 |
# File 'lib/gorillib/receiver.rb', line 144 def attr_set?(attr) receiver_attrs.has_key?(attr) && self.instance_variable_defined?("@#{attr}") end |
#missing_attrs ⇒ Object
returns a list of required but missing attributes
13 14 15 16 17 18 19 |
# File 'lib/gorillib/receiver/validations.rb', line 13 def missing_attrs missing = [] self.class.required_rcvrs.each do |name, info| missing << name if (not attr_set?(name)) end missing end |
#receive!(hsh = {}) ⇒ Object
modify object in place with new typecast values.
129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/gorillib/receiver.rb', line 129 def receive! hsh={} raise ArgumentError, "Can't receive (it isn't hashlike): {#{hsh.inspect}}" unless hsh.respond_to?(:[]) && hsh.respond_to?(:has_key?) _receiver_fields.each do |attr| if hsh.has_key?(attr.to_sym) then val = hsh[attr.to_sym] elsif hsh.has_key?(attr.to_s) then val = hsh[attr.to_s] else next ; end _receive_attr attr, val end impose_defaults!(hsh) (hsh) run_after_receivers(hsh) self end |
#to_tuple ⇒ Object
361 362 363 364 365 366 367 368 369 370 371 |
# File 'lib/gorillib/receiver.rb', line 361 def to_tuple tuple = [] self.each_value do |val| if val.respond_to?(:to_tuple) tuple += val.to_tuple else tuple << val end end tuple end |
#tree_diff(other) ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# File 'lib/gorillib/receiver/tree_diff.rb', line 2 def tree_diff(other) diff_hsh = {} other = other.symbolize_keys if other.respond_to?(:symbolize_keys) each do |k, v| case when v.is_a?(Array) && other[k].is_a?(Array) val = v.tree_diff(other[k]) diff_hsh[k] = val unless val.blank? when v.respond_to?(:tree_diff) && other[k].respond_to?(:to_hash) val = v.tree_diff(other[k]) diff_hsh[k] = val unless val.blank? else diff_hsh[k] = v unless v == other[k] end end other_hsh = other.dup.delete_if{|k, v| has_key?(k) } diff_hsh.merge!(other_hsh) end |
#validation_errors ⇒ Object
An array of strings describing any ways this fails validation
4 5 6 7 8 9 10 |
# File 'lib/gorillib/receiver/validations.rb', line 4 def validation_errors errors = [] if (ma = missing_attrs).present? errors << "Missing values for {#{ma.join(",")}}" end errors end |