id 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,2 +1,4 @@
1
1
  source 'https://rubygems.org'
2
2
  gemspec
3
+ gem 'test-unit'
4
+ gem 'json', '~> 1.7.7'
data/Gemfile.lock CHANGED
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- id (0.0.7)
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.2)
20
- optional (0.0.4)
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.8'
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
@@ -1,3 +1,4 @@
1
+ require 'active_model'
1
2
  require 'active_support/inflector'
2
3
  require 'active_support/core_ext/string/inflections'
3
4
  require 'active_support/core_ext/hash/except'
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
- private
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 memoize(f, &b)
24
- instance_variable_get("@#{f}") || instance_variable_set("@#{f}", b.call)
22
+ def hash
23
+ data.hash
25
24
  end
26
25
 
27
- module Descriptor
28
-
29
- def field(f, options={})
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
- def has_many(f, options={})
38
- HasMany.new(self, f, options).define
39
- end
30
+ def errors
31
+ as_form.errors
32
+ end
40
33
 
41
- def compound_field(f, fields, options={})
42
- CompoundField.new(self, f, fields, options).define
43
- end
34
+ def valid?
35
+ as_form.valid?
36
+ end
44
37
 
45
- def builder
46
- builder_class.new(self)
47
- end
38
+ private
48
39
 
49
- private
40
+ def self.included(base)
41
+ base.extend(Descriptor)
42
+ end
50
43
 
51
- def builder_class
52
- @builder_class ||= Class.new { include Builder }
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
@@ -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
- if d = data.fetch(field.key, &field.default_value)
73
- Some[field.cast d]
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
@@ -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
@@ -5,6 +5,7 @@ end
5
5
 
6
6
  require 'rspec'
7
7
  require_relative '../lib/id'
8
+ require_relative 'support/active_model_lint'
8
9
 
9
10
 
10
11
  RSpec.configure do |config|
@@ -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.8
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: