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.
229 230 231 |
# File 'lib/rep.rb', line 229 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.
235 236 237 |
# File 'lib/rep.rb', line 235 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.
213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/rep.rb', line 213 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.
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/rep.rb', line 133 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] }
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/rep.rb', line 194 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"
118 119 120 121 122 123 124 125 126 127 |
# File 'lib/rep.rb', line 118 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.
262 263 264 265 266 267 268 |
# File 'lib/rep.rb', line 262 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
304 305 306 307 308 309 310 311 |
# File 'lib/rep.rb', line 304 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 |