id 0.0.8 → 0.0.9
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/Gemfile +2 -0
- data/Gemfile.lock +12 -3
- data/id.gemspec +2 -1
- data/lib/id.rb +1 -0
- data/lib/id/model.rb +20 -28
- data/lib/id/model/all.rb +2 -0
- data/lib/id/model/descriptor.rb +52 -0
- data/lib/id/model/field.rb +13 -4
- data/lib/id/model/form.rb +37 -0
- data/spec/lib/id/model/field_spec.rb +0 -0
- data/spec/lib/id/model/form_spec.rb +55 -0
- data/spec/lib/id/model_spec.rb +36 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/active_model_lint.rb +14 -0
- metadata +25 -1
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
id (0.0.
|
4
|
+
id (0.0.9)
|
5
|
+
activemodel
|
5
6
|
activesupport
|
6
7
|
money
|
7
8
|
optional
|
@@ -9,15 +10,20 @@ PATH
|
|
9
10
|
GEM
|
10
11
|
remote: https://rubygems.org/
|
11
12
|
specs:
|
13
|
+
activemodel (3.2.12)
|
14
|
+
activesupport (= 3.2.12)
|
15
|
+
builder (~> 3.0.0)
|
12
16
|
activesupport (3.2.12)
|
13
17
|
i18n (~> 0.6)
|
14
18
|
multi_json (~> 1.0)
|
19
|
+
builder (3.0.4)
|
15
20
|
diff-lcs (1.2.1)
|
16
21
|
i18n (0.6.4)
|
22
|
+
json (1.7.7)
|
17
23
|
money (5.1.1)
|
18
24
|
i18n (~> 0.6.0)
|
19
|
-
multi_json (1.7.
|
20
|
-
optional (0.0.
|
25
|
+
multi_json (1.7.3)
|
26
|
+
optional (0.0.6)
|
21
27
|
rspec (2.13.0)
|
22
28
|
rspec-core (~> 2.13.0)
|
23
29
|
rspec-expectations (~> 2.13.0)
|
@@ -30,11 +36,14 @@ GEM
|
|
30
36
|
multi_json (~> 1.0)
|
31
37
|
simplecov-html (~> 0.7.1)
|
32
38
|
simplecov-html (0.7.1)
|
39
|
+
test-unit (2.5.5)
|
33
40
|
|
34
41
|
PLATFORMS
|
35
42
|
ruby
|
36
43
|
|
37
44
|
DEPENDENCIES
|
38
45
|
id!
|
46
|
+
json (~> 1.7.7)
|
39
47
|
rspec
|
40
48
|
simplecov
|
49
|
+
test-unit
|
data/id.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'id'
|
3
|
-
s.version = '0.0.
|
3
|
+
s.version = '0.0.9'
|
4
4
|
s.date = '2013-03-28'
|
5
5
|
s.summary = "Simple models based on hashes"
|
6
6
|
s.description = "Developed at On The Beach Ltd. Contact russell.dunphy@onthebeach.co.uk"
|
@@ -14,6 +14,7 @@ Gem::Specification.new do |s|
|
|
14
14
|
s.add_dependency "optional"
|
15
15
|
s.add_dependency "money"
|
16
16
|
s.add_dependency "activesupport"
|
17
|
+
s.add_dependency "activemodel"
|
17
18
|
|
18
19
|
s.add_development_dependency "rspec"
|
19
20
|
s.add_development_dependency "simplecov"
|
data/lib/id.rb
CHANGED
data/lib/id/model.rb
CHANGED
@@ -14,43 +14,35 @@ module Id
|
|
14
14
|
self.class.new(data.except(*keys.map(&:to_s)))
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
def self.included(base)
|
20
|
-
base.extend(Descriptor)
|
17
|
+
def eql? other
|
18
|
+
other.is_a?(Id::Model) && other.data.eql?(self.data)
|
21
19
|
end
|
20
|
+
alias_method :==, :eql?
|
22
21
|
|
23
|
-
def
|
24
|
-
|
22
|
+
def hash
|
23
|
+
data.hash
|
25
24
|
end
|
26
25
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
(options[:optional] ? FieldOption : Field).new(self, f, options).define
|
31
|
-
end
|
32
|
-
|
33
|
-
def has_one(f, options={})
|
34
|
-
(options[:optional] ? HasOneOption : HasOne).new(self, f, options).define
|
35
|
-
end
|
26
|
+
def as_form
|
27
|
+
@form_object ||= self.class.form_object.new(self)
|
28
|
+
end
|
36
29
|
|
37
|
-
|
38
|
-
|
39
|
-
|
30
|
+
def errors
|
31
|
+
as_form.errors
|
32
|
+
end
|
40
33
|
|
41
|
-
|
42
|
-
|
43
|
-
|
34
|
+
def valid?
|
35
|
+
as_form.valid?
|
36
|
+
end
|
44
37
|
|
45
|
-
|
46
|
-
builder_class.new(self)
|
47
|
-
end
|
38
|
+
private
|
48
39
|
|
49
|
-
|
40
|
+
def self.included(base)
|
41
|
+
base.extend(Descriptor)
|
42
|
+
end
|
50
43
|
|
51
|
-
|
52
|
-
|
53
|
-
end
|
44
|
+
def memoize(f, &b)
|
45
|
+
instance_variable_get("@#{f}") || instance_variable_set("@#{f}", b.call)
|
54
46
|
end
|
55
47
|
|
56
48
|
end
|
data/lib/id/model/all.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require_relative 'descriptor'
|
1
2
|
require_relative 'type_casts'
|
2
3
|
require_relative 'field'
|
3
4
|
require_relative 'association'
|
@@ -5,3 +6,4 @@ require_relative 'has_one'
|
|
5
6
|
require_relative 'has_many'
|
6
7
|
require_relative 'builder'
|
7
8
|
require_relative 'compound_field'
|
9
|
+
require_relative 'form'
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Id
|
2
|
+
module Model
|
3
|
+
module Descriptor
|
4
|
+
|
5
|
+
def field(f, options={})
|
6
|
+
(options[:optional] ? FieldOption : Field).new(self, f, options).define
|
7
|
+
end
|
8
|
+
|
9
|
+
def has_one(f, options={})
|
10
|
+
(options[:optional] ? HasOneOption : HasOne).new(self, f, options).define
|
11
|
+
end
|
12
|
+
|
13
|
+
def has_many(f, options={})
|
14
|
+
HasMany.new(self, f, options).define
|
15
|
+
end
|
16
|
+
|
17
|
+
def compound_field(f, fields, options={})
|
18
|
+
CompoundField.new(self, f, fields, options).define
|
19
|
+
end
|
20
|
+
|
21
|
+
def builder
|
22
|
+
builder_class.new(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def form &block
|
26
|
+
form_object.send :instance_exec, &block
|
27
|
+
end
|
28
|
+
|
29
|
+
def form_object
|
30
|
+
base = self
|
31
|
+
@form_object ||= Class.new(Form) do
|
32
|
+
instance_exec do
|
33
|
+
define_singleton_method :model_name do
|
34
|
+
ActiveModel::Name.new(self, nil, base.name)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_proc
|
41
|
+
->(data) { new data }
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def builder_class
|
47
|
+
@builder_class ||= Class.new { include Builder }
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/id/model/field.rb
CHANGED
@@ -8,6 +8,16 @@ module Id
|
|
8
8
|
@options = options
|
9
9
|
end
|
10
10
|
|
11
|
+
def define_form_field
|
12
|
+
field = self
|
13
|
+
model.form_object.send :define_method, name do
|
14
|
+
memoize field.name do
|
15
|
+
Option[model.send(field.name)].flatten.value_or nil if model.data.has_key? field.key
|
16
|
+
end
|
17
|
+
end
|
18
|
+
model.form_object.send :attr_writer, name
|
19
|
+
end
|
20
|
+
|
11
21
|
def define_getter
|
12
22
|
field = self
|
13
23
|
model.send :define_method, name do
|
@@ -32,6 +42,7 @@ module Id
|
|
32
42
|
define_getter
|
33
43
|
define_setter
|
34
44
|
define_is_present
|
45
|
+
define_form_field
|
35
46
|
end
|
36
47
|
|
37
48
|
def cast(value)
|
@@ -69,10 +80,8 @@ module Id
|
|
69
80
|
field = self
|
70
81
|
model.send :define_method, name do
|
71
82
|
memoize field.name do
|
72
|
-
|
73
|
-
|
74
|
-
else
|
75
|
-
None
|
83
|
+
Option[data.fetch(field.key, &field.default_value)].map do |d|
|
84
|
+
field.cast d
|
76
85
|
end
|
77
86
|
end
|
78
87
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Id
|
2
|
+
module Model
|
3
|
+
class Form
|
4
|
+
include ActiveModel::Validations
|
5
|
+
include ActiveModel::Conversion
|
6
|
+
extend ActiveModel::Naming
|
7
|
+
|
8
|
+
def self.i18n_scope
|
9
|
+
:id
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(model)
|
13
|
+
@model = model
|
14
|
+
end
|
15
|
+
|
16
|
+
def persisted?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_model
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def method_missing(name, *args, &block)
|
27
|
+
model.send(name, *args, &block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def memoize(f, &b)
|
31
|
+
instance_variable_get("@#{f}") || instance_variable_set("@#{f}", b.call)
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_reader :model
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
File without changes
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Gerbil
|
4
|
+
include Id::Model
|
5
|
+
|
6
|
+
field :name
|
7
|
+
field :paws
|
8
|
+
|
9
|
+
form do
|
10
|
+
validates_presence_of :name
|
11
|
+
validates_length_of :name, maximum: 4
|
12
|
+
end
|
13
|
+
|
14
|
+
def name_in_caps
|
15
|
+
name.upcase
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
module Id
|
21
|
+
module Model
|
22
|
+
describe Form do
|
23
|
+
|
24
|
+
let (:gerbil) { Gerbil.new(name: 'Berty') }
|
25
|
+
let (:form) { gerbil.as_form }
|
26
|
+
|
27
|
+
subject { gerbil.as_form }
|
28
|
+
|
29
|
+
it_behaves_like "ActiveModel"
|
30
|
+
|
31
|
+
it 'is delegated to by the model for errors' do
|
32
|
+
gerbil.errors.should eq form.errors
|
33
|
+
gerbil.valid?.should eq form.valid?
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'responds to to_model' do
|
37
|
+
subject.to_model.should eq subject
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'has the same fields as the model' do
|
41
|
+
form.name.should eq 'Berty'
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'works with active model validations' do
|
45
|
+
form.should_not be_valid
|
46
|
+
form.name = 'Bert'
|
47
|
+
form.should be_valid
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'delegates to the model' do
|
51
|
+
form.name_in_caps.should eq 'BERTY'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/spec/lib/id/model_spec.rb
CHANGED
@@ -157,4 +157,40 @@ describe Id::Model do
|
|
157
157
|
end
|
158
158
|
end
|
159
159
|
|
160
|
+
describe "#==" do
|
161
|
+
it 'is equal to another id model with the same data' do
|
162
|
+
one = TestModel.new(foo: 1)
|
163
|
+
two = TestModel.new(foo: 1)
|
164
|
+
one.should eq two
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'is not equal to two models with different data' do
|
168
|
+
one = TestModel.new(foo: 1)
|
169
|
+
two = TestModel.new(foo: 2)
|
170
|
+
one.should_not eq two
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
describe "#hash" do
|
175
|
+
it 'allows id models to be used as hash keys' do
|
176
|
+
one = TestModel.new(foo: 1)
|
177
|
+
two = TestModel.new(foo: 1)
|
178
|
+
hash = { one => :found }
|
179
|
+
hash[two].should eq :found
|
180
|
+
end
|
181
|
+
it 'they are different keys if the data is different' do
|
182
|
+
one = TestModel.new(foo: 1)
|
183
|
+
two = TestModel.new(foo: 2)
|
184
|
+
hash = { one => :found }
|
185
|
+
hash[two].should be_nil
|
186
|
+
hash[one].should eq :found
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe ".to_proc" do
|
191
|
+
it 'allows eta expansion of the class name to its constructor' do
|
192
|
+
[{foo: 1}].map(&TestModel).first.should be_a TestModel
|
193
|
+
[{foo: 1}].map(&TestModel).first.foo.should eq 1
|
194
|
+
end
|
195
|
+
end
|
160
196
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
shared_examples_for "ActiveModel" do
|
2
|
+
require 'test/unit/assertions'
|
3
|
+
require 'active_model/lint'
|
4
|
+
include Test::Unit::Assertions
|
5
|
+
include ActiveModel::Lint::Tests
|
6
|
+
|
7
|
+
ActiveModel::Lint::Tests.public_instance_methods.map { |method| method.to_s }.grep(/^test/).each do |method|
|
8
|
+
example(method.gsub('_', ' ')) { send method }
|
9
|
+
end
|
10
|
+
|
11
|
+
def model
|
12
|
+
subject
|
13
|
+
end
|
14
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: id
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -60,6 +60,22 @@ dependencies:
|
|
60
60
|
- - ! '>='
|
61
61
|
- !ruby/object:Gem::Version
|
62
62
|
version: '0'
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: activemodel
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ! '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
type: :runtime
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
63
79
|
- !ruby/object:Gem::Dependency
|
64
80
|
name: rspec
|
65
81
|
requirement: !ruby/object:Gem::Requirement
|
@@ -115,19 +131,24 @@ files:
|
|
115
131
|
- lib/id/model/association.rb
|
116
132
|
- lib/id/model/builder.rb
|
117
133
|
- lib/id/model/compound_field.rb
|
134
|
+
- lib/id/model/descriptor.rb
|
118
135
|
- lib/id/model/field.rb
|
136
|
+
- lib/id/model/form.rb
|
119
137
|
- lib/id/model/has_many.rb
|
120
138
|
- lib/id/model/has_one.rb
|
121
139
|
- lib/id/model/type_casts.rb
|
122
140
|
- lib/id/timestamps.rb
|
123
141
|
- spec/lib/id/model/association_spec.rb
|
124
142
|
- spec/lib/id/model/builder_spec.rb
|
143
|
+
- spec/lib/id/model/field_spec.rb
|
144
|
+
- spec/lib/id/model/form_spec.rb
|
125
145
|
- spec/lib/id/model/has_one_spec.rb
|
126
146
|
- spec/lib/id/model/type_casts_spec.rb
|
127
147
|
- spec/lib/id/model_spec.rb
|
128
148
|
- spec/lib/id/timestamps_spec.rb
|
129
149
|
- spec/lib/mike_spec.rb
|
130
150
|
- spec/spec_helper.rb
|
151
|
+
- spec/support/active_model_lint.rb
|
131
152
|
homepage: http://github.com/onthebeach/id
|
132
153
|
licenses: []
|
133
154
|
post_install_message:
|
@@ -155,10 +176,13 @@ summary: Simple models based on hashes
|
|
155
176
|
test_files:
|
156
177
|
- spec/lib/id/model/association_spec.rb
|
157
178
|
- spec/lib/id/model/builder_spec.rb
|
179
|
+
- spec/lib/id/model/field_spec.rb
|
180
|
+
- spec/lib/id/model/form_spec.rb
|
158
181
|
- spec/lib/id/model/has_one_spec.rb
|
159
182
|
- spec/lib/id/model/type_casts_spec.rb
|
160
183
|
- spec/lib/id/model_spec.rb
|
161
184
|
- spec/lib/id/timestamps_spec.rb
|
162
185
|
- spec/lib/mike_spec.rb
|
163
186
|
- spec/spec_helper.rb
|
187
|
+
- spec/support/active_model_lint.rb
|
164
188
|
has_rdoc:
|