Module: Candy::Wrapper
- Defined in:
- lib/candy/wrapper.rb
Overview
Utility methods to serialize and unserialize many types of objects into BSON.
Constant Summary collapse
- BSON_SAFE =
[String, Symbol, NilClass, TrueClass, FalseClass, Fixnum, Float, Time, Regexp, BSON::ByteBuffer, BSON::ObjectID, BSON::Code, BSON::DBRef]
Class Method Summary collapse
-
.unwrap(thing, parent = nil, attribute = nil) ⇒ Object
Undoes any magic from the Wrapper.wrap method.
-
.unwrap_hash(hash, parent = nil, attribute = nil) ⇒ Object
Returns the hash as a Candy::Piece if a class name is embedded, or a CandyHash object otherwise.
-
.unwrap_key(key) ⇒ Object
The inverse of Wrapper.wrap_key – removes single-quoting from strings and converts other strings to symbols.
-
.unwrap_object(hash) ⇒ Object
Turns a hashed object back into an object of the stated class, setting any captured instance variables.
-
.wrap(thing) ⇒ Object
Makes an object safe for the sharp pointy edges of MongoDB.
-
.wrap_array(array) ⇒ Object
Takes an array and returns the same array with unsafe objects wrapped.
-
.wrap_hash(hash) ⇒ Object
Takes a hash and returns it with values wrapped.
-
.wrap_key(key) ⇒ Object
Lightly wraps hash keys, converting symbols to strings and wrapping strings in single quotes.
-
.wrap_object(object) ⇒ Object
Returns a nested hash containing the class and instance variables of the object.
Class Method Details
.unwrap(thing, parent = nil, attribute = nil) ⇒ Object
Undoes any magic from the Wrapper.wrap method. Almost everything falls through untouched except for arrays and hashes. The ‘parent’ and ‘attribute’ parameters are for recursively setting the parent properties of embedded Candy objects.
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/candy/wrapper.rb', line 94 def self.unwrap(thing, parent=nil, attribute=nil) case thing when Hash if thing.has_key?("__object_") unwrap_object(thing) else unwrap_hash(thing, parent, attribute) end when Array if parent # We only want to create CandyArrays inside Candy pieces CandyArray.(parent, attribute, *thing) else thing.collect {|element| unwrap(element)} end else thing end end |
.unwrap_hash(hash, parent = nil, attribute = nil) ⇒ Object
Returns the hash as a Candy::Piece if a class name is embedded, or a CandyHash object otherwise. The ‘parent’ and ‘attribute’ parameters should be set by the caller if this is an embedded Candy object.
117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/candy/wrapper.rb', line 117 def self.unwrap_hash(hash, parent=nil, attribute=nil) if class_name = hash.delete(CLASS_KEY.to_s) klass = qualified_const_get(class_name) else klass = CandyHash end if parent klass.(parent, attribute, hash) else klass.piece(hash) end end |
.unwrap_key(key) ⇒ Object
The inverse of Wrapper.wrap_key – removes single-quoting from strings and converts other strings to symbols.
133 134 135 136 137 138 139 |
# File 'lib/candy/wrapper.rb', line 133 def self.unwrap_key(key) if key =~ /^'(.*)'$/ $1 else key.to_sym end end |
.unwrap_object(hash) ⇒ Object
Turns a hashed object back into an object of the stated class, setting any captured instance variables. The main limitation is that the object’s class must respond to Class.new without any parameters; we will not attempt to guess at any complex initialization behavior.
144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/candy/wrapper.rb', line 144 def self.unwrap_object(hash) if innards = hash["__object_"] klass = Kernel.qualified_const_get(innards["class"]) object = klass.new if innards["ivars"] innards["ivars"].each do |name, value| object.instance_variable_set(name, unwrap(value)) end end object end end |
.wrap(thing) ⇒ Object
Makes an object safe for the sharp pointy edges of MongoDB. Types properly serialized by the BSON.serialize call get passed through unmolested; others are unpacked and their pieces individually shrink-wrapped.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/candy/wrapper.rb', line 27 def self.wrap(thing) # Pass the simple cases through return thing if BSON_SAFE.include?(thing.class) thing = thing.to_candy if thing.respond_to?(:to_candy) # Make it sweeter if it can be sweetened case thing when Array wrap_array(thing) when Hash wrap_hash(thing) when Numeric # The most obvious are in BSON_SAFE, but not all thing when Date thing.to_time # Problem children when Proc raise TypeError, "Candy can't wrap Proc objects!" when Range raise TypeError, "Candy can't wrap ranges!" else wrap_object(thing) # Our catchall machinery end end |
.wrap_array(array) ⇒ Object
Takes an array and returns the same array with unsafe objects wrapped.
51 52 53 |
# File 'lib/candy/wrapper.rb', line 51 def self.wrap_array(array) array.map {|element| wrap(element)} end |
.wrap_hash(hash) ⇒ Object
Takes a hash and returns it with values wrapped. Symbol keys are reversibly converted to strings.
56 57 58 59 60 61 62 |
# File 'lib/candy/wrapper.rb', line 56 def self.wrap_hash(hash) wrapped = {} hash.each do |key, value| wrapped[wrap_key(key)] = wrap(value) end wrapped end |
.wrap_key(key) ⇒ Object
Lightly wraps hash keys, converting symbols to strings and wrapping strings in single quotes. Thus, we can recover symbols when we unwrap them later. Other key types will raise an exception.
66 67 68 69 70 71 72 73 74 75 |
# File 'lib/candy/wrapper.rb', line 66 def self.wrap_key(key) case key when String "'#{key}'" when Symbol key.to_s else raise TypeError, "Candy field names must be strings or symbols. You gave us #{key.class}: #{key}" end end |
.wrap_object(object) ⇒ Object
Returns a nested hash containing the class and instance variables of the object. It’s not the deepest we could ever go (it doesn’t handle singleton methods, etc.) but it’s a start.
79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/candy/wrapper.rb', line 79 def self.wrap_object(object) wrapped = {"class" => object.class.name} ivars = {} object.instance_variables.each do |ivar| # Different Ruby versions spit different things out for instance_variables. Annoying. ivar_name = '@' + ivar.to_s.sub(/^@/,'') ivars[ivar_name] = wrap(object.instance_variable_get(ivar_name)) end wrapped["ivars"] = ivars unless ivars.empty? {"__object_" => wrapped} end |