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