minimapper 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml CHANGED
@@ -1,5 +1,8 @@
1
1
  language: ruby
2
2
  rvm:
3
+ # 2.0 rubies
4
+ - 2.0.0
5
+
3
6
  # 1.9 rubies
4
7
  - 1.9.3
5
8
  - 1.9.2
data/Gemfile CHANGED
@@ -1,7 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  # We don't require active_record to use minimapper, only to
4
- # use minimapper/ar. We do require it for the tests though :)
4
+ # use minimapper/mapper/ar. We do require it for the tests though :)
5
5
  gem "activerecord"
6
6
  gem "rspec"
7
7
 
data/README.md CHANGED
@@ -57,7 +57,7 @@ You can use the mappers like this (<strong>it's runnable, try copy and pasting i
57
57
  require "rubygems"
58
58
  require "minimapper"
59
59
  require "minimapper/entity"
60
- require "minimapper/memory"
60
+ require "minimapper/mapper/memory"
61
61
 
62
62
  class User
63
63
  include Minimapper::Entity
@@ -66,7 +66,7 @@ class User
66
66
  validates :name, :presence => true
67
67
  end
68
68
 
69
- class UserMapper < Minimapper::Memory
69
+ class UserMapper < Minimapper::Mapper::Memory
70
70
  end
71
71
 
72
72
  ## Creating
@@ -89,7 +89,7 @@ old_id = user.id
89
89
  user_mapper.delete(user)
90
90
  p user.id # => nil
91
91
  p user_mapper.find_by_id(old_id) # => nil
92
- # user_mapper.find(old_id) # raises Minimapper::Common::CanNotFindEntity
92
+ # user_mapper.find(old_id) # raises Minimapper::Mapper::CanNotFindEntity
93
93
  # user_mapper.delete_all
94
94
  # user_mapper.delete_by_id(1)
95
95
 
@@ -120,7 +120,7 @@ This is not directly runnable like the previous example, it requires ActiveRecor
120
120
  When you do need to use an ORM like ActiveRecord however, it now has the same API as your in-memory persistence (thanks to the [shared tests](https://github.com/joakimk/minimapper/blob/master/spec/support/shared_examples/mapper.rb) which define how a mapper is supposed to behave).
121
121
 
122
122
  ``` ruby
123
- require "minimapper/ar"
123
+ require "minimapper/mapper/ar"
124
124
 
125
125
  module AR
126
126
  class UserMapper < Minimapper::AR
@@ -136,6 +136,24 @@ mapper = AR::UserMapper.new
136
136
  mapper.create(user)
137
137
  ```
138
138
 
139
+ ### Uniqueness validations and other DB validations
140
+
141
+ Validations on uniqueness can't be implemented on the entity, because they need to access the database.
142
+
143
+ Therefore, the ActiveRecord mapper will copy over any record errors to the entity when attempting to create or update.
144
+
145
+ You would add these validations to the record itself, like:
146
+
147
+ ``` ruby
148
+ class User < ActiveRecord::Base
149
+ validates :email, :uniqueness => true
150
+ end
151
+ ```
152
+
153
+ Note that calling `valid?` on the entity will not access the database. Errors copied over from the record will remain until the next attempt to create or update.
154
+
155
+ So an entity that wouldn't be unique in the database will be `valid?` before you attempt to create it. And after you attempt to create it, the entity will not be `valid?` even after assigning a new value, until you attempt to create it again.
156
+
139
157
  ### Custom queries
140
158
 
141
159
  You can write custom queries like this:
@@ -143,7 +161,7 @@ You can write custom queries like this:
143
161
  ``` ruby
144
162
  # Memory implementation
145
163
  module Memory
146
- class ProjectMapper < Minimapper::Memory
164
+ class ProjectMapper < Minimapper::Mapper::Memory
147
165
  def waiting_for_review
148
166
  all.find_all { |p| p.waiting_for_review }.sort_by(&:id).reverse
149
167
  end
@@ -173,6 +191,19 @@ end
173
191
 
174
192
  It gets simpler to maintain if you use shared tests to test both implementations. For inspiration, see the [shared tests](https://github.com/joakimk/minimapper/blob/master/spec/support/shared_examples/mapper.rb) used to test minimapper.
175
193
 
194
+ `entity_for` returns nil for nil.
195
+
196
+ It takes an optional second argument if you want a different entity class than the mapper's:
197
+
198
+ ```
199
+ class ProjectMapper < Minimapper::AR
200
+ def owner_of(project)
201
+ owner_record = find(project).owner
202
+ entity_for(owner_record, User)
203
+ end
204
+ end
205
+ ```
206
+
176
207
  ### Typed attributes
177
208
 
178
209
  If you specify type, minimapper will attempt to convert into that type. Supported types: Integer and DateTime (:integer and :date_time).
@@ -287,7 +318,6 @@ You need mysql and postgres installed (but they do not have to be running) to be
287
318
  I won't implement anything that isn't actually used. But here are some ideas for things that might make it into minimapper someday if there is a need for it.
288
319
 
289
320
  * Provide a hook to convert attributes between entities and the backing models (when your entity attributes and db-schema isn't a one-to-one match).
290
- * Copy validation errors back from the mapper to the entity (for example if you do uniqueness validation in a backing ActiveRecord-model).
291
321
 
292
322
  ## Credits and license
293
323
 
@@ -15,6 +15,7 @@ module Minimapper
15
15
  attributes [ *opts ]
16
16
  end
17
17
 
18
+ # Compatibility with certain Rails plugins, like Traco.
18
19
  def column_names
19
20
  @entity_columns.map(&:name).map(&:to_s)
20
21
  end
@@ -15,7 +15,15 @@ module Minimapper
15
15
  end
16
16
 
17
17
  def valid?
18
- true
18
+ mapper_errors.empty?
19
+ end
20
+
21
+ def mapper_errors
22
+ @mapper_errors ||= []
23
+ end
24
+
25
+ def mapper_errors=(list)
26
+ @mapper_errors = list
19
27
  end
20
28
 
21
29
  private
@@ -1,3 +1,7 @@
1
+ # Include this in your entity models for
2
+ # Ruby on Rails conveniences, like being
3
+ # able to use them in forms.
4
+
1
5
  require "active_model"
2
6
 
3
7
  module Minimapper
@@ -7,6 +11,22 @@ module Minimapper
7
11
  klass.class_eval do
8
12
  extend ActiveModel::Naming
9
13
  include ActiveModel::Validations
14
+
15
+ # Must be later than ActiveModel::Validations so
16
+ # it can call it with super.
17
+ include ValidationsWithMapperErrors
18
+ end
19
+ end
20
+
21
+ module ValidationsWithMapperErrors
22
+ def valid?
23
+ super
24
+
25
+ mapper_errors.each do |a, v|
26
+ errors.add(a, v)
27
+ end
28
+
29
+ errors.empty?
10
30
  end
11
31
  end
12
32
 
@@ -19,7 +19,11 @@ module Minimapper
19
19
  def self.included(klass)
20
20
  klass.send(:include, Minimapper::Entity::Rails)
21
21
  klass.send(:extend, Minimapper::Entity::Attributes)
22
- klass.attributes([ :id, :integer ], [ :created_at, :date_time ], [ :updated_at, :date_time ])
22
+ klass.attributes(
23
+ [ :id, :integer ],
24
+ [ :created_at, :date_time ],
25
+ [ :updated_at, :date_time ]
26
+ )
23
27
  end
24
28
  end
25
29
  end
@@ -0,0 +1,138 @@
1
+ require "minimapper/mapper/common"
2
+
3
+ module Minimapper
4
+ module Mapper
5
+ class AR
6
+ include Common
7
+
8
+ # Create
9
+
10
+ def create(entity)
11
+ record = record_class.new
12
+ validate_record_and_copy_errors_to_entity(record, entity)
13
+
14
+ if entity.valid?
15
+ record.save!
16
+ entity.id = record.id
17
+ entity.id
18
+ else
19
+ false
20
+ end
21
+ end
22
+
23
+ # Read
24
+
25
+ def find(id)
26
+ entity_for(find_record_safely(id))
27
+ end
28
+
29
+ def find_by_id(id)
30
+ entity_for(find_record(id))
31
+ end
32
+
33
+ def all
34
+ entities_for record_class.all
35
+ end
36
+
37
+ def first
38
+ entity_for(record_class.order("id ASC").first)
39
+ end
40
+
41
+ def last
42
+ entity_for(record_class.order("id ASC").last)
43
+ end
44
+
45
+ def count
46
+ record_class.count
47
+ end
48
+
49
+ # Update
50
+
51
+ def update(entity)
52
+ record = record_for(entity)
53
+ validate_record_and_copy_errors_to_entity(record, entity)
54
+
55
+ if entity.valid?
56
+ record.save!
57
+ true
58
+ else
59
+ false
60
+ end
61
+ end
62
+
63
+ # Delete
64
+
65
+ def delete(entity)
66
+ delete_by_id(entity.id)
67
+ entity.id = nil
68
+ end
69
+
70
+ def delete_by_id(id)
71
+ find_record_safely(id).delete
72
+ end
73
+
74
+ def delete_all
75
+ record_class.delete_all
76
+ end
77
+
78
+ private
79
+
80
+ # NOTE: Don't memoize the classes or code reloading will break in rails apps.
81
+
82
+ # Will attempt to use AR:Project as the record class
83
+ # when the mapper class name is AR::ProjectMapper
84
+ def record_class
85
+ self.class.name.sub(/Mapper$/, '').constantize
86
+ end
87
+
88
+ # Will attempt to use Project as the enity class when
89
+ # the mapper class name is AR::ProjectMapper
90
+ def entity_class
91
+ ("::" + self.class.name.split('::').last.sub(/Mapper$/, '')).constantize
92
+ end
93
+
94
+ def accessible_attributes(entity)
95
+ entity.attributes.reject { |k, v| protected_attributes.include?(k.to_s) }
96
+ end
97
+
98
+ def protected_attributes
99
+ record_class.protected_attributes
100
+ end
101
+
102
+ def validate_record_and_copy_errors_to_entity(record, entity)
103
+ record.attributes = accessible_attributes(entity)
104
+ record.valid?
105
+ entity.mapper_errors = record.errors.map { |k, v| [k, v] }
106
+ end
107
+
108
+ def find_record_safely(id)
109
+ find_record(id) ||
110
+ raise(CanNotFindEntity, :id => id)
111
+ end
112
+
113
+ def find_record(id)
114
+ id && record_class.find_by_id(id)
115
+ end
116
+
117
+ def record_for(entity)
118
+ (entity.id && record_class.find_by_id(entity.id)) ||
119
+ raise(CanNotFindEntity, entity.inspect)
120
+ end
121
+
122
+ def entities_for(records)
123
+ records.map { |record| entity_for(record) }
124
+ end
125
+
126
+ def entity_for(record, klass = entity_class)
127
+ if record
128
+ entity = klass.new
129
+ entity.id = record.id
130
+ entity.attributes = record.attributes.symbolize_keys
131
+ entity
132
+ else
133
+ nil
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,9 @@
1
+ module Minimapper
2
+ module Mapper
3
+ CanNotFindEntity = Class.new(StandardError)
4
+
5
+ module Common
6
+ attr_accessor :repository
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,94 @@
1
+ require 'minimapper/mapper/common'
2
+
3
+ module Minimapper
4
+ module Mapper
5
+ class Memory
6
+ include Common
7
+
8
+ def initialize
9
+ @store = []
10
+ @last_id = 0
11
+ end
12
+
13
+ # Create
14
+ def create(entity)
15
+ if entity.valid?
16
+ entity.id = next_id
17
+ store.push(entity.dup)
18
+ last_id
19
+ else
20
+ false
21
+ end
22
+ end
23
+
24
+ # Read
25
+ def find(id)
26
+ find_internal_safely(id).dup
27
+ end
28
+
29
+ def find_by_id(id)
30
+ entity = find_internal(id)
31
+ entity && entity.dup
32
+ end
33
+
34
+ def all
35
+ store.map { |entity| entity.dup }
36
+ end
37
+
38
+ def first
39
+ store.first && store.first.dup
40
+ end
41
+
42
+ def last
43
+ store.last && store.last.dup
44
+ end
45
+
46
+ def count
47
+ all.size
48
+ end
49
+
50
+ # Update
51
+ def update(entity)
52
+ if entity.valid?
53
+ known_entity = find_internal_safely(entity.id)
54
+ known_entity.attributes = entity.attributes
55
+ true
56
+ else
57
+ false
58
+ end
59
+ end
60
+
61
+ # Delete
62
+ def delete(entity)
63
+ delete_by_id(entity.id)
64
+ entity.id = nil
65
+ end
66
+
67
+ def delete_by_id(id)
68
+ entity = find_internal_safely(id)
69
+ store.delete(entity)
70
+ end
71
+
72
+ def delete_all
73
+ store.clear
74
+ end
75
+
76
+ private
77
+
78
+ def find_internal_safely(id)
79
+ find_internal(id) ||
80
+ raise(CanNotFindEntity, :id => id)
81
+ end
82
+
83
+ def find_internal(id)
84
+ id && store.find { |e| e.id == id.to_i }
85
+ end
86
+
87
+ def next_id
88
+ @last_id += 1
89
+ end
90
+
91
+ attr_reader :store, :last_id
92
+ end
93
+ end
94
+ end
@@ -1,3 +1,3 @@
1
1
  module Minimapper
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -0,0 +1,135 @@
1
+ require "spec_helper"
2
+ require "minimapper/entity/core"
3
+ require "minimapper/mapper/ar"
4
+
5
+ class TestEntity
6
+ include Minimapper::Entity::Core
7
+ end
8
+
9
+ class TestMapper < Minimapper::Mapper::AR
10
+ private
11
+
12
+ def entity_class
13
+ TestEntity
14
+ end
15
+
16
+ def record_class
17
+ Record
18
+ end
19
+
20
+ class Record < ActiveRecord::Base
21
+ attr_protected :visible
22
+
23
+ validates :email,
24
+ :uniqueness => true,
25
+ :allow_nil => true
26
+
27
+ self.table_name = :projects
28
+ self.mass_assignment_sanitizer = :strict
29
+ end
30
+ end
31
+
32
+ describe Minimapper::Mapper::AR do
33
+ let(:mapper) { TestMapper.new }
34
+ let(:entity_class) { TestEntity }
35
+
36
+ include_examples :mapper
37
+
38
+ describe "#create" do
39
+ it "does not include protected attributes" do
40
+ # because it leads to exceptions when mass_assignment_sanitizer is set to strict
41
+ entity = build_entity(:visible => true, :name => "Joe")
42
+ mapper.create(entity)
43
+
44
+ stored_entity = mapper.find(entity.id)
45
+ stored_entity.attributes[:visible].should be_nil
46
+ stored_entity.attributes[:name].should == "Joe"
47
+
48
+ entity = TestEntity.new
49
+ entity.attributes = { :visible => true, :name => "Joe" }
50
+ TestMapper::Record.stub(:protected_attributes => [])
51
+ lambda { mapper.create(entity) }.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
52
+ end
53
+
54
+ it "copies record validation errors to entity" do
55
+ old_entity = build_entity(:email => "joe@example.com")
56
+ mapper.create(old_entity)
57
+ old_entity.mapper_errors.should == []
58
+
59
+ new_entity = build_entity(:email => "joe@example.com")
60
+ mapper.create(new_entity)
61
+ new_entity.mapper_errors.should == [ [:email, "has already been taken"] ]
62
+ end
63
+
64
+ it "can revalidate on record validation errors" do
65
+ old_entity = build_entity(:email => "joe@example.com")
66
+ mapper.create(old_entity)
67
+
68
+ new_entity = build_entity(:email => "joe@example.com")
69
+ mapper.create(new_entity)
70
+ new_entity.mapper_errors.should == [ [:email, "has already been taken"] ]
71
+
72
+ new_entity.attributes = { :email => "something.else@example.com" }
73
+ mapper.create(new_entity)
74
+ new_entity.should be_valid
75
+ end
76
+
77
+ def build_entity(attributes)
78
+ entity = TestEntity.new
79
+ entity.attributes = attributes
80
+ entity
81
+ end
82
+ end
83
+
84
+ describe "#update" do
85
+ it "does not include protected attributes" do
86
+ entity = TestEntity.new
87
+ mapper.create(entity)
88
+
89
+ entity.attributes = { :visible => true, :name => "Joe" }
90
+ mapper.update(entity)
91
+ stored_entity = mapper.find(entity.id)
92
+ stored_entity.attributes[:visible].should be_nil
93
+ stored_entity.attributes[:name].should == "Joe"
94
+
95
+ TestMapper::Record.stub(:protected_attributes => [])
96
+ lambda { mapper.update(entity) }.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
97
+ end
98
+
99
+ it "copies record validation errors to entity" do
100
+ old_entity = build_entity(:email => "joe@example.com")
101
+ mapper.create(old_entity)
102
+
103
+ new_entity = TestEntity.new
104
+ mapper.create(new_entity)
105
+ new_entity.mapper_errors.should == []
106
+
107
+ new_entity.attributes = { :email => "joe@example.com" }
108
+ mapper.update(new_entity)
109
+ new_entity.mapper_errors.should == [ [:email, "has already been taken"] ]
110
+ end
111
+
112
+ it "can revalidate on record validation errors" do
113
+ old_entity = build_entity(:email => "joe@example.com")
114
+ mapper.create(old_entity)
115
+
116
+ new_entity = TestEntity.new
117
+ mapper.create(new_entity)
118
+ new_entity.mapper_errors.should == []
119
+
120
+ new_entity.attributes = { :email => "joe@example.com" }
121
+ mapper.update(new_entity)
122
+ new_entity.mapper_errors.should == [ [:email, "has already been taken"] ]
123
+
124
+ new_entity.attributes = { :email => "something.else@example.com" }
125
+ mapper.update(new_entity)
126
+ new_entity.should be_valid
127
+ end
128
+
129
+ def build_entity(attributes)
130
+ entity = TestEntity.new
131
+ entity.attributes = attributes
132
+ entity
133
+ end
134
+ end
135
+ end
@@ -45,7 +45,7 @@ end
45
45
  silence_stream(STDOUT) do
46
46
  ActiveRecord::Schema.define(:version => 0) do
47
47
  create_table :projects, :force => true do |t|
48
- t.string :name
48
+ t.string :name, :email
49
49
  t.boolean :visible
50
50
  end
51
51
  end
@@ -64,7 +64,7 @@ shared_examples :mapper do
64
64
  end
65
65
 
66
66
  it "fails when an entity can not be found" do
67
- lambda { mapper.find(-1) }.should raise_error(Minimapper::Common::CanNotFindEntity)
67
+ lambda { mapper.find(-1) }.should raise_error(Minimapper::Mapper::CanNotFindEntity)
68
68
  end
69
69
  end
70
70
 
@@ -199,14 +199,14 @@ shared_examples :mapper do
199
199
 
200
200
  it "fails when the entity does not have an id" do
201
201
  entity = build_valid_entity
202
- lambda { mapper.update(entity) }.should raise_error(Minimapper::Common::CanNotFindEntity)
202
+ lambda { mapper.update(entity) }.should raise_error(Minimapper::Mapper::CanNotFindEntity)
203
203
  end
204
204
 
205
205
  it "fails when the entity no longer exists" do
206
206
  entity = build_valid_entity
207
207
  mapper.create(entity)
208
208
  mapper.delete_all
209
- lambda { mapper.update(entity) }.should raise_error(Minimapper::Common::CanNotFindEntity)
209
+ lambda { mapper.update(entity) }.should raise_error(Minimapper::Mapper::CanNotFindEntity)
210
210
  end
211
211
  end
212
212
 
@@ -231,13 +231,13 @@ shared_examples :mapper do
231
231
 
232
232
  it "fails when the entity does not have an id" do
233
233
  entity = entity_class.new
234
- lambda { mapper.delete(entity) }.should raise_error(Minimapper::Common::CanNotFindEntity)
234
+ lambda { mapper.delete(entity) }.should raise_error(Minimapper::Mapper::CanNotFindEntity)
235
235
  end
236
236
 
237
237
  it "fails when the entity can not be found" do
238
238
  entity = entity_class.new
239
239
  entity.id = -1
240
- lambda { mapper.delete(entity) }.should raise_error(Minimapper::Common::CanNotFindEntity)
240
+ lambda { mapper.delete(entity) }.should raise_error(Minimapper::Mapper::CanNotFindEntity)
241
241
  end
242
242
  end
243
243
 
@@ -252,7 +252,7 @@ shared_examples :mapper do
252
252
  end
253
253
 
254
254
  it "fails when an entity can not be found" do
255
- lambda { mapper.delete_by_id(-1) }.should raise_error(Minimapper::Common::CanNotFindEntity)
255
+ lambda { mapper.delete_by_id(-1) }.should raise_error(Minimapper::Mapper::CanNotFindEntity)
256
256
  end
257
257
  end
258
258
 
@@ -27,14 +27,43 @@ describe Minimapper::Entity::Core do
27
27
  entity.attributes.should == { :one => 11 }
28
28
  end
29
29
 
30
- it "returns true for valid?" do
31
- entity = BasicEntity.new
32
- entity.should be_valid
33
- end
34
-
35
30
  it "responds to id" do
36
31
  entity = BasicEntity.new
37
32
  entity.id = 10
38
33
  entity.id.should == 10
39
34
  end
35
+
36
+ describe "#mapper_errors" do
37
+ it "defaults to an empty array" do
38
+ entity = BasicEntity.new
39
+ entity.mapper_errors.should == []
40
+ end
41
+
42
+ it "can be changed" do
43
+ entity = BasicEntity.new
44
+ entity.mapper_errors = [ [:one, "bad"] ]
45
+ entity.mapper_errors.should == [ [:one, "bad"] ]
46
+ end
47
+ end
48
+
49
+ describe "#mapper_errors=" do
50
+ it "makes it invalid if present" do
51
+ entity = BasicEntity.new
52
+ entity.mapper_errors = [ [:one, "bad"] ]
53
+ entity.valid?.should be_false
54
+ end
55
+ end
56
+
57
+ describe "#valid?" do
58
+ it "is true without errors" do
59
+ entity = BasicEntity.new
60
+ entity.valid?.should be_true
61
+ end
62
+
63
+ it "is false with errors" do
64
+ entity = BasicEntity.new
65
+ entity.mapper_errors = [ [:one, "bad"] ]
66
+ entity.valid?.should be_false
67
+ end
68
+ end
40
69
  end
@@ -50,4 +50,15 @@ describe Minimapper::Entity::Rails do
50
50
  entity.name = "Joe"
51
51
  entity.should be_valid
52
52
  end
53
+
54
+ describe "#mapper_errors=" do
55
+ it "adds an error to the errors collection" do
56
+ entity = RailsEntity.new
57
+ entity.name = "Joe"
58
+ entity.should be_valid
59
+ entity.mapper_errors = [ [:name, "must be unique"] ]
60
+ entity.should_not be_valid
61
+ entity.errors[:name].should == ["must be unique"]
62
+ end
63
+ end
53
64
  end
@@ -1,11 +1,11 @@
1
- require 'minimapper/memory'
1
+ require 'minimapper/mapper/memory'
2
2
  require 'minimapper/entity/core'
3
3
 
4
4
  class BasicEntity
5
5
  include Minimapper::Entity::Core
6
6
  end
7
7
 
8
- describe Minimapper::Memory do
8
+ describe Minimapper::Mapper::Memory do
9
9
  let(:mapper) { described_class.new }
10
10
  let(:entity_class) { BasicEntity }
11
11
 
@@ -1,8 +1,8 @@
1
1
  require 'minimapper/repository'
2
- require 'minimapper/memory'
2
+ require 'minimapper/mapper/memory'
3
3
 
4
4
  module Test
5
- class ProjectMapper < Minimapper::Memory
5
+ class ProjectMapper < Minimapper::Mapper::Memory
6
6
  end
7
7
  end
8
8
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minimapper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-14 00:00:00.000000000 Z
12
+ date: 2013-03-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
- requirement: &70114985880520 !ruby/object:Gem::Requirement
16
+ requirement: &70108741422080 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70114985880520
24
+ version_requirements: *70108741422080
25
25
  description: A minimalistic way of separating your models from ORMs like ActiveRecord.
26
26
  email:
27
27
  - joakim.kolsjo@gmail.com
@@ -37,8 +37,6 @@ files:
37
37
  - README.md
38
38
  - Rakefile
39
39
  - lib/minimapper.rb
40
- - lib/minimapper/ar.rb
41
- - lib/minimapper/common.rb
42
40
  - lib/minimapper/entity.rb
43
41
  - lib/minimapper/entity/attributes.rb
44
42
  - lib/minimapper/entity/convert.rb
@@ -46,13 +44,15 @@ files:
46
44
  - lib/minimapper/entity/convert/to_integer.rb
47
45
  - lib/minimapper/entity/core.rb
48
46
  - lib/minimapper/entity/rails.rb
49
- - lib/minimapper/memory.rb
47
+ - lib/minimapper/mapper/ar.rb
48
+ - lib/minimapper/mapper/common.rb
49
+ - lib/minimapper/mapper/memory.rb
50
50
  - lib/minimapper/repository.rb
51
51
  - lib/minimapper/version.rb
52
52
  - minimapper.gemspec
53
53
  - script/ci
54
54
  - script/turbux_rspec
55
- - spec/ar_spec.rb
55
+ - spec/mapper/ar_spec.rb
56
56
  - spec/spec_helper.rb
57
57
  - spec/support/database_setup.rb
58
58
  - spec/support/shared_examples/mapper.rb
@@ -60,7 +60,7 @@ files:
60
60
  - unit/entity/core_spec.rb
61
61
  - unit/entity/rails_spec.rb
62
62
  - unit/entity_spec.rb
63
- - unit/memory_spec.rb
63
+ - unit/mapper/memory_spec.rb
64
64
  - unit/repository_spec.rb
65
65
  - unit/spec_helper.rb
66
66
  homepage: ''
@@ -77,7 +77,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
77
77
  version: '0'
78
78
  segments:
79
79
  - 0
80
- hash: 1267216802408816283
80
+ hash: 3446705768728463359
81
81
  required_rubygems_version: !ruby/object:Gem::Requirement
82
82
  none: false
83
83
  requirements:
@@ -86,7 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
86
  version: '0'
87
87
  segments:
88
88
  - 0
89
- hash: 1267216802408816283
89
+ hash: 3446705768728463359
90
90
  requirements: []
91
91
  rubyforge_project:
92
92
  rubygems_version: 1.8.5
@@ -94,7 +94,7 @@ signing_key:
94
94
  specification_version: 3
95
95
  summary: A minimalistic way of separating your models from ORMs like ActiveRecord.
96
96
  test_files:
97
- - spec/ar_spec.rb
97
+ - spec/mapper/ar_spec.rb
98
98
  - spec/spec_helper.rb
99
99
  - spec/support/database_setup.rb
100
100
  - spec/support/shared_examples/mapper.rb
data/lib/minimapper/ar.rb DELETED
@@ -1,118 +0,0 @@
1
- require "minimapper/common"
2
-
3
- module Minimapper
4
- class AR
5
- include Common
6
-
7
- # Create
8
- def create(entity)
9
- if entity.valid?
10
- entity.id = record_class.create!(accessible_attributes(entity)).id
11
- else
12
- false
13
- end
14
- end
15
-
16
- # Read
17
- def find(id)
18
- entity_for(find_record_safely(id))
19
- end
20
-
21
- def find_by_id(id)
22
- entity_for(find_record(id))
23
- end
24
-
25
- def all
26
- entities_for record_class.all
27
- end
28
-
29
- def first
30
- entity_for(record_class.order("id ASC").first)
31
- end
32
-
33
- def last
34
- entity_for(record_class.order("id ASC").last)
35
- end
36
-
37
- def count
38
- record_class.count
39
- end
40
-
41
- # Update
42
- def update(entity)
43
- if entity.valid?
44
- record_for(entity).update_attributes!(accessible_attributes(entity))
45
- true
46
- else
47
- false
48
- end
49
- end
50
-
51
- # Delete
52
- def delete(entity)
53
- delete_by_id(entity.id)
54
- entity.id = nil
55
- end
56
-
57
- def delete_by_id(id)
58
- find_record_safely(id).delete
59
- end
60
-
61
- def delete_all
62
- record_class.delete_all
63
- end
64
-
65
- private
66
-
67
- # NOTE: Don't memoize the classes or code reloading will break in rails apps.
68
-
69
- # Will attempt to use AR:Project as the record class
70
- # when the mapper class name is AR::ProjectMapper
71
- def record_class
72
- self.class.name.gsub(/Mapper/, '').constantize
73
- end
74
-
75
- # Will attempt to use Project as the enity class when
76
- # the mapper class name is AR::ProjectMapper
77
- def entity_class
78
- ("::" + self.class.name.split('::').last.gsub(/Mapper/, '')).constantize
79
- end
80
-
81
- def accessible_attributes(entity)
82
- entity.attributes.reject { |k, v| protected_attributes.include?(k.to_s) }
83
- end
84
-
85
- def protected_attributes
86
- record_class.protected_attributes
87
- end
88
-
89
- def find_record_safely(id)
90
- find_record(id) ||
91
- raise(Common::CanNotFindEntity, :id => id)
92
- end
93
-
94
- def find_record(id)
95
- id && record_class.find_by_id(id)
96
- end
97
-
98
- def record_for(entity)
99
- (entity.id && record_class.find_by_id(entity.id)) ||
100
- raise(Common::CanNotFindEntity, entity.inspect)
101
- end
102
-
103
- def entities_for(records)
104
- records.map { |record| entity_for(record) }
105
- end
106
-
107
- def entity_for(record)
108
- if record
109
- entity = entity_class.new
110
- entity.id = record.id
111
- entity.attributes = record.attributes.symbolize_keys
112
- entity
113
- else
114
- nil
115
- end
116
- end
117
- end
118
- end
@@ -1,7 +0,0 @@
1
- module Minimapper
2
- module Common
3
- CanNotFindEntity = Class.new(StandardError)
4
-
5
- attr_accessor :repository
6
- end
7
- end
@@ -1,92 +0,0 @@
1
- require 'minimapper/common'
2
-
3
- module Minimapper
4
- class Memory
5
- include Common
6
-
7
- def initialize
8
- @store = []
9
- @last_id = 0
10
- end
11
-
12
- # Create
13
- def create(entity)
14
- if entity.valid?
15
- entity.id = next_id
16
- store.push(entity.dup)
17
- last_id
18
- else
19
- false
20
- end
21
- end
22
-
23
- # Read
24
- def find(id)
25
- find_internal_safely(id).dup
26
- end
27
-
28
- def find_by_id(id)
29
- entity = find_internal(id)
30
- entity && entity.dup
31
- end
32
-
33
- def all
34
- store.map { |entity| entity.dup }
35
- end
36
-
37
- def first
38
- store.first && store.first.dup
39
- end
40
-
41
- def last
42
- store.last && store.last.dup
43
- end
44
-
45
- def count
46
- all.size
47
- end
48
-
49
- # Update
50
- def update(entity)
51
- if entity.valid?
52
- known_entity = find_internal_safely(entity.id)
53
- known_entity.attributes = entity.attributes
54
- true
55
- else
56
- false
57
- end
58
- end
59
-
60
- # Delete
61
- def delete(entity)
62
- delete_by_id(entity.id)
63
- entity.id = nil
64
- end
65
-
66
- def delete_by_id(id)
67
- entity = find_internal_safely(id)
68
- store.delete(entity)
69
- end
70
-
71
- def delete_all
72
- store.clear
73
- end
74
-
75
- private
76
-
77
- def find_internal_safely(id)
78
- find_internal(id) ||
79
- raise(Common::CanNotFindEntity, :id => id)
80
- end
81
-
82
- def find_internal(id)
83
- id && store.find { |e| e.id == id.to_i }
84
- end
85
-
86
- def next_id
87
- @last_id += 1
88
- end
89
-
90
- attr_reader :store, :last_id
91
- end
92
- end
data/spec/ar_spec.rb DELETED
@@ -1,67 +0,0 @@
1
- require "spec_helper"
2
- require "minimapper/entity/core"
3
- require "minimapper/ar"
4
-
5
- class TestEntity
6
- include Minimapper::Entity::Core
7
- end
8
-
9
- class TestMapper < Minimapper::AR
10
- private
11
-
12
- def entity_class
13
- TestEntity
14
- end
15
-
16
- def record_class
17
- Record
18
- end
19
-
20
- class Record < ActiveRecord::Base
21
- attr_protected :visible
22
-
23
- self.table_name = :projects
24
- self.mass_assignment_sanitizer = :strict
25
- end
26
- end
27
-
28
- describe Minimapper::AR do
29
- let(:mapper) { TestMapper.new }
30
- let(:entity_class) { TestEntity }
31
-
32
- include_examples :mapper
33
-
34
- describe "#create" do
35
- it "does not include protected attributes" do
36
- # because it leads to exceptions when mass_assignment_sanitizer is set to strict
37
- entity = TestEntity.new
38
- entity.attributes = { :visible => true, :name => "Joe" }
39
- mapper.create(entity)
40
-
41
- stored_entity = mapper.find(entity.id)
42
- stored_entity.attributes[:visible].should be_nil
43
- stored_entity.attributes[:name].should == "Joe"
44
-
45
- entity = TestEntity.new
46
- entity.attributes = { :visible => true, :name => "Joe" }
47
- TestMapper::Record.stub(:protected_attributes => [])
48
- lambda { mapper.create(entity) }.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
49
- end
50
- end
51
-
52
- describe "#update" do
53
- it "does not include protected attributes" do
54
- entity = TestEntity.new
55
- mapper.create(entity)
56
-
57
- entity.attributes = { :visible => true, :name => "Joe" }
58
- mapper.update(entity)
59
- stored_entity = mapper.find(entity.id)
60
- stored_entity.attributes[:visible].should be_nil
61
- stored_entity.attributes[:name].should == "Joe"
62
-
63
- TestMapper::Record.stub(:protected_attributes => [])
64
- lambda { mapper.update(entity) }.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
65
- end
66
- end
67
- end