mince_model 0.1.0
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/lib/mince_model.rb +103 -0
- data/lib/mince_model/version.rb +3 -0
- data/spec/lib/mince_model_spec.rb +128 -0
- metadata +99 -0
data/lib/mince_model.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_model'
|
3
|
+
require 'active_support/core_ext/module/delegation'
|
4
|
+
|
5
|
+
require "mince_model/version"
|
6
|
+
|
7
|
+
|
8
|
+
module MinceModel
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
included do
|
12
|
+
include ActiveModel::Conversion
|
13
|
+
extend ActiveModel::Naming
|
14
|
+
|
15
|
+
attr_accessor :id
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
def data_model(model=nil)
|
20
|
+
@data_model = model if model
|
21
|
+
@data_model
|
22
|
+
end
|
23
|
+
|
24
|
+
def all
|
25
|
+
data_model.all.map{|a| new a }
|
26
|
+
end
|
27
|
+
|
28
|
+
def find(id)
|
29
|
+
a = data_model.find(id)
|
30
|
+
new a if a
|
31
|
+
end
|
32
|
+
|
33
|
+
def field(field_name, options={})
|
34
|
+
if options[:assignable]
|
35
|
+
add_assignable_field(field_name)
|
36
|
+
else
|
37
|
+
add_readonly_field(field_name)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_readonly_field(field_name)
|
42
|
+
fields << field_name
|
43
|
+
readonly_fields << field_name
|
44
|
+
attr_reader field_name
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_assignable_field(field_name)
|
48
|
+
fields << field_name
|
49
|
+
assignable_fields << field_name
|
50
|
+
attr_accessor field_name
|
51
|
+
end
|
52
|
+
|
53
|
+
def fields(*field_names)
|
54
|
+
@fields ||= []
|
55
|
+
field_names.each {|field_name| field(field_name) }
|
56
|
+
@fields
|
57
|
+
end
|
58
|
+
|
59
|
+
def assignable_fields
|
60
|
+
@assignable_fields ||= []
|
61
|
+
end
|
62
|
+
|
63
|
+
def readonly_fields
|
64
|
+
@readonly_fields ||= []
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
delegate :data_model, :assignable_fields, :readonly_fields, :fields, to: 'self.class'
|
69
|
+
|
70
|
+
def initialize(hash={})
|
71
|
+
@id = hash[:id]
|
72
|
+
readonly_fields.each do |field_name|
|
73
|
+
self.instance_variable_set("@#{field_name}", hash[field_name]) if hash[field_name]
|
74
|
+
end
|
75
|
+
self.attributes = hash
|
76
|
+
end
|
77
|
+
|
78
|
+
def persisted?
|
79
|
+
!!id
|
80
|
+
end
|
81
|
+
|
82
|
+
def save
|
83
|
+
ensure_no_extra_fields
|
84
|
+
if persisted?
|
85
|
+
data_model.update(self)
|
86
|
+
else
|
87
|
+
@id = data_model.store(self)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def ensure_no_extra_fields
|
92
|
+
extra_fields = (fields - data_model.data_fields)
|
93
|
+
if extra_fields.any?
|
94
|
+
raise "Tried to save a #{self.class.name} with fields not specified in #{data_model.name}: #{extra_fields.join(', ')}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def attributes=(hash={})
|
99
|
+
assignable_fields.each do |field|
|
100
|
+
send("#{field}=", hash[field]) if hash[field]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require_relative '../../lib/mince_model'
|
2
|
+
|
3
|
+
describe MinceModel do
|
4
|
+
let(:klass) do
|
5
|
+
Class.new do
|
6
|
+
include MinceModel
|
7
|
+
|
8
|
+
data_model Class.new
|
9
|
+
|
10
|
+
field :meh
|
11
|
+
field :foo, assignable: true
|
12
|
+
field :bar, assignable: false
|
13
|
+
fields :baz, :qaaz
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:meh) { mock 'meh' }
|
18
|
+
let(:foo) { mock 'foo' }
|
19
|
+
let(:bar) { mock 'bar' }
|
20
|
+
let(:baz) { mock 'baz' }
|
21
|
+
let(:qaaz) { mock 'qaaz' }
|
22
|
+
|
23
|
+
subject { klass.new(meh: meh, foo: foo, bar: bar, baz: baz, qaaz: qaaz) }
|
24
|
+
|
25
|
+
it 'initializes the object and assigns values to the fields' do
|
26
|
+
subject.meh.should == meh
|
27
|
+
subject.foo.should == foo
|
28
|
+
subject.bar.should == bar
|
29
|
+
subject.baz.should == baz
|
30
|
+
subject.qaaz.should == qaaz
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'can set the assignable fields outside of the initilizer' do
|
34
|
+
subject.foo = 'foo1'
|
35
|
+
subject.foo.should == 'foo1'
|
36
|
+
subject.attributes = { foo: 'foo2' }
|
37
|
+
subject.foo.should == 'foo2'
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'cannot set the readonly field outside of the initilizer' do
|
41
|
+
subject.attributes = { bar: 'bar1' }
|
42
|
+
subject.bar.should == bar
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'fields are readonly by default' do
|
46
|
+
subject.attributes = { meh: 'meh1' }
|
47
|
+
subject.meh.should == meh
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'saving' do
|
51
|
+
let(:id) { mock 'id' }
|
52
|
+
let(:data_fields) { subject.fields }
|
53
|
+
|
54
|
+
before do
|
55
|
+
subject.data_model.stub(:data_fields).and_return(data_fields)
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'when the model has fields that are not defined in the data model' do
|
59
|
+
let(:data_fields) { subject.fields - extra_fields }
|
60
|
+
let(:extra_fields) { subject.fields[0..-2] }
|
61
|
+
|
62
|
+
it 'raises an exception with a message' do
|
63
|
+
expect { subject.save }.to raise_error("Tried to save a #{subject.class.name} with fields not specified in #{subject.data_model.name}: #{extra_fields.join(', ')}")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'when it has not yet been persisted to the mince data model' do
|
68
|
+
before do
|
69
|
+
subject.data_model.stub(:store => id)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'stores the model' do
|
73
|
+
subject.data_model.should_receive(:store).with(subject).and_return(id)
|
74
|
+
|
75
|
+
subject.save
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'assigns the id' do
|
79
|
+
subject.save
|
80
|
+
|
81
|
+
subject.id.should == id
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'when it has already been persisted to the mince data model' do
|
86
|
+
let(:subject) { klass.new(id: id) }
|
87
|
+
|
88
|
+
it 'updates the model' do
|
89
|
+
subject.data_model.should_receive(:update).with(subject)
|
90
|
+
|
91
|
+
subject.save
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "Query Methods:" do
|
97
|
+
describe 'finding a model by id' do
|
98
|
+
subject { klass.find(id) }
|
99
|
+
|
100
|
+
let(:id) { mock 'id' }
|
101
|
+
|
102
|
+
before do
|
103
|
+
klass.data_model.stub(:find).with(id).and_return(data)
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'when it exists' do
|
107
|
+
let(:data) { mock 'data' }
|
108
|
+
let(:model) { mock 'model' }
|
109
|
+
|
110
|
+
before do
|
111
|
+
klass.stub(:new).with(data).and_return(model)
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'returns the model' do
|
115
|
+
subject.should == model
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'when it does not exist' do
|
120
|
+
let(:data) { nil }
|
121
|
+
|
122
|
+
it 'returns nothing' do
|
123
|
+
subject.should be_nil
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mince_model
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Matt Simpson
|
9
|
+
- Craig Buchek
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-08-20 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activesupport
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '3.2'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '3.2'
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: activemodel
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ~>
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '3.2'
|
39
|
+
type: :runtime
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '3.2'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.11'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '2.11'
|
63
|
+
description: Common model behavior for objects backed by Mince
|
64
|
+
email:
|
65
|
+
- matt.simpson3@gmail.com
|
66
|
+
executables: []
|
67
|
+
extensions: []
|
68
|
+
extra_rdoc_files: []
|
69
|
+
files:
|
70
|
+
- lib/mince_model.rb
|
71
|
+
- lib/mince_model/version.rb
|
72
|
+
- spec/lib/mince_model_spec.rb
|
73
|
+
homepage: http://github.com/ionicmobile/mince_model
|
74
|
+
licenses: []
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ! '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ! '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
requirements: []
|
92
|
+
rubyforge_project:
|
93
|
+
rubygems_version: 1.8.24
|
94
|
+
signing_key:
|
95
|
+
specification_version: 3
|
96
|
+
summary: Common model behavior for objects backed by Mince
|
97
|
+
test_files:
|
98
|
+
- spec/lib/mince_model_spec.rb
|
99
|
+
has_rdoc:
|