data_bindings 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/README.md +167 -0
- data/Rakefile +13 -0
- data/data_bindings.gemspec +35 -0
- data/lib/data_bindings/adapters/bson.rb +32 -0
- data/lib/data_bindings/adapters/json.rb +32 -0
- data/lib/data_bindings/adapters/native.rb +50 -0
- data/lib/data_bindings/adapters/params.rb +91 -0
- data/lib/data_bindings/adapters/ruby.rb +44 -0
- data/lib/data_bindings/adapters/tnetstring.rb +32 -0
- data/lib/data_bindings/adapters/xml.rb +74 -0
- data/lib/data_bindings/adapters/yaml.rb +26 -0
- data/lib/data_bindings/adapters.rb +12 -0
- data/lib/data_bindings/bound.rb +331 -0
- data/lib/data_bindings/converters.rb +83 -0
- data/lib/data_bindings/generator.rb +140 -0
- data/lib/data_bindings/unbound.rb +76 -0
- data/lib/data_bindings/util.rb +78 -0
- data/lib/data_bindings/version.rb +3 -0
- data/lib/data_bindings.rb +52 -0
- data/test/array_test.rb +55 -0
- data/test/bson_test.rb +23 -0
- data/test/converter_test.rb +36 -0
- data/test/data_bindings_test.rb +67 -0
- data/test/fixtures/1.json +1 -0
- data/test/json_test.rb +40 -0
- data/test/native_test.rb +64 -0
- data/test/params_test.rb +60 -0
- data/test/test_helper.rb +17 -0
- data/test/tnetstring_test.rb +23 -0
- data/test/validation_test.rb +217 -0
- data/test/xml_test.rb +20 -0
- data/test/yaml_test.rb +19 -0
- metadata +282 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
module DataBindings
|
|
2
|
+
|
|
3
|
+
# Exception raised by invalid #*_http calls.
|
|
4
|
+
class HttpError < RuntimeError
|
|
5
|
+
# The HTTParty::Response object underlying this exception
|
|
6
|
+
attr_reader :response
|
|
7
|
+
|
|
8
|
+
def initialize(m, response)
|
|
9
|
+
super m
|
|
10
|
+
@response = response
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# This defines the default readers used.
|
|
15
|
+
module Readers
|
|
16
|
+
include GemRequirement
|
|
17
|
+
|
|
18
|
+
# Takes an IO object and reads it's contents.
|
|
19
|
+
# @param [IO] i The IO object to read from
|
|
20
|
+
# @return The contents of the IO object
|
|
21
|
+
def io(i)
|
|
22
|
+
i.rewind
|
|
23
|
+
i.read
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Takes a file path and returns it's contents.
|
|
27
|
+
# @param [String] path The file path
|
|
28
|
+
# @return The contents of the file
|
|
29
|
+
def file(path)
|
|
30
|
+
File.read(path)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Takes a URL and returns it's contents. Uses HTTPParty underlyingly.
|
|
34
|
+
# @param [String] url The URL to request from
|
|
35
|
+
# @param [Hash] opts The options to pass in to HTTPParty
|
|
36
|
+
# @return The body of the response from the URL as a String
|
|
37
|
+
# @see https://github.com/jnunemaker/httparty
|
|
38
|
+
def http(url, opts = {})
|
|
39
|
+
method = opts[:method] || :get
|
|
40
|
+
response = HTTParty.send(method, url, opts)
|
|
41
|
+
if (200..299).include?(response.code)
|
|
42
|
+
response.body
|
|
43
|
+
else
|
|
44
|
+
raise HttpError.new("Bad response: #{response.code} #{response.body}", response)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
gentle_require_gem :http, 'httparty'
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# This defines the default writers used.
|
|
51
|
+
module Writers
|
|
52
|
+
include GemRequirement
|
|
53
|
+
|
|
54
|
+
# Takes data and an IO object and writes it's contents to it.
|
|
55
|
+
# @param [String] data The data to be written
|
|
56
|
+
# @param [IO] i The IO object to write to
|
|
57
|
+
def io(data, io)
|
|
58
|
+
io.write(obj)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Takes data and a file path and writes it's contents to it.
|
|
62
|
+
# @param [String] data The data to be written
|
|
63
|
+
# @param [String] path The IO object to write to
|
|
64
|
+
def file(data, path)
|
|
65
|
+
File.open(path, 'w') { |f| f << data }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Takes a URL and posts the contents of your data to it. Uses HTTPParty underlyingly.
|
|
69
|
+
# @param [String] data The data to send to
|
|
70
|
+
# @param [String] url The URL to send your request to
|
|
71
|
+
# @param [Hash] opts The options to pass in to HTTPParty
|
|
72
|
+
# @see https://github.com/jnunemaker/httparty
|
|
73
|
+
def http(data, url, opts = {})
|
|
74
|
+
method = opts[:method] || :post
|
|
75
|
+
opts[:data] = data
|
|
76
|
+
response = HTTParty.send(method, url, opts)
|
|
77
|
+
unless (200..299).include?(response.code)
|
|
78
|
+
raise HttpError.new("Bad response: #{response.code} #{response.body}", response)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
gentle_require_gem :http, 'httparty'
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
module DataBindings
|
|
2
|
+
# This is the class the handles registering readers, writers, adapters and types.
|
|
3
|
+
class Generator
|
|
4
|
+
# Enable/disable strict mode
|
|
5
|
+
attr_accessor :strict
|
|
6
|
+
alias_method :strict?, :strict
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
reset!
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Defines an object type
|
|
13
|
+
# @param [Symbol] name The name of the type
|
|
14
|
+
# @see https://github.com/joshbuddy/data_bindings/wiki/Types
|
|
15
|
+
def type(name, &blk)
|
|
16
|
+
@types[name] = blk
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Retrieves an object type
|
|
20
|
+
# @param [Symbol] name The name of the type
|
|
21
|
+
# @return [Proc] The body of the type
|
|
22
|
+
def get_type(name)
|
|
23
|
+
@types[name]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def binding_class(cls)
|
|
27
|
+
mod = @writer_module
|
|
28
|
+
@binding_classes[cls] ||= begin
|
|
29
|
+
Class.new(cls) do
|
|
30
|
+
include mod
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Retrieves an adapter
|
|
36
|
+
# @param [Symbol] name The name of the adapter
|
|
37
|
+
# @return [Object] The adapter
|
|
38
|
+
def get_adapter(name)
|
|
39
|
+
@adapter_classes[name] or raise UnknownAdapterError, "Could not find adapter #{name.inspect}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Defines a reader
|
|
43
|
+
# @param [Symbol] name The name of the reader
|
|
44
|
+
# @yield [*Object] All arguments passed to the method used to invoke this reader
|
|
45
|
+
def reader(name, &blk)
|
|
46
|
+
@reader_module.define_singleton_method(name, &blk)
|
|
47
|
+
@build = false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Defines a writer
|
|
51
|
+
# @param [Symbol] name The name of the writer
|
|
52
|
+
# @yield [*Object] All arguments passed to the method used to invoke this writer
|
|
53
|
+
def writer(name, &blk)
|
|
54
|
+
@writer_module.define_singleton_method(name, &blk)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Passes off writing of an object through a specific writer.
|
|
58
|
+
# @param [Symbol] method_name The method name to be invoked on the writer
|
|
59
|
+
# @param [String] data The data to be written
|
|
60
|
+
def write(method_name, obj, *args, &blk)
|
|
61
|
+
@writer_module.send(method_name, obj, *args, &blk)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Tests if a specific type of writer is supported
|
|
65
|
+
# @param [Symbol] name The name of the writer to test
|
|
66
|
+
# @return [Boolean]
|
|
67
|
+
def write_targets(name)
|
|
68
|
+
target, format = name.to_s.split(/_/, 2)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Registers an adapter
|
|
72
|
+
# @param [Symbol] name The name of the adapter
|
|
73
|
+
# @param [Object] The adapter
|
|
74
|
+
def register(name, cls)
|
|
75
|
+
@adapters[name] = cls
|
|
76
|
+
@build = false
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Resets the generator to a blank state
|
|
80
|
+
def reset!
|
|
81
|
+
@reader_module = Module.new { extend Readers }
|
|
82
|
+
@writer_module = Module.new { extend Writers; include WritingInterceptor }
|
|
83
|
+
@strict = false
|
|
84
|
+
@types = {}
|
|
85
|
+
@adapters = {}
|
|
86
|
+
@adapter_classes = {}
|
|
87
|
+
@binding_classes = {}
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Defines a native constructor
|
|
91
|
+
# @param [Symbol] name The name of the type to create a constructor for
|
|
92
|
+
def for_native(name, &blk)
|
|
93
|
+
native_constructors[name] = blk
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def native_constructors
|
|
97
|
+
@native_constructors ||= {}
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
# @api private
|
|
102
|
+
def build!
|
|
103
|
+
return if @built
|
|
104
|
+
@adapters.each do |name, cls|
|
|
105
|
+
unless @adapter_classes[name]
|
|
106
|
+
@adapter_classes[name] = cls.to_s.split('::').inject(Object) {|const, n| const.const_get(n)}
|
|
107
|
+
extend @adapter_classes[name]
|
|
108
|
+
@writer_module.send(:include, @adapter_classes[name]::Convert) if @adapter_classes[name].const_defined?(:Convert)
|
|
109
|
+
end
|
|
110
|
+
converter_methods = @reader_module.methods - Module.methods
|
|
111
|
+
converter_methods.each do |m|
|
|
112
|
+
method_name = :"from_#{name}_#{m}"
|
|
113
|
+
unless singleton_methods.include?(method_name)
|
|
114
|
+
define_singleton_method method_name do |*args|
|
|
115
|
+
out = @reader_module.send(m, *args)
|
|
116
|
+
send(:"from_#{name}", out)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
@build = true
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
class DefaultGenerator < Generator
|
|
126
|
+
# Resets the generator to a blank state and installs the json, yaml, ruby and native
|
|
127
|
+
# adpaters
|
|
128
|
+
def reset!
|
|
129
|
+
super
|
|
130
|
+
register(:json, 'DataBindings::Adapters::JSON')
|
|
131
|
+
register(:yaml, 'DataBindings::Adapters::YAML')
|
|
132
|
+
register(:ruby, 'DataBindings::Adapters::Ruby')
|
|
133
|
+
register(:native, 'DataBindings::Adapters::Native')
|
|
134
|
+
register(:bson, 'DataBindings::Adapters::BSON')
|
|
135
|
+
register(:params, 'DataBindings::Adapters::Params')
|
|
136
|
+
register(:xml, 'DataBindings::Adapters::XML')
|
|
137
|
+
register(:tnetstring, 'DataBindings::Adapters::TNetstring')
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module DataBindings
|
|
2
|
+
# Module that handles unbound objects
|
|
3
|
+
module Unbound
|
|
4
|
+
|
|
5
|
+
attr_reader :binding, :binding_name
|
|
6
|
+
|
|
7
|
+
def bind!(name = nil, &blk)
|
|
8
|
+
bind(name, &blk).valid!
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def convert_target
|
|
12
|
+
bind { copy_source }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def bind(name = nil, opts = nil, &blk)
|
|
16
|
+
if name.is_a?(Unbound)
|
|
17
|
+
update_binding(name.binding_name, &name.binding)
|
|
18
|
+
else
|
|
19
|
+
name, opts = nil, name if name.is_a?(Hash) && opts.nil?
|
|
20
|
+
raise if name.nil? && blk.nil?
|
|
21
|
+
update_binding(name, &blk)
|
|
22
|
+
end
|
|
23
|
+
binding_class.new(@generator, @array, self, name, opts, &@binding)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def bind_array(type = nil, opts = nil, &blk)
|
|
27
|
+
type, opts = nil, type if type.is_a?(Hash) && opts.nil?
|
|
28
|
+
update_binding([], &blk)
|
|
29
|
+
binding_class.new(@generator, @array, self, nil, opts, &blk)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def type
|
|
33
|
+
if self.is_a?(Array)
|
|
34
|
+
:array
|
|
35
|
+
elsif self.is_a?(Hash)
|
|
36
|
+
:hash
|
|
37
|
+
else
|
|
38
|
+
raise
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def hash?
|
|
43
|
+
type == :hash
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def array?
|
|
47
|
+
type == :array
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def to_native
|
|
51
|
+
array? ?
|
|
52
|
+
map{ |m| m.respond_to?(:to_native) ? m.to_native : m } :
|
|
53
|
+
OpenStruct.new(inject({}) {|h, (k, v)| v = @generator.from_ruby(v); h[k] = (v.respond_to?(:to_native) ? v.to_native : v); h})
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def update_binding(name, &blk)
|
|
57
|
+
if name.is_a?(Array)
|
|
58
|
+
n = name.at(0)
|
|
59
|
+
@array = true
|
|
60
|
+
blk = proc { all_elements n }
|
|
61
|
+
name = nil
|
|
62
|
+
else
|
|
63
|
+
@array = false
|
|
64
|
+
end
|
|
65
|
+
@binding = @generator.get_type(name) || blk || raise(UnknownBindingError, "Unknown binding #{name.inspect}")
|
|
66
|
+
@name = name
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def binding_class
|
|
70
|
+
case type
|
|
71
|
+
when :array then @generator.binding_class(Bound::BoundArray)
|
|
72
|
+
when :hash then @generator.binding_class(Bound::BoundObject)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
module DataBindings
|
|
2
|
+
class FailedValidation < RuntimeError
|
|
3
|
+
attr_reader :errors, :original
|
|
4
|
+
def initialize(message, errors, original)
|
|
5
|
+
@errors, @original = errors, original
|
|
6
|
+
super message
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
UnboundError = Class.new(RuntimeError)
|
|
11
|
+
UnknownAdapterError = Class.new(RuntimeError)
|
|
12
|
+
UnknownBindingError = Class.new(RuntimeError)
|
|
13
|
+
BindingMismatch = Class.new(RuntimeError)
|
|
14
|
+
|
|
15
|
+
class IndifferentHash < Hash
|
|
16
|
+
include Hashie::Extensions::IndifferentAccess
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module ConverterHelper
|
|
20
|
+
def self.included(m)
|
|
21
|
+
m.class_eval <<-EOT, __FILE__, __LINE__ +1
|
|
22
|
+
def self.standard_converter(m)
|
|
23
|
+
define_method(m) do
|
|
24
|
+
pre_convert if respond_to?(:pre_convert)
|
|
25
|
+
send(:"force_\#{m}")
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
EOT
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
module WritingInterceptor
|
|
33
|
+
def method_missing(m, *args, &blk)
|
|
34
|
+
if match = m.to_s.match(/^((?:force_)?convert_to_(?:[^_]+))_(.*)/)
|
|
35
|
+
self.class.class_eval <<-EOT, __FILE__, __LINE__ + 1
|
|
36
|
+
def #{m}(*args, &blk)
|
|
37
|
+
@generator.write(#{match[2].inspect}, send(#{match[1].inspect}, *args, &blk), *args, &blk)
|
|
38
|
+
end
|
|
39
|
+
EOT
|
|
40
|
+
send(m, *args, &blk)
|
|
41
|
+
else
|
|
42
|
+
super
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
module GemRequirement
|
|
48
|
+
def self.included(o)
|
|
49
|
+
o.extend ClassMethods
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
module ClassMethods
|
|
53
|
+
def gentle_require_gem(method, gem)
|
|
54
|
+
class_eval <<-EOT, __FILE__, __LINE__ + 1
|
|
55
|
+
alias_method :#{method}_without_gem, :#{method}
|
|
56
|
+
def #{method}(*args, &blk)
|
|
57
|
+
DataBindings::GemRequirement.gentle_require_gem #{gem.to_s.inspect}
|
|
58
|
+
class << self
|
|
59
|
+
self
|
|
60
|
+
end.instance_eval do
|
|
61
|
+
alias_method :#{method}, :#{method}_without_gem
|
|
62
|
+
end
|
|
63
|
+
#{method}(*args, &blk)
|
|
64
|
+
end
|
|
65
|
+
EOT
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.gentle_require_gem(gem)
|
|
70
|
+
begin
|
|
71
|
+
require gem
|
|
72
|
+
rescue LoadError
|
|
73
|
+
warn "The `#{gem}' gem must be loadable"
|
|
74
|
+
exit 1
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require 'hashie'
|
|
2
|
+
|
|
3
|
+
require 'data_bindings/util'
|
|
4
|
+
require 'data_bindings/generator'
|
|
5
|
+
require 'data_bindings/version'
|
|
6
|
+
require 'data_bindings/converters'
|
|
7
|
+
require 'data_bindings/bound'
|
|
8
|
+
require 'data_bindings/unbound'
|
|
9
|
+
require 'data_bindings/adapters'
|
|
10
|
+
|
|
11
|
+
# From https://github.com/marcandre/backports
|
|
12
|
+
module Kernel
|
|
13
|
+
# Standard in ruby 1.9. See official documentation[http://ruby-doc.org/core-1.9/classes/Object.html]
|
|
14
|
+
def define_singleton_method(*args, &block)
|
|
15
|
+
class << self
|
|
16
|
+
self
|
|
17
|
+
end.send(:define_method, *args, &block)
|
|
18
|
+
end unless method_defined? :define_singleton_method
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Top-level constant for DataBindings
|
|
22
|
+
module DataBindings
|
|
23
|
+
|
|
24
|
+
class << self
|
|
25
|
+
# Sends all methods calls to DefaultGenerator
|
|
26
|
+
def method_missing(m, *args, &blk)
|
|
27
|
+
DefaultGeneratorInstance.send(:build!)
|
|
28
|
+
DefaultGeneratorInstance.send(m, *args, &blk)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def type(name, &blk)
|
|
32
|
+
DefaultGeneratorInstance.type(name, &blk)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def true_boolean?(el)
|
|
36
|
+
el == true or el == 'true' or el == 1 or el == '1' or el == 'yes'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def primitive_value?(val)
|
|
40
|
+
case val
|
|
41
|
+
when Integer, Float, true, false, String, Symbol, nil
|
|
42
|
+
true
|
|
43
|
+
else
|
|
44
|
+
false
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Generator instance used by default when you make a call to DataBindings. This can act as a singleton, so, if you want your own
|
|
50
|
+
# generator, create an instance of it
|
|
51
|
+
DefaultGeneratorInstance = DefaultGenerator.new
|
|
52
|
+
end
|
data/test/array_test.rb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require File.expand_path("../test_helper", __FILE__)
|
|
2
|
+
|
|
3
|
+
describe "Data Bindings array" do
|
|
4
|
+
before do
|
|
5
|
+
DataBindings.reset!
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
describe "from bind" do
|
|
9
|
+
it "should validate a list of integers" do
|
|
10
|
+
a = DataBindings.from_json("[1,2,3]").bind([Integer])
|
|
11
|
+
assert a.valid?
|
|
12
|
+
assert a.errors.empty?
|
|
13
|
+
assert_equal [1, 2, 3], [a[0], a[1], a[2]]
|
|
14
|
+
a.unshift 'asd'
|
|
15
|
+
refute a.valid?
|
|
16
|
+
refute a.errors.empty?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "should bind a list of complex things" do
|
|
20
|
+
DataBindings.type(:person) { property :name, String }
|
|
21
|
+
a = DataBindings.from_json('[{"name":"a"},{"name":"b"},{"name":"c"}]').bind([:person])
|
|
22
|
+
assert a.valid?
|
|
23
|
+
assert a.errors.empty?
|
|
24
|
+
assert_equal 3, a.size
|
|
25
|
+
assert_equal 'c', a[2][:name]
|
|
26
|
+
a.unshift 'asd'
|
|
27
|
+
refute a.valid?
|
|
28
|
+
refute a.errors.empty?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "should validate a list" do
|
|
32
|
+
assert DataBindings.from_json("[1,2,3]").bind([]).valid?
|
|
33
|
+
refute DataBindings.from_json("[1,2,3]").bind([], :length => 2).valid?
|
|
34
|
+
assert_raises(DataBindings::BindingMismatch) { DataBindings.from_json("{}").bind([]) }
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe "from within a bind" do
|
|
39
|
+
it "should validate a list of integers" do
|
|
40
|
+
a = DataBindings.from_json('{"a":[1,2,3]}').bind { property :a, [Integer] }
|
|
41
|
+
assert a.valid?
|
|
42
|
+
assert a.errors.empty?
|
|
43
|
+
assert_equal [1, 2, 3], [a[:a][0], a[:a][1], a[:a][2]]
|
|
44
|
+
a[:a].unshift 'asd'
|
|
45
|
+
refute a.valid?
|
|
46
|
+
refute a.errors.empty?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "should validate a list" do
|
|
50
|
+
assert DataBindings.from_json('{"a":[1,2,3]}').bind { property :a, [Integer], :length => 3 }.valid?
|
|
51
|
+
refute DataBindings.from_json('{"a":[1,2,3,4]}').bind { property :a, [Integer], :length => 3 }.valid?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
end
|