id 0.0.9 → 0.0.10

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/.rspec ADDED
@@ -0,0 +1 @@
1
+ -I .
@@ -0,0 +1 @@
1
+ ree-1.8.7-2012.02
data/Gemfile CHANGED
@@ -2,3 +2,4 @@ source 'https://rubygems.org'
2
2
  gemspec
3
3
  gem 'test-unit'
4
4
  gem 'json', '~> 1.7.7'
5
+ gem 'money', '3.7.1'
@@ -20,10 +20,10 @@ GEM
20
20
  diff-lcs (1.2.1)
21
21
  i18n (0.6.4)
22
22
  json (1.7.7)
23
- money (5.1.1)
24
- i18n (~> 0.6.0)
23
+ money (3.7.1)
24
+ i18n (~> 0.4)
25
25
  multi_json (1.7.3)
26
- optional (0.0.6)
26
+ optional (0.0.7)
27
27
  rspec (2.13.0)
28
28
  rspec-core (~> 2.13.0)
29
29
  rspec-expectations (~> 2.13.0)
@@ -44,6 +44,7 @@ PLATFORMS
44
44
  DEPENDENCIES
45
45
  id!
46
46
  json (~> 1.7.7)
47
+ money (= 3.7.1)
47
48
  rspec
48
49
  simplecov
49
50
  test-unit
data/README.md CHANGED
@@ -58,3 +58,8 @@ Types are inferred from the association name unless one is specified.
58
58
  You can even set fields on nested models in this way:
59
59
 
60
60
  person.hat.set(color: 'red') # => returns a new person object with a new hat object with its color set to red
61
+
62
+ #### Avoiding nils
63
+
64
+ `id` tries to avoid nils entirely, by using the Option pattern found in many functional programming languages and implemented [here](http://github.com/rsslldnphy/optional).
65
+ Just mark optional fields as `optional: true` and their accessors will return either `Some[value]` or `None`.
data/id.gemspec CHANGED
@@ -1,11 +1,11 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'id'
3
- s.version = '0.0.9'
3
+ s.version = '0.0.10'
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"
7
7
  s.authors = ["Russell Dunphy", "Radek Molenda"]
8
- s.email = ['russell@russelldunphy.com', 'radek.molenda@gmail.com']
8
+ s.email = ['rssll@rsslldnphy.com', 'radek.molenda@gmail.com']
9
9
  s.files = `git ls-files`.split($\)
10
10
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
11
11
  s.require_paths = ["lib"]
data/lib/id.rb CHANGED
@@ -4,4 +4,19 @@ require 'active_support/core_ext/string/inflections'
4
4
  require 'active_support/core_ext/hash/except'
5
5
  require 'optional'
6
6
  require 'money'
7
- require_relative 'id/all'
7
+ require 'id/missing_attribute_error'
8
+ require 'id/hashifier'
9
+ require 'id/model/definer'
10
+ require 'id/model/descriptor'
11
+ require 'id/model/type_casts'
12
+ require 'id/model/field'
13
+ require 'id/model/association'
14
+ require 'id/model/has_one'
15
+ require 'id/model/has_many'
16
+ require 'id/model'
17
+ require 'id/timestamps'
18
+ require 'id/form'
19
+ require 'id/form/active_model_form'
20
+ require 'id/form/descriptor'
21
+ require 'id/form/field_with_form_support'
22
+ require 'id/form/field_form'
@@ -0,0 +1,21 @@
1
+ module Id
2
+ module Form
3
+
4
+ def as_form
5
+ @form_object ||= self.class.form_object.new(self)
6
+ end
7
+
8
+ def errors
9
+ as_form.errors
10
+ end
11
+
12
+ def valid?
13
+ as_form.valid?
14
+ end
15
+
16
+ def self.included(base)
17
+ base.extend(Descriptor)
18
+ end
19
+
20
+ end
21
+ end
@@ -1,6 +1,6 @@
1
1
  module Id
2
- module Model
3
- class Form
2
+ module Form
3
+ class ActiveModelForm
4
4
  include ActiveModel::Validations
5
5
  include ActiveModel::Conversion
6
6
  extend ActiveModel::Naming
@@ -0,0 +1,27 @@
1
+ module Id
2
+ module Form
3
+ module Descriptor
4
+
5
+ def field(f, options={})
6
+ FieldWithFormSupport.new(self, f, options).define
7
+ end
8
+
9
+ def form &block
10
+ form_object.send :instance_exec, &block
11
+ end
12
+
13
+ def form_object
14
+ base = self
15
+ @form_object ||= Class.new(ActiveModelForm) do
16
+ eingenclass = class << self
17
+ self
18
+ end
19
+ eingenclass.send(:define_method, :model_name) do
20
+ ActiveModel::Name.new(self, nil, base.name)
21
+ end
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,14 @@
1
+ class FieldForm
2
+
3
+ def self.define(field)
4
+ field.model.form_object.instance_eval do
5
+ define_method field.name do
6
+ memoize field.name do
7
+ Option[model.send(field.name)].flatten.value_or nil if model.data.has_key? field.key
8
+ end
9
+ end
10
+ attr_writer field.name
11
+ end
12
+ end
13
+
14
+ end
@@ -0,0 +1,12 @@
1
+ module Id
2
+ module Form
3
+ class FieldWithFormSupport < Id::Model::Field
4
+
5
+ def hook_define
6
+ FieldForm.define(self)
7
+ end
8
+
9
+ end
10
+ end
11
+ end
12
+
@@ -23,18 +23,6 @@ module Id
23
23
  data.hash
24
24
  end
25
25
 
26
- def as_form
27
- @form_object ||= self.class.form_object.new(self)
28
- end
29
-
30
- def errors
31
- as_form.errors
32
- end
33
-
34
- def valid?
35
- as_form.valid?
36
- end
37
-
38
26
  private
39
27
 
40
28
  def self.included(base)
@@ -42,7 +30,7 @@ module Id
42
30
  end
43
31
 
44
32
  def memoize(f, &b)
45
- instance_variable_get("@#{f}") || instance_variable_set("@#{f}", b.call)
33
+ instance_variable_get("@#{f}") || instance_variable_set("@#{f}", b.call(data))
46
34
  end
47
35
 
48
36
  end
@@ -26,7 +26,9 @@ module Id
26
26
  end
27
27
 
28
28
  def parent
29
- @parent ||= constants.find { |c| c.const_defined? child }
29
+ @parent ||= constants.find do |c|
30
+ c.ancestors.find { |anc| anc.const_defined? child }
31
+ end
30
32
  end
31
33
 
32
34
  def constants
@@ -0,0 +1,23 @@
1
+ module Id
2
+ module Model
3
+ class Definer
4
+
5
+ def self.method_memoize(context, name, &value_block)
6
+ method(context, name) do |object|
7
+ object.instance_eval do
8
+ memoize(name, &value_block)
9
+ end
10
+ end
11
+ end
12
+
13
+ def self.method(context, name, &value_block)
14
+ context.instance_eval do
15
+ define_method name do
16
+ value_block.call(self)
17
+ end
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -3,48 +3,19 @@ module Id
3
3
  module Descriptor
4
4
 
5
5
  def field(f, options={})
6
- (options[:optional] ? FieldOption : Field).new(self, f, options).define
6
+ Field.new(self, f, options).define
7
7
  end
8
8
 
9
9
  def has_one(f, options={})
10
- (options[:optional] ? HasOneOption : HasOne).new(self, f, options).define
10
+ HasOne.new(self, f, options).define
11
11
  end
12
12
 
13
13
  def has_many(f, options={})
14
14
  HasMany.new(self, f, options).define
15
15
  end
16
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
17
  def to_proc
41
- ->(data) { new data }
42
- end
43
-
44
- private
45
-
46
- def builder_class
47
- @builder_class ||= Class.new { include Builder }
18
+ lambda { |data| new data }
48
19
  end
49
20
 
50
21
  end
@@ -8,41 +8,22 @@ 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
-
21
- def define_getter
22
- field = self
23
- model.send :define_method, name do
24
- memoize field.name do
25
- field.cast data.fetch(field.key, &field.default_value)
26
- end
27
- end
11
+ def define
12
+ Definer.method_memoize(model, name) { |data| value_of(data) }
13
+ Definer.method(model, "#{name}?") { |obj| presence_of(obj.data) }
14
+ hook_define
28
15
  end
29
16
 
30
- def define_setter
31
- model.send(:builder_class).define_setter name
17
+ def hook_define
32
18
  end
33
19
 
34
- def define_is_present
35
- field = self
36
- model.send :define_method, "#{name}?" do
37
- data.has_key?(field.key) && !data.fetch(field.key).nil?
38
- end
20
+ def value_of(data)
21
+ value = data.fetch(key, &default_value)
22
+ optional ? Option[value].map{ |d| cast d } : cast(value)
39
23
  end
40
24
 
41
- def define
42
- define_getter
43
- define_setter
44
- define_is_present
45
- define_form_field
25
+ def presence_of(data)
26
+ data.has_key?(key) && !data.fetch(key).nil?
46
27
  end
47
28
 
48
29
  def cast(value)
@@ -71,21 +52,10 @@ module Id
71
52
  def optional?
72
53
  options.fetch(:optional, false)
73
54
  end
55
+ alias_method :optional, :optional?
74
56
 
75
57
  attr_reader :model, :name, :options
76
- end
77
58
 
78
- class FieldOption < Field
79
- def define_getter
80
- field = self
81
- model.send :define_method, name do
82
- memoize field.name do
83
- Option[data.fetch(field.key, &field.default_value)].map do |d|
84
- field.cast d
85
- end
86
- end
87
- end
88
- end
89
59
  end
90
60
  end
91
61
  end
@@ -2,13 +2,8 @@ module Id
2
2
  module Model
3
3
  class HasMany < Association
4
4
 
5
- def define_getter
6
- field = self
7
- model.send :define_method, name do
8
- memoize field.name do
9
- data.fetch(field.key, []).map { |r| field.type.new(r) }
10
- end
11
- end
5
+ def value_of(data)
6
+ data.fetch(key, []).map { |r| type.new(r) }
12
7
  end
13
8
 
14
9
  end
@@ -2,27 +2,13 @@ module Id
2
2
  module Model
3
3
  class HasOne < Association
4
4
 
5
- def define_getter
6
- field = self
7
- model.send :define_method, name do
8
- memoize field.name do
9
- child = data.fetch(field.key) { raise MissingAttributeError, field.key }
10
- field.type.new(child) unless child.nil?
11
- end
12
- end
13
- end
14
-
15
- end
16
-
17
- class HasOneOption < Association
18
-
19
- def define_getter
20
- field = self
21
- model.send :define_method, name do
22
- memoize field.name do
23
- child = data.fetch(field.key, nil)
24
- child.nil? ? None : Some[field.type.new(child)]
25
- end
5
+ def value_of(data)
6
+ if optional?
7
+ child = data.fetch(field.key, nil)
8
+ child.nil? ? None : Some[field.type.new(child)]
9
+ else
10
+ child = data.fetch(key) { raise MissingAttributeError, key }
11
+ type.new(child) unless child.nil?
26
12
  end
27
13
  end
28
14
 
@@ -1,23 +1,20 @@
1
1
  module Id
2
2
  module Timestamps
3
-
4
3
  def self.included(base)
5
4
  base.field :created_at
6
5
  base.field :updated_at
7
6
  end
8
7
 
9
8
  def initialize(data = {})
10
- super data.merge(_timestamps data)
9
+ super data.merge(:created_at => data.fetch('created_at', Time.now))
11
10
  end
12
11
 
13
- private
14
-
15
- def _timestamps(data, now=Time.now)
16
- {
17
- created_at: data.fetch('created_at', now),
18
- updated_at: now
19
- }
12
+ def set(values)
13
+ self.class.new(super.data.merge(:updated_at => Time.now))
20
14
  end
21
15
 
16
+ def unset(*keys)
17
+ self.class.new(super.data.merge(:updated_at => Time.now))
18
+ end
22
19
  end
23
20
  end
@@ -11,7 +11,7 @@ describe Id::Model::Association do
11
11
  end
12
12
  end
13
13
 
14
- let (:model) { stub(name: "Foo::Bar::Baz::Quux") }
14
+ let (:model) { stub(:name => "Foo::Bar::Baz::Quux") }
15
15
  let (:has_one) { Id::Model::Association.new(model, "yak", {}) }
16
16
 
17
17
  describe "hierarchy" do
@@ -2,13 +2,14 @@ require 'spec_helper'
2
2
 
3
3
  class Gerbil
4
4
  include Id::Model
5
+ include Id::Form
5
6
 
6
7
  field :name
7
8
  field :paws
8
9
 
9
10
  form do
10
11
  validates_presence_of :name
11
- validates_length_of :name, maximum: 4
12
+ validates_length_of :name, :maximum => 4
12
13
  end
13
14
 
14
15
  def name_in_caps
@@ -21,7 +22,7 @@ module Id
21
22
  module Model
22
23
  describe Form do
23
24
 
24
- let (:gerbil) { Gerbil.new(name: 'Berty') }
25
+ let (:gerbil) { Gerbil.new(:name => 'Berty') }
25
26
  let (:form) { gerbil.as_form }
26
27
 
27
28
  subject { gerbil.as_form }
@@ -5,28 +5,20 @@ class NestedModel
5
5
  field :yak
6
6
  end
7
7
 
8
- class CompboundElementModel
9
- include Id::Model
10
- field :plugh
11
- field :thud
12
- end
13
-
14
8
  class TestModel
15
9
  include Id::Model
16
10
 
17
11
  field :foo
18
- field :bar, key: 'baz'
19
- field :qux, optional: true
20
- field :quux, default: false
21
- field :date_of_birth, optional: true, type: Date
22
- field :empty_date, optional: true, type: Date
23
- field :christmas, default: Date.new(2014,12,25), type: Date
24
- field :quxx, optional: true
25
-
26
- compound_field :corge, {plugh: 'foo', thud: 'quux'}, type: CompboundElementModel
27
-
28
- has_one :aliased_model, type: NestedModel
29
- has_one :nested_model, key: 'aliased_model'
12
+ field :bar, :key => 'baz'
13
+ field :qux, :optional => true
14
+ field :quux, :default => false
15
+ field :date_of_birth, :optional => true, :type => Date
16
+ field :empty_date, :optional => true, :type => Date
17
+ field :christmas, :default => Date.new(2014,12,25), :type => Date
18
+ field :quxx, :optional => true
19
+
20
+ has_one :aliased_model, :type => NestedModel
21
+ has_one :nested_model, :key => 'aliased_model'
30
22
  has_one :extra_nested_model
31
23
  has_one :test_model
32
24
  has_many :nested_models
@@ -38,19 +30,19 @@ class TestModel
38
30
  end
39
31
 
40
32
  describe Id::Model do
41
- let (:model) { TestModel.new(foo: 3,
42
- baz: 6,
43
- quxx: 8,
44
- test_model: {},
45
- date_of_birth: '06-06-1983',
46
- aliased_model: { 'yak' => 11},
47
- nested_models: [{ 'yak' => 11}, { yak: 14 }],
48
- extra_nested_model: { cats: "MIAOW" }) }
33
+ let (:model) { TestModel.new(:foo => 3,
34
+ :baz => 6,
35
+ :quxx => 8,
36
+ :test_model => {},
37
+ :date_of_birth => '06-06-1983',
38
+ :aliased_model => { 'yak' => 11},
39
+ :nested_models => [{ 'yak' => 11}, { :yak => 14 }],
40
+ :extra_nested_model => { :cats => "MIAOW" }) }
49
41
 
50
42
 
51
43
  describe ".new" do
52
44
  it 'converts any passed id models to their hash representations' do
53
- new_model = TestModel.new(test_model: model)
45
+ new_model = TestModel.new(:test_model => model)
54
46
  new_model.test_model.data.should eq model.data
55
47
  end
56
48
  end
@@ -96,16 +88,6 @@ describe Id::Model do
96
88
 
97
89
  end
98
90
 
99
- describe ".compound_field" do
100
- it 'defines an accessor on the model' do
101
- model.corge.should be_a CompboundElementModel
102
- end
103
-
104
- it 'deals with default values' do
105
- model.corge.thud.should be_false
106
- end
107
- end
108
-
109
91
  describe ".has_one" do
110
92
  it "allows nested models" do
111
93
  model.aliased_model.should be_a NestedModel
@@ -134,14 +116,14 @@ describe Id::Model do
134
116
 
135
117
  describe "#set" do
136
118
  it "creates a new model with the provided values changed" do
137
- model.set(foo: 999).should be_a TestModel
138
- model.set(foo: 999).foo.should eq 999
119
+ model.set(:foo => 999).should be_a TestModel
120
+ model.set(:foo => 999).foo.should eq 999
139
121
  end
140
122
  end
141
123
 
142
124
  describe "#unset" do
143
125
  it 'returns a new basket minus the passed key' do
144
- expect { model.set(foo: 999, bar: 555).unset(:foo, :bar).foo }.to raise_error Id::MissingAttributeError, "foo"
126
+ expect { model.set(:foo => 999, :bar => 555).unset(:foo, :bar).foo }.to raise_error Id::MissingAttributeError, "foo"
145
127
  end
146
128
 
147
129
  it 'does not error if the key to be removed does not exist' do
@@ -151,7 +133,7 @@ describe Id::Model do
151
133
 
152
134
  describe "#fields are present methods" do
153
135
  it 'allows you to check if fields are present' do
154
- model = TestModel.new(foo: 1)
136
+ model = TestModel.new(:foo => 1)
155
137
  model.foo?.should be_true
156
138
  model.bar?.should be_false
157
139
  end
@@ -159,38 +141,38 @@ describe Id::Model do
159
141
 
160
142
  describe "#==" do
161
143
  it 'is equal to another id model with the same data' do
162
- one = TestModel.new(foo: 1)
163
- two = TestModel.new(foo: 1)
144
+ one = TestModel.new(:foo => 1)
145
+ two = TestModel.new(:foo => 1)
164
146
  one.should eq two
165
147
  end
166
148
 
167
149
  it 'is not equal to two models with different data' do
168
- one = TestModel.new(foo: 1)
169
- two = TestModel.new(foo: 2)
150
+ one = TestModel.new(:foo => 1)
151
+ two = TestModel.new(:foo => 2)
170
152
  one.should_not eq two
171
153
  end
172
154
  end
173
155
 
174
156
  describe "#hash" do
175
157
  it 'allows id models to be used as hash keys' do
176
- one = TestModel.new(foo: 1)
177
- two = TestModel.new(foo: 1)
158
+ one = TestModel.new(:foo => 1)
159
+ two = TestModel.new(:foo => 1)
178
160
  hash = { one => :found }
179
161
  hash[two].should eq :found
180
162
  end
181
163
  it 'they are different keys if the data is different' do
182
- one = TestModel.new(foo: 1)
183
- two = TestModel.new(foo: 2)
164
+ one = TestModel.new(:foo => 1)
165
+ two = TestModel.new(:foo => 2)
184
166
  hash = { one => :found }
185
167
  hash[two].should be_nil
186
168
  hash[one].should eq :found
187
169
  end
188
170
  end
189
171
 
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
172
+ describe "#to_proc" do
173
+ it 'eta expands the model class into its constructor' do
174
+ [{},{}].map(&TestModel).all? { |m| m.is_a? TestModel }.should be_true
194
175
  end
195
176
  end
177
+
196
178
  end
@@ -11,18 +11,14 @@ end
11
11
  module Id
12
12
  describe Timestamps do
13
13
 
14
- let (:model) { TimeStampedModel.new(foo: 999, bar: 666) }
14
+ let(:model) { TimeStampedModel.new(:foo => 999, :bar => 666) }
15
15
 
16
16
  it 'should have a created_at date' do
17
17
  model.created_at.should be_a Time
18
18
  end
19
19
 
20
- it 'should have an updated_at date' do
21
- model.updated_at.should be_a Time
22
- end
23
-
24
20
  it 'should update the updated at when set is called' do
25
- updated = model.set(foo: 123)
21
+ updated = model.set(:foo => 123)
26
22
  expect(updated.created_at).to be < updated.updated_at
27
23
  end
28
24
 
@@ -4,7 +4,7 @@ class Mike
4
4
  include Id::Model
5
5
 
6
6
  field :cat
7
- field :dog, optional: true
7
+ field :dog, :optional => true
8
8
 
9
9
  def catdog
10
10
  dog.value_or cat
@@ -13,7 +13,7 @@ class Mike
13
13
  end
14
14
 
15
15
  describe "Foobar" do
16
- let (:mike) { Mike.new(cat: 'pooface') }
16
+ let (:mike) { Mike.new(:cat => 'pooface') }
17
17
  it 'returns cat' do
18
18
  mike.catdog.should eq 'pooface'
19
19
  end
@@ -1,11 +1,6 @@
1
- require 'simplecov'
2
- SimpleCov.start do
3
- add_filter '/spec'
4
- end
5
-
6
1
  require 'rspec'
7
- require_relative '../lib/id'
8
- require_relative 'support/active_model_lint'
2
+ require 'lib/id'
3
+ require 'support/active_model_lint'
9
4
 
10
5
 
11
6
  RSpec.configure do |config|
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.9
4
+ version: 0.0.10
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -110,39 +110,40 @@ dependencies:
110
110
  version: '0'
111
111
  description: Developed at On The Beach Ltd. Contact russell.dunphy@onthebeach.co.uk
112
112
  email:
113
- - russell@russelldunphy.com
113
+ - rssll@rsslldnphy.com
114
114
  - radek.molenda@gmail.com
115
115
  executables: []
116
116
  extensions: []
117
117
  extra_rdoc_files: []
118
118
  files:
119
119
  - .gitignore
120
+ - .rspec
121
+ - .ruby-version
120
122
  - Gemfile
121
123
  - Gemfile.lock
122
124
  - LICENSE.md
123
125
  - README.md
124
126
  - id.gemspec
125
127
  - lib/id.rb
126
- - lib/id/all.rb
128
+ - lib/id/form.rb
129
+ - lib/id/form/active_model_form.rb
130
+ - lib/id/form/descriptor.rb
131
+ - lib/id/form/field_form.rb
132
+ - lib/id/form/field_with_form_support.rb
127
133
  - lib/id/hashifier.rb
128
134
  - lib/id/missing_attribute_error.rb
129
135
  - lib/id/model.rb
130
- - lib/id/model/all.rb
131
136
  - lib/id/model/association.rb
132
- - lib/id/model/builder.rb
133
- - lib/id/model/compound_field.rb
137
+ - lib/id/model/definer.rb
134
138
  - lib/id/model/descriptor.rb
135
139
  - lib/id/model/field.rb
136
- - lib/id/model/form.rb
137
140
  - lib/id/model/has_many.rb
138
141
  - lib/id/model/has_one.rb
139
142
  - lib/id/model/type_casts.rb
140
143
  - lib/id/timestamps.rb
141
144
  - spec/lib/id/model/association_spec.rb
142
- - spec/lib/id/model/builder_spec.rb
143
145
  - spec/lib/id/model/field_spec.rb
144
146
  - spec/lib/id/model/form_spec.rb
145
- - spec/lib/id/model/has_one_spec.rb
146
147
  - spec/lib/id/model/type_casts_spec.rb
147
148
  - spec/lib/id/model_spec.rb
148
149
  - spec/lib/id/timestamps_spec.rb
@@ -175,10 +176,8 @@ specification_version: 3
175
176
  summary: Simple models based on hashes
176
177
  test_files:
177
178
  - spec/lib/id/model/association_spec.rb
178
- - spec/lib/id/model/builder_spec.rb
179
179
  - spec/lib/id/model/field_spec.rb
180
180
  - spec/lib/id/model/form_spec.rb
181
- - spec/lib/id/model/has_one_spec.rb
182
181
  - spec/lib/id/model/type_casts_spec.rb
183
182
  - spec/lib/id/model_spec.rb
184
183
  - spec/lib/id/timestamps_spec.rb
@@ -1,5 +0,0 @@
1
- require_relative 'missing_attribute_error'
2
- require_relative 'hashifier'
3
- require_relative 'model/all'
4
- require_relative 'model'
5
- require_relative 'timestamps'
@@ -1,9 +0,0 @@
1
- require_relative 'descriptor'
2
- require_relative 'type_casts'
3
- require_relative 'field'
4
- require_relative 'association'
5
- require_relative 'has_one'
6
- require_relative 'has_many'
7
- require_relative 'builder'
8
- require_relative 'compound_field'
9
- require_relative 'form'
@@ -1,45 +0,0 @@
1
- module Id
2
- module Model
3
- module Builder
4
-
5
- def initialize(model, data={})
6
- @model = model
7
- @data = data
8
- end
9
-
10
- def build
11
- model.new data
12
- end
13
-
14
- private
15
-
16
- def set(f, value)
17
- self.class.new(model, data.merge(f.to_s => ensure_hash(value)))
18
- end
19
-
20
- def ensure_hash(value)
21
- case value
22
- when Id::Model then value.data
23
- when Array then value.map { |v| ensure_hash(v) }
24
- else value end
25
- end
26
-
27
- attr_reader :model, :data
28
-
29
- def self.included(base)
30
- base.extend(FieldBuilder)
31
- end
32
-
33
- module FieldBuilder
34
-
35
- def define_setter(f)
36
- define_method f do |value|
37
- set(f, value)
38
- end
39
- end
40
-
41
- end
42
-
43
- end
44
- end
45
- end
@@ -1,23 +0,0 @@
1
- module Id
2
- module Model
3
- class CompoundField < Association
4
-
5
- def initialize(model, name, fields, options)
6
- @fields = fields
7
- super(model, name, options)
8
- end
9
-
10
- def define_getter
11
- field = self
12
- model.send :define_method, name do
13
- memoize field.name do
14
- compound = Hash[field.fields.map { |k,v| [k.to_s, send(v) { raise MissingAttributeError, k.to_s }]}]
15
- field.type.new(compound)
16
- end
17
- end
18
- end
19
-
20
- attr_accessor :fields
21
- end
22
- end
23
- end
@@ -1,45 +0,0 @@
1
- require 'spec_helper'
2
-
3
- class BuilderModel
4
- include Id::Model
5
-
6
- field :foo
7
- field :bar
8
- has_one :nested_builder_model
9
- has_many :nested_builder_models
10
-
11
- class NestedBuilderModel
12
- include Id::Model
13
-
14
- field :baz
15
- end
16
-
17
- end
18
-
19
- module Id
20
- describe Model::Builder do
21
-
22
- it 'models can be built using a builder' do
23
- BuilderModel.builder.build.should be_a BuilderModel
24
- end
25
-
26
- it 'defines chainable setter methods for each field' do
27
- model = BuilderModel.builder.foo(4).bar("hello cat").build
28
- model.foo.should eq 4
29
- model.bar.should eq "hello cat"
30
- end
31
-
32
- it 'allows setting of has_one associations using their respective builders' do
33
- nested_model = BuilderModel::NestedBuilderModel.builder.baz(:quux).build
34
- model = BuilderModel.builder.nested_builder_model(nested_model).build
35
- model.nested_builder_model.baz.should eq :quux
36
- end
37
-
38
- it 'allows setting of has_many associations using their respective builders' do
39
- nested_model = BuilderModel::NestedBuilderModel.builder.baz(:quux).build
40
- model = BuilderModel.builder.nested_builder_models([nested_model]).build
41
- model.nested_builder_models.first.baz.should eq :quux
42
- end
43
-
44
- end
45
- end
@@ -1,29 +0,0 @@
1
- require 'spec_helper'
2
-
3
- class Cat
4
- include Id::Model
5
- end
6
-
7
- class OptionalModel
8
- include Id::Model
9
-
10
- has_one :cat, optional: true
11
- end
12
-
13
- module Id
14
- module Model
15
-
16
- describe HasOneOption do
17
-
18
- it 'returns a none if the key is missing' do
19
- OptionalModel.new({}).cat.should be_none
20
- end
21
-
22
- it 'returns a some if the key is there' do
23
- OptionalModel.new(cat: {}).cat.should be_some(Cat)
24
- end
25
-
26
- end
27
-
28
- end
29
- end