mince_model 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|