opium 1.0.0.beta
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.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +24 -0
- data/.travis.yml +17 -0
- data/Gemfile +4 -0
- data/Guardfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +71 -0
- data/Rakefile +10 -0
- data/lib/generators/opium/config_generator.rb +15 -0
- data/lib/generators/opium/model_generator.rb +33 -0
- data/lib/generators/opium/templates/config.yml +27 -0
- data/lib/generators/opium/templates/model.rb +10 -0
- data/lib/opium/config.rb +44 -0
- data/lib/opium/extensions/array.rb +10 -0
- data/lib/opium/extensions/boolean.rb +13 -0
- data/lib/opium/extensions/date.rb +18 -0
- data/lib/opium/extensions/date_time.rb +18 -0
- data/lib/opium/extensions/false_class.rb +7 -0
- data/lib/opium/extensions/float.rb +13 -0
- data/lib/opium/extensions/geo_point.rb +37 -0
- data/lib/opium/extensions/hash.rb +43 -0
- data/lib/opium/extensions/integer.rb +13 -0
- data/lib/opium/extensions/numeric.rb +7 -0
- data/lib/opium/extensions/object.rb +15 -0
- data/lib/opium/extensions/pointer.rb +20 -0
- data/lib/opium/extensions/regexp.rb +12 -0
- data/lib/opium/extensions/string.rb +20 -0
- data/lib/opium/extensions/time.rb +19 -0
- data/lib/opium/extensions/true_class.rb +7 -0
- data/lib/opium/extensions.rb +16 -0
- data/lib/opium/model/attributable.rb +37 -0
- data/lib/opium/model/callbacks.rb +38 -0
- data/lib/opium/model/connectable.rb +155 -0
- data/lib/opium/model/criteria.rb +123 -0
- data/lib/opium/model/dirty.rb +35 -0
- data/lib/opium/model/field.rb +31 -0
- data/lib/opium/model/fieldable.rb +57 -0
- data/lib/opium/model/findable.rb +20 -0
- data/lib/opium/model/kaminari/queryable.rb +46 -0
- data/lib/opium/model/kaminari/scopable.rb +15 -0
- data/lib/opium/model/kaminari.rb +4 -0
- data/lib/opium/model/persistable.rb +153 -0
- data/lib/opium/model/queryable.rb +150 -0
- data/lib/opium/model/scopable.rb +58 -0
- data/lib/opium/model/serialization.rb +13 -0
- data/lib/opium/model.rb +47 -0
- data/lib/opium/railtie.rb +14 -0
- data/lib/opium/user.rb +44 -0
- data/lib/opium/version.rb +3 -0
- data/lib/opium.rb +9 -0
- data/opium.gemspec +40 -0
- data/spec/opium/config/opium.yml +5 -0
- data/spec/opium/config_spec.rb +56 -0
- data/spec/opium/extensions/array_spec.rb +34 -0
- data/spec/opium/extensions/boolean_spec.rb +28 -0
- data/spec/opium/extensions/date_spec.rb +55 -0
- data/spec/opium/extensions/date_time_spec.rb +55 -0
- data/spec/opium/extensions/float_spec.rb +42 -0
- data/spec/opium/extensions/geo_point_spec.rb +55 -0
- data/spec/opium/extensions/hash_spec.rb +76 -0
- data/spec/opium/extensions/integer_spec.rb +42 -0
- data/spec/opium/extensions/object_spec.rb +24 -0
- data/spec/opium/extensions/pointer_spec.rb +28 -0
- data/spec/opium/extensions/regexp_spec.rb +23 -0
- data/spec/opium/extensions/string_spec.rb +65 -0
- data/spec/opium/extensions/time_spec.rb +55 -0
- data/spec/opium/model/attributable_spec.rb +45 -0
- data/spec/opium/model/callbacks_spec.rb +59 -0
- data/spec/opium/model/connectable_spec.rb +218 -0
- data/spec/opium/model/criteria_spec.rb +285 -0
- data/spec/opium/model/dirty_spec.rb +39 -0
- data/spec/opium/model/fieldable_spec.rb +133 -0
- data/spec/opium/model/findable_spec.rb +57 -0
- data/spec/opium/model/kaminari/queryable_spec.rb +22 -0
- data/spec/opium/model/kaminari/scopable_spec.rb +20 -0
- data/spec/opium/model/kaminari_spec.rb +104 -0
- data/spec/opium/model/persistable_spec.rb +367 -0
- data/spec/opium/model/queryable_spec.rb +338 -0
- data/spec/opium/model/scopable_spec.rb +115 -0
- data/spec/opium/model/serialization_spec.rb +51 -0
- data/spec/opium/model_spec.rb +49 -0
- data/spec/opium/user_spec.rb +195 -0
- data/spec/opium_spec.rb +5 -0
- data/spec/spec_helper.rb +25 -0
- metadata +400 -0
@@ -0,0 +1,150 @@
|
|
1
|
+
module Opium
|
2
|
+
module Model
|
3
|
+
module Queryable
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
delegate :count, :total_count, to: :criteria
|
8
|
+
|
9
|
+
def all( constraints )
|
10
|
+
imbued_where( arrayize( constraints ), '$all' )
|
11
|
+
end
|
12
|
+
|
13
|
+
alias_method :all_in, :all
|
14
|
+
|
15
|
+
def between( constraints )
|
16
|
+
start = constraints.map {|key, range| [key, range.begin]}
|
17
|
+
inclusive = constraints.reject {|_, range| range.exclude_end?}.map {|key, range| [key, range.end]}
|
18
|
+
exclusive = constraints.select {|_, range| range.exclude_end?}.map {|key, range| [key, range.end]}
|
19
|
+
gte( start ).lte( inclusive ).lt( exclusive )
|
20
|
+
end
|
21
|
+
|
22
|
+
def exists( constraints )
|
23
|
+
imbued_where( constraints.map {|key, value| [key, value.to_bool] }, '$exists' )
|
24
|
+
end
|
25
|
+
|
26
|
+
def gt( constraints )
|
27
|
+
imbued_where( constraints, '$gt' )
|
28
|
+
end
|
29
|
+
|
30
|
+
def gte( constraints )
|
31
|
+
imbued_where( constraints, '$gte' )
|
32
|
+
end
|
33
|
+
|
34
|
+
def lt( constraints )
|
35
|
+
imbued_where( constraints, '$lt' )
|
36
|
+
end
|
37
|
+
|
38
|
+
def lte( constraints )
|
39
|
+
imbued_where( constraints, '$lte' )
|
40
|
+
end
|
41
|
+
|
42
|
+
def in( constraints )
|
43
|
+
imbued_where( arrayize( constraints ), '$in' )
|
44
|
+
end
|
45
|
+
|
46
|
+
alias_method :any_in, :in
|
47
|
+
|
48
|
+
def nin( constraints )
|
49
|
+
imbued_where( arrayize( constraints ), '$nin' )
|
50
|
+
end
|
51
|
+
|
52
|
+
def ne( constraints )
|
53
|
+
imbued_where( constraints, '$ne' )
|
54
|
+
end
|
55
|
+
|
56
|
+
def or( *subqueries )
|
57
|
+
where( '$or' => subqueries )
|
58
|
+
end
|
59
|
+
|
60
|
+
def select( constraints )
|
61
|
+
imbued_where( constraints, '$select' )
|
62
|
+
end
|
63
|
+
|
64
|
+
def dont_select( constraints )
|
65
|
+
imbued_where( constraints, '$dontSelect' )
|
66
|
+
end
|
67
|
+
|
68
|
+
def keys( *field_names )
|
69
|
+
validate_fields_exist( field_names )
|
70
|
+
criteria.update_constraint( :keys, field_names.map(&method(:translate_name)).join(',') )
|
71
|
+
end
|
72
|
+
|
73
|
+
# Should be noted that pluck is an immediate query execution, so doesn't play well with further chainable criteria
|
74
|
+
def pluck( field_name )
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
def order( options )
|
79
|
+
validate_fields_exist( options )
|
80
|
+
previous = criteria.constraints[:order]
|
81
|
+
ordering = (
|
82
|
+
[previous].compact + options.map {|key, value| (['-', 'desc', '-1'].include?( value.to_s ) ? '-' : '' ) + translate_name( key.to_s ) }
|
83
|
+
).join(',')
|
84
|
+
criteria.update_constraint( :order, ordering )
|
85
|
+
end
|
86
|
+
|
87
|
+
def limit( value )
|
88
|
+
criteria.update_constraint( :limit, value )
|
89
|
+
end
|
90
|
+
|
91
|
+
def skip( value )
|
92
|
+
criteria.update_constraint( :skip, value )
|
93
|
+
end
|
94
|
+
|
95
|
+
def where( constraints )
|
96
|
+
validate_fields_exist( constraints )
|
97
|
+
criteria.update_constraint( :where, translate_to_parse( constraints ) )
|
98
|
+
end
|
99
|
+
|
100
|
+
alias_method :and, :where
|
101
|
+
|
102
|
+
def cache
|
103
|
+
criteria.update_variable( :cache, true )
|
104
|
+
end
|
105
|
+
|
106
|
+
def uncache
|
107
|
+
criteria.update_variable( :cache, false )
|
108
|
+
end
|
109
|
+
|
110
|
+
def cached?
|
111
|
+
criteria.variables[:cache]
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def model
|
117
|
+
self
|
118
|
+
end
|
119
|
+
|
120
|
+
def validate_fields_exist( field_names )
|
121
|
+
field_names = field_names.keys if field_names.respond_to? :keys
|
122
|
+
unless field_names.all? {|field_name| model.fields.key?( field_name ) || field_name =~ /^\$/ }
|
123
|
+
not_fields = field_names.reject {|field_name| model.fields.key? field_name }
|
124
|
+
raise ArgumentError, "#{not_fields.join(', ')} #{not_fields.length > 1 ? 'are not fields' : 'is not a field'} on this model"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def translate_name( field_name )
|
129
|
+
field_name =~ /^\$/ ? field_name : model.parse_canonical_field_names[ field_name ]
|
130
|
+
end
|
131
|
+
|
132
|
+
def translate_to_parse( constraints )
|
133
|
+
Hash[ *constraints.flat_map {|key, value| [translate_name( key ), value.to_parse]} ]
|
134
|
+
end
|
135
|
+
|
136
|
+
def arrayize( constraints )
|
137
|
+
constraints.map {|key, value| [key, value.to_a]}
|
138
|
+
end
|
139
|
+
|
140
|
+
def imbued_where( constraints, operator )
|
141
|
+
where( imbue_field_constraints_with_operator( constraints, operator ) )
|
142
|
+
end
|
143
|
+
|
144
|
+
def imbue_field_constraints_with_operator( constraints, operator )
|
145
|
+
Hash[ *constraints.flat_map {|key, value| [key, { operator => value }]} ]
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Opium
|
2
|
+
module Model
|
3
|
+
module Scopable
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def criteria
|
11
|
+
@_unscoped ? blank_criteria : default_scope
|
12
|
+
end
|
13
|
+
|
14
|
+
def scope( scope_name, criteria = nil, &block )
|
15
|
+
class_eval do
|
16
|
+
method_body = if block_given? || criteria.is_a?(Proc)
|
17
|
+
block || criteria
|
18
|
+
elsif criteria.nil?
|
19
|
+
raise ArgumentError, "Criteria cannot be nil if no block is provided."
|
20
|
+
else
|
21
|
+
-> { criteria }
|
22
|
+
end
|
23
|
+
define_singleton_method( scope_name, method_body )
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def default_scope( criteria = nil, &block )
|
28
|
+
@default_scope = block || criteria if block_given? || criteria
|
29
|
+
s = @default_scope || blank_criteria
|
30
|
+
s.is_a?( Proc ) ? unscoped { s.call } : s
|
31
|
+
end
|
32
|
+
|
33
|
+
def scoped
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
def unscoped
|
38
|
+
if block_given?
|
39
|
+
@_unscoped = true
|
40
|
+
yield
|
41
|
+
else
|
42
|
+
blank_criteria
|
43
|
+
end
|
44
|
+
ensure
|
45
|
+
@_unscoped = false
|
46
|
+
end
|
47
|
+
|
48
|
+
def blank_criteria
|
49
|
+
Criteria.new( self.model_name )
|
50
|
+
end
|
51
|
+
|
52
|
+
def with_scope( criteria )
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/opium/model.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'active_support/core_ext/string'
|
3
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
4
|
+
require 'active_support/core_ext/hash/deep_merge'
|
5
|
+
require 'active_support/inflector'
|
6
|
+
require 'opium/model/connectable'
|
7
|
+
require 'opium/model/persistable'
|
8
|
+
require 'opium/model/callbacks'
|
9
|
+
require 'opium/model/serialization'
|
10
|
+
require 'opium/model/dirty'
|
11
|
+
require 'opium/model/fieldable'
|
12
|
+
require 'opium/model/attributable'
|
13
|
+
require 'opium/model/queryable'
|
14
|
+
require 'opium/model/criteria'
|
15
|
+
require 'opium/model/scopable'
|
16
|
+
require 'opium/model/findable'
|
17
|
+
require 'opium/model/kaminari'
|
18
|
+
|
19
|
+
module Opium
|
20
|
+
module Model
|
21
|
+
extend ActiveSupport::Concern
|
22
|
+
|
23
|
+
include ActiveModel::Model
|
24
|
+
|
25
|
+
included do
|
26
|
+
include Connectable
|
27
|
+
include Persistable
|
28
|
+
include Dirty
|
29
|
+
include Fieldable
|
30
|
+
include Serialization
|
31
|
+
include Attributable
|
32
|
+
include Queryable
|
33
|
+
include Callbacks
|
34
|
+
include Scopable
|
35
|
+
include Findable
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize( attributes = {} )
|
39
|
+
self.attributes = attributes
|
40
|
+
end
|
41
|
+
|
42
|
+
def inspect
|
43
|
+
inspected_fields = self.attributes.map {|k, v| [k, v.inspect].join(': ')}.join(', ')
|
44
|
+
"#<#{self.class.model_name} #{inspected_fields}>"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Opium
|
2
|
+
class Railtie < Rails::Railtie
|
3
|
+
config.app_generators.orm :opium, migration: false
|
4
|
+
|
5
|
+
config.opium = ::Opium.config
|
6
|
+
|
7
|
+
initializer 'opium.load-config' do
|
8
|
+
config_file = Rails.root.join( 'config', 'opium.yml' )
|
9
|
+
if config_file.file?
|
10
|
+
::Opium.load!( config_file )
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/opium/user.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module Opium
|
2
|
+
class User
|
3
|
+
include Opium::Model
|
4
|
+
|
5
|
+
field :username, type: String
|
6
|
+
field :password, type: String
|
7
|
+
field :email, type: String
|
8
|
+
field :email_verified, type: Boolean
|
9
|
+
field :session_token, type: String, readonly: true
|
10
|
+
|
11
|
+
no_object_prefix!
|
12
|
+
requires_heightened_privileges!
|
13
|
+
add_header_to [:put, :delete], :x_parse_session_token, :session_token.to_proc
|
14
|
+
|
15
|
+
class << self
|
16
|
+
# Note that this will eat any ParseErrors which get raised, and not perform any logging.
|
17
|
+
def authenticate( username, password )
|
18
|
+
authenticate!( username, password )
|
19
|
+
rescue Opium::Model::Connectable::ParseError => e
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def authenticate!( username, password )
|
24
|
+
new( as_resource('login') { http_get query: { username: username, password: password } } )
|
25
|
+
end
|
26
|
+
|
27
|
+
def find_by_session_token( token )
|
28
|
+
new( http_get id: 'me', headers: { x_parse_session_token: token } )
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def reset_password
|
33
|
+
reset_password!
|
34
|
+
rescue => e
|
35
|
+
self.errors.add( :email, e.to_s )
|
36
|
+
false
|
37
|
+
end
|
38
|
+
|
39
|
+
def reset_password!
|
40
|
+
fail KeyError, 'an email address is required to reset password' unless email
|
41
|
+
self.class.as_resource('requestPasswordReset') { self.class.http_post data: email }.empty?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/opium.rb
ADDED
data/opium.gemspec
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'opium/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "opium"
|
8
|
+
spec.version = Opium::VERSION
|
9
|
+
spec.authors = ["Joshua Bowers"]
|
10
|
+
spec.email = ["joshua.bowers+code@gmail.com"]
|
11
|
+
spec.summary = %q{An Object Parse.com Mapping technology for defining models.}
|
12
|
+
spec.description = %q{Provides an intuitive, Mongoid-inspired mapping layer between your application's object space and Parse.'}
|
13
|
+
spec.homepage = "https://github.com/joshuabowers/opium"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
spec.add_development_dependency "rspec-nc"
|
25
|
+
spec.add_development_dependency "rspec-its"
|
26
|
+
spec.add_development_dependency "webmock"
|
27
|
+
spec.add_development_dependency "guard"
|
28
|
+
spec.add_development_dependency "guard-rspec"
|
29
|
+
spec.add_development_dependency "pry"
|
30
|
+
spec.add_development_dependency "pry-remote"
|
31
|
+
spec.add_development_dependency "pry-nav"
|
32
|
+
spec.add_development_dependency "coveralls"
|
33
|
+
|
34
|
+
spec.add_development_dependency "rails"
|
35
|
+
spec.add_development_dependency "kaminari"
|
36
|
+
|
37
|
+
spec.add_dependency "activemodel", "~> 4.0"
|
38
|
+
spec.add_dependency "faraday", "~> 0.9"
|
39
|
+
spec.add_dependency "faraday_middleware", "~> 0.9"
|
40
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Opium do
|
4
|
+
it { should respond_to(:configure, :config) }
|
5
|
+
it { should respond_to(:load!).with(2).arguments }
|
6
|
+
it { should respond_to(:reset) }
|
7
|
+
|
8
|
+
describe ':configure' do
|
9
|
+
it do
|
10
|
+
expect {|b| Opium.configure(&b) }.to yield_with_args
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe ':config' do
|
15
|
+
subject { Opium.config }
|
16
|
+
|
17
|
+
it { should_not be_nil }
|
18
|
+
it { should be_an( Opium::Config ) }
|
19
|
+
end
|
20
|
+
|
21
|
+
describe ':reset' do
|
22
|
+
it 'should put all changed settings back to their defaults' do
|
23
|
+
expect { described_class.config.log_network_responses = true }.to change( described_class.config, :log_network_responses ).from( false ).to( true )
|
24
|
+
described_class.reset
|
25
|
+
described_class.config.log_network_responses.should == false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe ':load!' do
|
30
|
+
let(:file) { File.join( File.dirname( __FILE__ ), 'config', 'opium.yml' ) }
|
31
|
+
|
32
|
+
before do
|
33
|
+
described_class.load!( file, :test )
|
34
|
+
end
|
35
|
+
|
36
|
+
after do
|
37
|
+
described_class.reset
|
38
|
+
end
|
39
|
+
|
40
|
+
it { subject.config.app_id.should == 'abcd1234' }
|
41
|
+
it { subject.config.api_key.should == 'efgh5678' }
|
42
|
+
it { subject.config.master_key.should == '9012ijkl' }
|
43
|
+
it { subject.config.log_network_responses.should == true }
|
44
|
+
end
|
45
|
+
|
46
|
+
describe Opium::Config do
|
47
|
+
it { should respond_to( :app_id, :api_key, :master_key, :log_network_responses ) }
|
48
|
+
|
49
|
+
describe 'defaults' do
|
50
|
+
its(:app_id) { should == 'PARSE_APP_ID' }
|
51
|
+
its(:api_key) { should == 'PARSE_API_KEY' }
|
52
|
+
its(:master_key) { should == 'PARSE_MASTER_KEY' }
|
53
|
+
its(:log_network_responses) { should == false }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Array do
|
4
|
+
subject { Array }
|
5
|
+
|
6
|
+
describe 'instance' do
|
7
|
+
describe ':to_parse' do
|
8
|
+
subject { [GeoPoint.new( latitude: 33.33, longitude: -117.117 )] }
|
9
|
+
|
10
|
+
it 'should call :to_parse on each element' do
|
11
|
+
subject.to_parse.tap do |result|
|
12
|
+
result.should =~ [ { '__type' => 'GeoPoint', 'latitude' => 33.33, 'longitude' => -117.117 } ]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe ':to_geo_point' do
|
18
|
+
describe 'with two values' do
|
19
|
+
subject { [33.33, -117.117] }
|
20
|
+
|
21
|
+
it { subject.to_geo_point.should be_a_kind_of(GeoPoint) }
|
22
|
+
it 'should have the expected latitude and longitude' do
|
23
|
+
subject.to_geo_point.latitude.should == 33.33
|
24
|
+
subject.to_geo_point.longitude.should == -117.117
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe 'with more or fewer than two values' do
|
29
|
+
it { expect { [33.33].to_geo_point }.to raise_exception }
|
30
|
+
it { expect { [33.33, -117.117, :deadbeef].to_geo_point }.to raise_exception }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Boolean do
|
4
|
+
subject { Boolean }
|
5
|
+
it { should respond_to(:to_ruby).with(1).argument }
|
6
|
+
it { should respond_to(:to_parse).with(1).argument }
|
7
|
+
|
8
|
+
it ":to_ruby(object) should convert object to a boolean" do
|
9
|
+
["true", "false", true, false, 1.0, 1, 0.0, 0].each do |value|
|
10
|
+
subject.to_ruby(value).should be_a_kind_of(Boolean)
|
11
|
+
subject.to_ruby(value).should == value.to_bool
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it ":to_parse(object) should convert object to a boolean" do
|
16
|
+
["true", "false", true, false, 1.0, 1, 0.0, 0].each do |value|
|
17
|
+
subject.to_parse(value).should be_a_kind_of(Boolean)
|
18
|
+
subject.to_parse(value).should == value.to_bool
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "instance" do
|
23
|
+
subject { Class.new { include Boolean }.new }
|
24
|
+
it { should respond_to(:to_ruby, :to_parse).with(0).arguments }
|
25
|
+
its(:to_ruby) { should == subject }
|
26
|
+
its(:to_parse) { should == subject }
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Date do
|
4
|
+
subject { Date }
|
5
|
+
it { should respond_to(:to_parse, :to_ruby).with(1).argument }
|
6
|
+
|
7
|
+
describe ":to_ruby" do
|
8
|
+
describe "with a Hash containing '__type: Date'" do
|
9
|
+
let(:object) { { '__type' => 'Date', 'iso' => Date.today.iso8601 } }
|
10
|
+
it { subject.to_ruby(object).should be_a_kind_of( Date ) }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "with a Hash without a '__type' key" do
|
14
|
+
let(:object) { { 'iso' => Date.today.iso8601 } }
|
15
|
+
it { expect { subject.to_ruby(object) }.to raise_exception }
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "with an object which responds to :to_date" do
|
19
|
+
let(:objects) { [ Date.today.iso8601, DateTime.now, Time.now, Date.today ] }
|
20
|
+
it do
|
21
|
+
objects.each do |object|
|
22
|
+
subject.to_ruby(object).should be_a_kind_of( Date )
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe ":to_parse(object)" do
|
29
|
+
let(:objects) { [ Date.today.iso8601, DateTime.now, Time.now, Date.today ] }
|
30
|
+
it "should ensure that the object is a datetime" do
|
31
|
+
objects.each do |object|
|
32
|
+
object.should_receive :to_date
|
33
|
+
subject.to_parse(object)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should make a parse object hash" do
|
38
|
+
objects.each do |object|
|
39
|
+
result = subject.to_parse(object)
|
40
|
+
result.should be_a_kind_of(Hash)
|
41
|
+
result.keys.should == ['__type', 'iso']
|
42
|
+
result['__type'].should == 'Date'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "instance" do
|
48
|
+
subject { Date.today }
|
49
|
+
let(:result) { subject.to_parse }
|
50
|
+
it { result.should be_a_kind_of(Hash) }
|
51
|
+
it { result.keys.should == ['__type', 'iso'] }
|
52
|
+
it { result['__type'].should == 'Date' }
|
53
|
+
it { result['iso'].should == subject.iso8601 }
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DateTime do
|
4
|
+
subject { DateTime }
|
5
|
+
it { should respond_to(:to_parse, :to_ruby).with(1).argument }
|
6
|
+
|
7
|
+
describe ":to_ruby" do
|
8
|
+
describe "with a Hash containing '__type: DateTime'" do
|
9
|
+
let(:object) { { '__type' => 'Date', 'iso' => DateTime.now.iso8601 } }
|
10
|
+
it { subject.to_ruby(object).should be_a_kind_of( DateTime ) }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "with a Hash without a '__type' key" do
|
14
|
+
let(:object) { { 'iso' => DateTime.now.iso8601 } }
|
15
|
+
it { expect { subject.to_ruby(object) }.to raise_exception }
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "with an object which responds to :to_datetime" do
|
19
|
+
let(:objects) { [ DateTime.now.iso8601, DateTime.now, Time.now, Date.today ] }
|
20
|
+
it do
|
21
|
+
objects.each do |object|
|
22
|
+
subject.to_ruby(object).should be_a_kind_of( DateTime )
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe ":to_parse(object)" do
|
29
|
+
let(:objects) { [ DateTime.now.iso8601, DateTime.now, Time.now, Date.today ] }
|
30
|
+
it "should ensure that the object is a datetime" do
|
31
|
+
objects.each do |object|
|
32
|
+
object.should_receive :to_datetime
|
33
|
+
subject.to_parse(object)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should make a parse object hash" do
|
38
|
+
objects.each do |object|
|
39
|
+
result = subject.to_parse(object)
|
40
|
+
result.should be_a_kind_of(Hash)
|
41
|
+
result.keys.should == ['__type', 'iso']
|
42
|
+
result['__type'].should == 'Date'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "instance" do
|
48
|
+
subject { DateTime.now }
|
49
|
+
let(:result) { subject.to_parse }
|
50
|
+
it { result.should be_a_kind_of(Hash) }
|
51
|
+
it { result.keys.should == ['__type', 'iso'] }
|
52
|
+
it { result['__type'].should == 'Date' }
|
53
|
+
it { result['iso'].should == subject.iso8601 }
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ::Float do
|
4
|
+
subject { Float }
|
5
|
+
it { should respond_to(:to_ruby).with(1).argument }
|
6
|
+
it { should respond_to(:to_parse).with(1).argument }
|
7
|
+
|
8
|
+
it ":to_ruby(object) should convert object to a float" do
|
9
|
+
{nil => nil, :foo => :foo.to_s.to_f}.each do |value, expected|
|
10
|
+
subject.to_ruby(value).should be_a_kind_of(expected.class)
|
11
|
+
subject.to_ruby(value).should == expected
|
12
|
+
end
|
13
|
+
["foo", 42.0, 42, Time.now].each do |value|
|
14
|
+
subject.to_ruby(value).should be_a_kind_of(Float)
|
15
|
+
subject.to_ruby(value).should == value.to_f
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it ":to_parse(object) should convert object to a float" do
|
20
|
+
{nil => nil, :foo => :foo.to_s.to_f}.each do |value, expected|
|
21
|
+
subject.to_parse(value).should be_a_kind_of(expected.class)
|
22
|
+
subject.to_parse(value).should == expected
|
23
|
+
end
|
24
|
+
["foo", 42.0, 42, Time.now].each do |value|
|
25
|
+
subject.to_parse(value).should be_a_kind_of(Float)
|
26
|
+
subject.to_parse(value).should == value.to_f
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "instance" do
|
31
|
+
subject { 42.0 }
|
32
|
+
it { should respond_to(:to_ruby, :to_parse, :to_bool).with(0).arguments }
|
33
|
+
its(:to_ruby) { should == subject }
|
34
|
+
its(:to_parse) { should == subject }
|
35
|
+
|
36
|
+
it "should convert 1.0 and 0.0 to true and false, all others raise" do
|
37
|
+
1.0.to_bool.should == true
|
38
|
+
0.0.to_bool.should == false
|
39
|
+
expect { subject.to_bool }.to raise_exception
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|