id 0.0.9 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +4 -3
- data/README.md +5 -0
- data/id.gemspec +2 -2
- data/lib/id.rb +16 -1
- data/lib/id/form.rb +21 -0
- data/lib/id/{model/form.rb → form/active_model_form.rb} +2 -2
- data/lib/id/form/descriptor.rb +27 -0
- data/lib/id/form/field_form.rb +14 -0
- data/lib/id/form/field_with_form_support.rb +12 -0
- data/lib/id/model.rb +1 -13
- data/lib/id/model/association.rb +3 -1
- data/lib/id/model/definer.rb +23 -0
- data/lib/id/model/descriptor.rb +3 -32
- data/lib/id/model/field.rb +11 -41
- data/lib/id/model/has_many.rb +2 -7
- data/lib/id/model/has_one.rb +7 -21
- data/lib/id/timestamps.rb +6 -9
- data/spec/lib/id/model/association_spec.rb +1 -1
- data/spec/lib/id/model/form_spec.rb +3 -2
- data/spec/lib/id/model_spec.rb +35 -53
- data/spec/lib/id/timestamps_spec.rb +2 -6
- data/spec/lib/mike_spec.rb +2 -2
- data/spec/spec_helper.rb +2 -7
- metadata +10 -11
- data/lib/id/all.rb +0 -5
- data/lib/id/model/all.rb +0 -9
- data/lib/id/model/builder.rb +0 -45
- data/lib/id/model/compound_field.rb +0 -23
- data/spec/lib/id/model/builder_spec.rb +0 -45
- data/spec/lib/id/model/has_one_spec.rb +0 -29
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
-I .
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ree-1.8.7-2012.02
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -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 (
|
24
|
-
i18n (~> 0.
|
23
|
+
money (3.7.1)
|
24
|
+
i18n (~> 0.4)
|
25
25
|
multi_json (1.7.3)
|
26
|
-
optional (0.0.
|
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.
|
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 = ['
|
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
|
-
|
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'
|
data/lib/id/form.rb
ADDED
@@ -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
|
@@ -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
|
data/lib/id/model.rb
CHANGED
@@ -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
|
data/lib/id/model/association.rb
CHANGED
@@ -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
|
data/lib/id/model/descriptor.rb
CHANGED
@@ -3,48 +3,19 @@ module Id
|
|
3
3
|
module Descriptor
|
4
4
|
|
5
5
|
def field(f, options={})
|
6
|
-
|
6
|
+
Field.new(self, f, options).define
|
7
7
|
end
|
8
8
|
|
9
9
|
def has_one(f, options={})
|
10
|
-
|
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
|
-
|
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
|
data/lib/id/model/field.rb
CHANGED
@@ -8,41 +8,22 @@ module Id
|
|
8
8
|
@options = options
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
12
|
-
|
13
|
-
model
|
14
|
-
|
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
|
31
|
-
model.send(:builder_class).define_setter name
|
17
|
+
def hook_define
|
32
18
|
end
|
33
19
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
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
|
42
|
-
|
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
|
data/lib/id/model/has_many.rb
CHANGED
@@ -2,13 +2,8 @@ module Id
|
|
2
2
|
module Model
|
3
3
|
class HasMany < Association
|
4
4
|
|
5
|
-
def
|
6
|
-
|
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
|
data/lib/id/model/has_one.rb
CHANGED
@@ -2,27 +2,13 @@ module Id
|
|
2
2
|
module Model
|
3
3
|
class HasOne < Association
|
4
4
|
|
5
|
-
def
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
|
data/lib/id/timestamps.rb
CHANGED
@@ -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(
|
9
|
+
super data.merge(:created_at => data.fetch('created_at', Time.now))
|
11
10
|
end
|
12
11
|
|
13
|
-
|
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
|
@@ -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
|
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
|
25
|
+
let (:gerbil) { Gerbil.new(:name => 'Berty') }
|
25
26
|
let (:form) { gerbil.as_form }
|
26
27
|
|
27
28
|
subject { gerbil.as_form }
|
data/spec/lib/id/model_spec.rb
CHANGED
@@ -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
|
19
|
-
field :qux, optional
|
20
|
-
field :quux, default
|
21
|
-
field :date_of_birth, optional
|
22
|
-
field :empty_date, optional
|
23
|
-
field :christmas, default
|
24
|
-
field :quxx, optional
|
25
|
-
|
26
|
-
|
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
|
42
|
-
baz
|
43
|
-
quxx
|
44
|
-
test_model
|
45
|
-
date_of_birth
|
46
|
-
aliased_model
|
47
|
-
nested_models
|
48
|
-
extra_nested_model
|
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
|
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
|
138
|
-
model.set(foo
|
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
|
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
|
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
|
163
|
-
two = TestModel.new(foo
|
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
|
169
|
-
two = TestModel.new(foo
|
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
|
177
|
-
two = TestModel.new(foo
|
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
|
183
|
-
two = TestModel.new(foo
|
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 "
|
191
|
-
it '
|
192
|
-
[{
|
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
|
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
|
21
|
+
updated = model.set(:foo => 123)
|
26
22
|
expect(updated.created_at).to be < updated.updated_at
|
27
23
|
end
|
28
24
|
|
data/spec/lib/mike_spec.rb
CHANGED
@@ -4,7 +4,7 @@ class Mike
|
|
4
4
|
include Id::Model
|
5
5
|
|
6
6
|
field :cat
|
7
|
-
field :dog, optional
|
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
|
16
|
+
let (:mike) { Mike.new(:cat => 'pooface') }
|
17
17
|
it 'returns cat' do
|
18
18
|
mike.catdog.should eq 'pooface'
|
19
19
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,11 +1,6 @@
|
|
1
|
-
require 'simplecov'
|
2
|
-
SimpleCov.start do
|
3
|
-
add_filter '/spec'
|
4
|
-
end
|
5
|
-
|
6
1
|
require 'rspec'
|
7
|
-
|
8
|
-
|
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.
|
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
|
-
-
|
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/
|
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/
|
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
|
data/lib/id/all.rb
DELETED
data/lib/id/model/all.rb
DELETED
@@ -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'
|
data/lib/id/model/builder.rb
DELETED
@@ -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
|