Module: Rep::ClassMethods
- Defined in:
- lib/rep.rb
Instance Method Summary collapse
-
#all_json_fields ⇒ Object
We need a way to get a flat, uniq’ed list of all the fields accross all field sets.
-
#all_json_methods ⇒ Object
We need a wya to get a flat, uniq’ed list of all the method names accross all field sets.
-
#flat_json_fields(side = :right) ⇒ Object
‘#flat_json_fields` is just a utility method to DRY up the next two methods, because their code is almost exactly the same, it is not intended for use directly and might be confusing.
-
#initialize_with(*args, &blk) ⇒ Object
Defines an ‘#initialize` method that accepts a Hash argument and copies some keys out into `attr_accessors`.
-
#json_fields(arg = nil) ⇒ Object
‘#json_fields` setups up some class instance variables to remember sets of top level keys for json structures.
-
#register_accessor(acc) ⇒ Object
Defines an attr_accessor with a default value.
-
#shared(opts = {}) ⇒ Object
An easy way to save on GC is to use the same instance to turn an array of objects into hashes instead of instantiating a new object for every object in the array.
-
#to_proc ⇒ Object
The fanciest thing in this entire library is this ‘#to_proc` method.
Instance Method Details
#all_json_fields ⇒ Object
We need a way to get a flat, uniq’ed list of all the fields accross all field sets. This is that.
218 219 220 |
# File 'lib/rep.rb', line 218 def all_json_fields flat_json_fields(:left) end |
#all_json_methods ⇒ Object
We need a wya to get a flat, uniq’ed list of all the method names accross all field sets. This is that.
224 225 226 |
# File 'lib/rep.rb', line 224 def all_json_methods flat_json_fields(:right) end |
#flat_json_fields(side = :right) ⇒ Object
‘#flat_json_fields` is just a utility method to DRY up the next two methods, because their code is almost exactly the same, it is not intended for use directly and might be confusing.
202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/rep.rb', line 202 def flat_json_fields(side = :right) side_number = side == :right ? 1 : 0 json_fields.reduce([]) do |memo, (name, fields)| memo + fields.map do |field| if field.is_a?(Hash) field.to_a.first[side_number] # [name, method_name] else field end end end.uniq end |
#initialize_with(*args, &blk) ⇒ Object
Defines an ‘#initialize` method that accepts a Hash argument and copies some keys out into `attr_accessors`. If your class already has an `#iniatialize` method then this will overwrite it (so don’t use it). ‘#initialize_with` does not have to be used to use any other parts of Rep.
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/rep.rb', line 122 def initialize_with(*args, &blk) @initializiation_args = args # Remember what args we normally initialize with so we can refer to them when building shared instances. if defined?(define_singleton_method) define_singleton_method :initializiation_args do @initializiation_args end else singleton = class << self; self end singleton.send :define_method, :initializiation_args, lambda { @initializiation_args } end # Create an `attr_accessor` for each one. Defaults can be provided using the Hash version { :arg => :default_value } args.each { |a| register_accessor(a) } define_method(:initialize) { |opts={}| parse_opts(opts) } # `#parse_opts` is responsable for getting the `attr_accessor` values prefilled. Since defaults can be specified, it # must negotiate Hashes and use the first key of the hash for the `attr_accessor`'s name. define_method :parse_opts do |opts| @rep_options = opts blk.call(opts) unless blk.nil? self.class.initializiation_args.each do |field| name = field.is_a?(Hash) ? field.to_a.first.first : field instance_variable_set(:"@#{name}", opts[name]) end end end |
#json_fields(arg = nil) ⇒ Object
‘#json_fields` setups up some class instance variables to remember sets of top level keys for json structures. Example:
class A
json_fields [:one, :two, :three] => :default
end
A.json_fields(:default) # => [:one, :two, :three]
There is a general assumption that each top level key’s value is provided by a method of the same name on an instance of the class. If this is not true, a Hash syntax can be used to alias to a different method name. Example:
class A
json_fields [{ :one => :the_real_one_method }, :two, { :three => :some_other_three }] => :default
end
Once can also set multiple sets of fields. Example:
class A
json_fields [:one, :two, :three] => :default
json_fields [:five, :two, :six] => :other
end
And all fields are returned by calling ‘#json_fields` with no args. Example:
A.json_fields # => { :default => [:one, :two, :three], :other => [:five, :two, :six] }
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/rep.rb', line 183 def json_fields(arg = nil) if arg.is_a?(Hash) fields, name = arg.to_a.first @json_fields ||= {} @json_fields[name] = [fields].flatten elsif arg.is_a?(Symbol) @json_fields ||= {} @json_fields[arg] elsif arg === nil @json_fields || {} else # TODO: make an exception class raise "You can only use a Hash to set fields, a Symbol to retrieve them, or no argument to retrieve all fields for all names" end end |
#register_accessor(acc) ⇒ Object
Defines an attr_accessor with a default value. The default for default is nil. Example:
class A
register_accessor :name => "No Name"
end
A.new.name # => "No Name"
107 108 109 110 111 112 113 114 115 116 |
# File 'lib/rep.rb', line 107 def register_accessor(acc) name, default = acc.is_a?(Hash) ? acc.to_a.first : [acc, nil] attr_accessor name if default define_method name do var_name = :"@#{name}" instance_variable_get(var_name) || instance_variable_set(var_name, default) end end end |
#shared(opts = {}) ⇒ Object
An easy way to save on GC is to use the same instance to turn an array of objects into hashes instead of instantiating a new object for every object in the array. Here is an example of it’s usage:
class BookRep
initialize_with :book_model
fields :title => :default
forward :title => :book_model
end
BookRep.shared(:book_model => Book.first).to_hash # => { :title => "Moby Dick" }
BookRep.shared(:book_model => Book.last).to_hash # => { :title => "Lost Horizon" }
This should terrify you. If it doesn’t, then this example will:
book1 = BookRep.shared(:book_model => Book.first)
book2 = BookRep.shared(:book_model => Book.last)
boo1.object_id === book2.object_id # => true
**It really is a shared object.**
You really shouldn’t use this method directly for anything.
251 252 253 254 255 256 257 |
# File 'lib/rep.rb', line 251 def shared(opts = {}) @pointer = (Thread.current[:rep_shared_instances] ||= {}) @pointer[object_id] ||= new @pointer[object_id].reset_for_json! @pointer[object_id].parse_opts(opts) @pointer[object_id] end |
#to_proc ⇒ Object
The fanciest thing in this entire library is this ‘#to_proc` method. Here is an example of it’s usage:
class BookRep
initialize_with :book_model
fields :title => :default
forward :title => :book_model
end
Book.all.map(&BookRep) # => [{ :title => "Moby Dick" }, { :title => "Lost Horizon " }]
And now I will explain how it works. Any object can have a to_proc method and when you call ‘#map` on an array and hand it a proc it will in turn hand each object as an argument to that proc. What I’ve decided to do with this object is use it the options for a shared instance to make a hash.
Since I know the different initialization argumants from a call to ‘initialize_with`, I can infer by order which object is which option. Then I can create a Hash to give to `parse_opts` through the `shared` method. I hope that makes sense.
It allows for extremely clean Rails controllers like this:
class PhotosController < ApplicationController
respond_to :json, :html
def index
@photos = Photo.paginate(page: params[:page], per_page: 20)
respond_with @photos.map(&PhotoRep)
end
def show
@photo = Photo.find(params[:id])
respond_with PhotoRep.new(photo: @photo)
end
end
293 294 295 296 297 298 299 300 |
# File 'lib/rep.rb', line 293 def to_proc proc { |obj| arr = [obj].flatten init_args = @initializiation_args[0..(arr.length-1)] opts = Hash[init_args.zip(arr)] shared(opts).to_hash } end |