data_bindings 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,32 @@
1
+ module DataBindings
2
+ module Adapters
3
+ module TNetstring
4
+ include Ruby
5
+ include DataBindings::GemRequirement
6
+
7
+ # Constructs a wrapped object from a Tnetstring
8
+ # @param [String] str The Tnetstring object
9
+ # @return [RubyObjectAdapter, RubyArrayAdapter] The wrapped object
10
+ def from_tnetstring(str)
11
+ from_ruby(::TNetstring.parse(str)[0])
12
+ end
13
+ gentle_require_gem :from_tnetstring, 'tnetstring'
14
+
15
+ module Convert
16
+ include ConverterHelper
17
+ include DataBindings::GemRequirement
18
+
19
+ # Creates a String repsentation of a Ruby Hash or Array.
20
+ # @param [Generator] generator The generator that invokes this constructor
21
+ # @param [Symbol] name The name of the binding used on this object
22
+ # @param [Array, Hash] obj The object to be represented in JSON
23
+ # @return [String] The Tnetstring representation of this object
24
+ def force_convert_to_tnetstring
25
+ ::TNetstring.dump(self.to_hash)
26
+ end
27
+ gentle_require_gem :force_convert_to_tnetstring, 'tnetstring'
28
+ standard_converter :convert_to_tnetstring
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,74 @@
1
+ module DataBindings
2
+ module Adapters
3
+ module XML
4
+ include Ruby
5
+ include DataBindings::GemRequirement
6
+
7
+ # Constructs a wrapped object from a JSON string
8
+ # @param [String] str The JSON object
9
+ # @return [RubyObjectAdapter, RubyArrayAdapter] The wrapped object
10
+ def from_xml(str)
11
+ from_ruby(from_xml_obj(Nokogiri::XML(str)))
12
+ end
13
+ gentle_require_gem :from_xml, 'nokogiri'
14
+
15
+ def from_xml_obj(o)
16
+ case o.type
17
+ when Nokogiri::XML::Node::DOCUMENT_NODE
18
+ from_xml_obj(o.children[0])
19
+ when Nokogiri::XML::Node::TEXT_NODE
20
+ o.text
21
+ when Nokogiri::XML::Node::ELEMENT_NODE
22
+ if o.children.size == 1 and o.children[0].text?
23
+ from_xml_obj(o.children[0])
24
+ elsif o.children[0].name == '0'
25
+ o.children.map{ |c| from_xml_obj(c) }
26
+ else
27
+ Hash[o.children.map { |n| [n.name, from_xml_obj(n)] }]
28
+ end
29
+ end
30
+ end
31
+ gentle_require_gem :from_xml_obj, 'nokogiri'
32
+
33
+ # Creates a String repsentation of a Ruby Hash or Array.
34
+ # @param [Generator] generator The generator that invokes this constructor
35
+ # @param [Symbol] name The name of the binding used on this object
36
+ # @param [Array, Hash] obj The object to be represented in JSON
37
+ # @return [String] The JSON representation of this object
38
+
39
+ module Convert
40
+ include DataBindings::GemRequirement
41
+ include ConverterHelper
42
+
43
+ def force_convert_to_xml
44
+ Convert.construct(@generator, @name, self, @binding_block)
45
+ end
46
+ gentle_require_gem :force_convert_to_xml, 'builder'
47
+ standard_converter :convert_to_xml
48
+
49
+ def self.construct(generator, name, obj, builder = nil)
50
+ root = builder.nil?
51
+ builder ||= Builder::XmlMarkup.new
52
+ builder.instruct!(:xml, :encoding => "UTF-8") if root
53
+ case obj
54
+ when Array
55
+ builder.__send__(name || "doc") do |b|
56
+ obj.each_with_index(o, i)
57
+ construct(generator, i.to_s, o, b)
58
+ end
59
+ when Hash
60
+ builder.__send__(name || "doc") do |b|
61
+ obj.each do |k, v|
62
+ construct(generator, k, v, b)
63
+ end
64
+ end
65
+ else
66
+ builder.__send__(name, obj)
67
+ end
68
+ builder.target! if root
69
+ end
70
+
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,26 @@
1
+ require 'yaml'
2
+
3
+ module DataBindings
4
+ module Adapters
5
+ module YAML
6
+ include Ruby
7
+
8
+ def from_yaml(str)
9
+ from_ruby(::YAML::load(str))
10
+ end
11
+
12
+ def from_yaml_file(f)
13
+ from_ruby(::YAML::load_file(f))
14
+ end
15
+
16
+ module Convert
17
+ include ConverterHelper
18
+
19
+ def force_convert_to_yaml
20
+ ::YAML::dump(self.to_nonindifferent_hash)
21
+ end
22
+ standard_converter :convert_to_yaml
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ module DataBindings
2
+ module Adapters
3
+ autoload :BSON, 'data_bindings/adapters/bson'
4
+ autoload :JSON, 'data_bindings/adapters/json'
5
+ autoload :Native, 'data_bindings/adapters/native'
6
+ autoload :Ruby, 'data_bindings/adapters/ruby'
7
+ autoload :YAML, 'data_bindings/adapters/yaml'
8
+ autoload :Params, 'data_bindings/adapters/params'
9
+ autoload :XML, 'data_bindings/adapters/xml'
10
+ autoload :TNetstring, 'data_bindings/adapters/tnetstring'
11
+ end
12
+ end
@@ -0,0 +1,331 @@
1
+ module DataBindings
2
+ module Bound
3
+ ValidationError = Class.new(RuntimeError)
4
+ NoBindingName = Class.new(RuntimeError)
5
+
6
+ class Errors < DataBindings::IndifferentHash
7
+ attr_accessor :base
8
+
9
+ def join(st = nil)
10
+ (base ? [base].concat(values) : values).join(st)
11
+ end
12
+
13
+ def valid?
14
+ base.nil? && empty?
15
+ end
16
+
17
+ def clear
18
+ super
19
+ @base = nil
20
+ end
21
+ end
22
+
23
+ #include DataBindings::WriterInterceptor
24
+
25
+ attr_reader :errors, :source, :name, :generator
26
+
27
+ def valid?
28
+ calculate_validness
29
+ errors.valid?
30
+ end
31
+
32
+ def calculate_validness
33
+ if @last_hash.nil? || @last_hash != hash
34
+ @from = self if @last_hash
35
+ errors.clear
36
+ validate
37
+ @last_hash = hash
38
+ end
39
+ end
40
+
41
+ def valid!
42
+ valid? or raise FailedValidation.new("Object was invalid with the following errors: #{errors.join(", ")}", errors, source)
43
+ self
44
+ end
45
+
46
+ def pre_convert
47
+ valid!
48
+ end
49
+
50
+ def cast_element(lookup_name, source, type, opts = nil, &blk)
51
+ name = get_parameter_name(lookup_name, source, opts)
52
+ el = name && source[name]
53
+ raise_on_error = opts && opts.key?(:raise_on_error) ? opts[:raise_on_error] : false
54
+ allow_nil = opts && opts.key?(:allow_nil) ? opts[:allow_nil] : false
55
+ el ||= opts[:default] if opts && opts.key?(:default)
56
+
57
+ if el.nil? && name.nil?
58
+ @errors[lookup_name] = generate_error("not found", raise_on_error)
59
+ nil
60
+ else
61
+ new_el = if type.nil?
62
+ # anything goes
63
+ case el
64
+ when Array, Hash
65
+ blk ? register_sub(name, @generator.from_ruby(el).bind(&blk), raise_on_error) : el
66
+ else
67
+ el
68
+ end
69
+ elsif type == String
70
+ @errors[lookup_name] = generate_error("was not a String", raise_on_error) unless el.is_a?(String)
71
+ el
72
+ elsif type == Integer
73
+ begin
74
+ Integer(el)
75
+ rescue ArgumentError, TypeError
76
+ @errors[lookup_name] = generate_error("was not an Integer", raise_on_error)
77
+ el
78
+ end
79
+ elsif type == Float
80
+ begin
81
+ Float(el)
82
+ rescue ArgumentError, TypeError
83
+ @errors[lookup_name] = generate_error("was not a Float", raise_on_error)
84
+ el
85
+ end
86
+ elsif Array === type
87
+ if el.is_a?(Array)
88
+ @errors[lookup_name] = generate_error("did not match the length", raise_on_error) if opts && opts[:length] && !(opts[:length] === el.size)
89
+ if type.first
90
+ register_sub(name, @generator.from_ruby(el).bind_array { all_elements type.first }, raise_on_error)
91
+ elsif blk
92
+ register_sub(name, @generator.from_ruby(el).bind_array { all_elements &blk }, raise_on_error)
93
+ else
94
+ el
95
+ end
96
+ else
97
+ @errors[lookup_name] = generate_error("was not an Array", raise_on_error)
98
+ el
99
+ end
100
+ elsif type == :boolean
101
+ if allow_nil
102
+ el.nil? ? nil : DataBindings.true_boolean?(el)
103
+ else
104
+ DataBindings.true_boolean?(el)
105
+ end
106
+ elsif Symbol === type
107
+ if el.is_a?(Hash)
108
+ register_sub(name, @generator.from_ruby(el).bind(type), raise_on_error)
109
+ else
110
+ @errors[lookup_name] = generate_error("was not a Hash", raise_on_error)
111
+ el
112
+ end
113
+ else
114
+ raise "Unknown type #{type.inspect}"
115
+ end
116
+ if inclusion = opts && opts[:in]
117
+ @errors[lookup_name] = generate_error("was not included in #{inclusion.inspect}", raise_on_error) unless inclusion.include?(new_el)
118
+ end
119
+ @errors[lookup_name] = generate_error("was nil", raise_on_error) if new_el.nil? && !allow_nil
120
+ new_el
121
+ end
122
+ end
123
+
124
+ private
125
+
126
+ def dump_val(val)
127
+ if val.respond_to?(:to_hash)
128
+ val.to_hash
129
+ elsif val.respond_to?(:to_ary)
130
+ val.to_ary
131
+ else
132
+ val
133
+ end
134
+ end
135
+
136
+ def convert_target
137
+ self
138
+ end
139
+
140
+ def generate_error(str, raise_on_error)
141
+ raise_on_error ? raise(ValidationError, str) : str
142
+ end
143
+
144
+ def register_sub(name, sub, raise_on_error)
145
+ unless sub.valid?
146
+ @errors[name] = generate_error(sub.errors.to_s, raise_on_error)
147
+ end
148
+ sub
149
+ end
150
+
151
+ def validate
152
+ reset_validation_state
153
+ run_validation
154
+ enforce_strictness if @strict
155
+ end
156
+
157
+ def init_bound(generator, source, name, opts, validator)
158
+ @errors, @generator, @source, @name, @opts, @validator = Errors.new, generator, source, name, opts, validator
159
+ @from = @source
160
+ @strict = opts && opts.key?(:strict) ? opts[:strict] : generator.strict?
161
+ reset_validation_state
162
+ valid?
163
+ end
164
+
165
+ class BoundObject < DataBindings::IndifferentHash
166
+ include Bound
167
+
168
+ def initialize(generator, array_expected, source, name, opts, &blk)
169
+ raise BindingMismatch if array_expected
170
+ init_bound(generator, source, name, opts, blk)
171
+ end
172
+
173
+ def to_hash
174
+ keys.inject(DataBindings::IndifferentHash.new) { |h, k|
175
+ val = self[k]
176
+ h[k] = dump_val(val)
177
+ h
178
+ }
179
+ end
180
+
181
+ def to_nonindifferent_hash
182
+ keys.inject({}) { |h, k|
183
+ val = self[k]
184
+ h[k.to_s] = dump_val(val)
185
+ h
186
+ }
187
+ end
188
+
189
+ def to_native
190
+ valid!
191
+ data = inject(IndifferentHash.new) { |h, (k, v)|
192
+ h[k] = v.respond_to?(:to_native) ? v.to_native : v
193
+ h
194
+ }
195
+ if constructor = generator.native_constructors[name]
196
+ o = constructor[data.to_hash]
197
+ else
198
+ OpenStruct.new(data)
199
+ end
200
+ end
201
+
202
+ def property(name, type = nil, opts = nil, &blk)
203
+ type, opts = nil, type if type.is_a?(Hash)
204
+ self[name] = cast_element(name, @from, type, opts, &blk)
205
+ end
206
+
207
+ def required(name, type = nil, opts = nil, &blk)
208
+ type, opts = nil, type if type.is_a?(Hash)
209
+ opts ||= {}
210
+ opts[:allow_nil] = false
211
+ property(name, type, opts, &blk)
212
+ end
213
+
214
+ def optional(name, type = nil, opts = nil, &blk)
215
+ type, opts = nil, type if type.is_a?(Hash)
216
+ opts ||= {}
217
+ opts[:allow_nil] = true
218
+ property(name, type, opts, &blk)
219
+ end
220
+
221
+ def all_properties(type = nil, opts = nil)
222
+ type, opts = nil, type if type.is_a?(Hash)
223
+ @from.keys.each do |key|
224
+ property key, type, opts
225
+ end
226
+ end
227
+
228
+ def enforce_strictness
229
+ @errors.base = "hasn't been fully matched" unless size == @from.size
230
+ end
231
+
232
+ def copy_source
233
+ replace @source
234
+ end
235
+
236
+ private
237
+
238
+ def get_parameter_name(name, source, opts)
239
+ name = if opts && opts[:alias]
240
+ aliases = Array(opts[:alias])
241
+ index = aliases.index {|k| source.key?(k) }
242
+ index ? aliases[index] : name
243
+ else
244
+ name
245
+ end
246
+ source.key?(name) ? name : nil
247
+ end
248
+
249
+ def reset_validation_state
250
+ errors.clear
251
+ end
252
+
253
+ def run_validation
254
+ instance_eval(&@validator)
255
+ end
256
+ end
257
+
258
+ class BoundArray < Array
259
+ include Bound
260
+
261
+ def initialize(generator, array_expected, source, name, opts, &blk)
262
+ raise BindingMismatch unless array_expected
263
+ init_bound(generator, source, name, opts, blk)
264
+ end
265
+
266
+ def to_ary
267
+ self.inject([]) { |a, v|
268
+ a << dump_val(v)
269
+ }
270
+ end
271
+
272
+ def to_native
273
+ valid!
274
+ inject([]) {|a, el| a << (el.respond_to?(:to_native) ? el.to_native : el); a}
275
+ end
276
+
277
+ def elements(size = nil, type = nil, opts = nil, &blk)
278
+ if size.nil? || size.respond_to?(:to_int)
279
+ size ||= source.size
280
+ # consume all
281
+ (@pos...(@pos+size)).each do |i|
282
+ self[@pos] = cast_element(@pos, @from, type, opts, &blk)
283
+ @pos += 1
284
+ end
285
+ elsif size.respond_to?(:min) && size.respond_to?(:max)
286
+ original_pos = @pos
287
+ while (@pos - original_pos) <= size.max
288
+ begin
289
+ opts ||= {}
290
+ opts[:raise_on_error] = @pos >= size.min
291
+ self[@pos] = cast_element(@pos, @from, type, opts, &blk)
292
+ rescue ValidationError
293
+ break
294
+ end
295
+ @pos += 1
296
+ end
297
+ else
298
+ raise "Size isn't understood: #{size.inspect}"
299
+ end
300
+ end
301
+
302
+ def copy_source
303
+ replace @source
304
+ end
305
+
306
+ def enforce_strictness
307
+ @errors.base = "hasn't been fully matched" unless @pos.succ == source.size
308
+ end
309
+
310
+ def all_elements(type = nil, &blk)
311
+ elements(nil, type, &blk)
312
+ end
313
+
314
+ private
315
+
316
+ def get_parameter_name(name, source, opts)
317
+ source.at(name) ? name : nil
318
+ end
319
+
320
+ def reset_validation_state
321
+ @pos = 0
322
+ errors.clear
323
+ end
324
+
325
+ def run_validation
326
+ errors.base = "didn't match legnth #{@opts[:length]}" if @opts && @opts[:length] && !(@opts[:length] === size)
327
+ instance_eval(&@validator)
328
+ end
329
+ end
330
+ end
331
+ end