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 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: