perpetuity 0.7.3 → 1.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/Gemfile +3 -0
  4. data/README.md +18 -8
  5. data/lib/perpetuity.rb +4 -4
  6. data/lib/perpetuity/attribute.rb +9 -0
  7. data/lib/perpetuity/attribute_set.rb +4 -6
  8. data/lib/perpetuity/data_injectable.rb +1 -1
  9. data/lib/perpetuity/duplicator.rb +27 -0
  10. data/lib/perpetuity/mapper.rb +9 -14
  11. data/lib/perpetuity/version.rb +1 -1
  12. data/perpetuity.gemspec +0 -1
  13. data/spec/integration/associations_spec.rb +2 -6
  14. data/spec/integration/indexing_spec.rb +1 -1
  15. data/spec/integration/persistence_spec.rb +3 -2
  16. data/spec/integration/retrieval_spec.rb +5 -13
  17. data/spec/perpetuity/attribute_set_spec.rb +1 -1
  18. data/spec/perpetuity/attribute_spec.rb +6 -1
  19. data/spec/perpetuity/duplicator_spec.rb +35 -0
  20. data/spec/perpetuity/mapper_spec.rb +21 -2
  21. data/spec/perpetuity_spec.rb +2 -2
  22. data/spec/spec_helper.rb +8 -2
  23. data/spec/support/test_classes.rb +22 -26
  24. data/spec/support/test_classes/topic.rb +5 -0
  25. data/spec/support/test_classes/user.rb +1 -1
  26. metadata +7 -55
  27. data/lib/perpetuity/mongodb.rb +0 -230
  28. data/lib/perpetuity/mongodb/index.rb +0 -52
  29. data/lib/perpetuity/mongodb/nil_query.rb +0 -11
  30. data/lib/perpetuity/mongodb/query.rb +0 -33
  31. data/lib/perpetuity/mongodb/query_attribute.rb +0 -66
  32. data/lib/perpetuity/mongodb/query_expression.rb +0 -94
  33. data/lib/perpetuity/mongodb/query_intersection.rb +0 -16
  34. data/lib/perpetuity/mongodb/query_union.rb +0 -16
  35. data/lib/perpetuity/mongodb/serializer.rb +0 -174
  36. data/lib/perpetuity/validations.rb +0 -1
  37. data/lib/perpetuity/validations/length.rb +0 -36
  38. data/lib/perpetuity/validations/presence.rb +0 -14
  39. data/lib/perpetuity/validations/validation_set.rb +0 -28
  40. data/spec/integration/mongodb_spec.rb +0 -218
  41. data/spec/integration/validations_spec.rb +0 -17
  42. data/spec/perpetuity/mongodb/index_spec.rb +0 -44
  43. data/spec/perpetuity/mongodb/query_attribute_spec.rb +0 -58
  44. data/spec/perpetuity/mongodb/query_expression_spec.rb +0 -67
  45. data/spec/perpetuity/mongodb/query_intersection_spec.rb +0 -16
  46. data/spec/perpetuity/mongodb/query_spec.rb +0 -79
  47. data/spec/perpetuity/mongodb/query_union_spec.rb +0 -16
  48. data/spec/perpetuity/mongodb/serializer_spec.rb +0 -212
  49. data/spec/perpetuity/validations/length_spec.rb +0 -53
  50. data/spec/perpetuity/validations/presence_spec.rb +0 -30
  51. data/spec/perpetuity/validations_spec.rb +0 -87
@@ -47,8 +47,7 @@ module Perpetuity
47
47
  obj.instance_variable_set '@my_attribute', 'foo'
48
48
  data_source.should_receive(:can_serialize?).with('foo') { true }
49
49
  data_source.should_receive(:insert)
50
- .with(Object,
51
- [{ 'my_attribute' => 'foo' }])
50
+ .with(Object, [{ 'my_attribute' => 'foo' }], mapper.attribute_set)
52
51
  .and_return(['bar'])
53
52
 
54
53
  mapper.insert(obj).should be == 'bar'
@@ -93,6 +92,9 @@ module Perpetuity
93
92
  it 'caches results' do
94
93
  mapper.give_id_to returned_object, 1
95
94
  criteria = data_source.query { |o| o.id == 1 }
95
+ duplicate = returned_object.dup
96
+ duplicate.stub(class: returned_object.class)
97
+ returned_object.stub(dup: duplicate)
96
98
  data_source.should_receive(:retrieve)
97
99
  .with(Object, criteria, options) { [returned_object] }
98
100
  .once
@@ -177,5 +179,22 @@ module Perpetuity
177
179
  end
178
180
  end
179
181
  end
182
+
183
+ describe 'setting the id manually' do
184
+ context 'when setting the type' do
185
+ it 'adds the attribute to the attribute set' do
186
+ mapper_class.id(String) { 1.to_s }
187
+ id_attr = mapper_class.attribute_set[:id]
188
+ id_attr.type.should be String
189
+ end
190
+ end
191
+
192
+ context 'when not setting the type' do
193
+ it 'does not add the attribute' do
194
+ mapper_class.id { 1.to_s }
195
+ mapper_class.attribute_set[:id].should be_nil
196
+ end
197
+ end
198
+ end
180
199
  end
181
200
  end
@@ -10,7 +10,7 @@ describe Perpetuity do
10
10
 
11
11
  it 'provides a DSL within the generated mapper' do
12
12
  Perpetuity.generate_mapper_for Object do
13
- id { object_id + 1 }
13
+ id(Integer) { object_id + 1 }
14
14
  attribute :object_id
15
15
  end
16
16
 
@@ -18,7 +18,7 @@ describe Perpetuity do
18
18
  object = Object.new
19
19
  mapper.insert object
20
20
  mapper.id_for(object).should be == object.object_id + 1
21
- mapper.attributes.should eq [:object_id]
21
+ mapper.attributes.should include :object_id
22
22
  end
23
23
  end
24
24
 
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,9 @@
1
- require 'perpetuity'
1
+ require 'bundler/setup'
2
2
 
3
- Perpetuity.data_source :mongodb, 'perpetuity_gem_test'
3
+ if ENV['PERPETUITY_ADAPTER'] == 'postgres'
4
+ require 'perpetuity/postgres'
5
+ Perpetuity.data_source :postgres, 'perpetuity_gem_test', user: ENV['USER'], password: nil
6
+ else
7
+ require 'perpetuity/mongodb'
8
+ Perpetuity.data_source :mongodb, 'perpetuity_gem_test'
9
+ end
@@ -4,19 +4,19 @@ end
4
4
 
5
5
  class UserMapper < Perpetuity::Mapper
6
6
  map User
7
- attribute :name
7
+ attribute :name, type: String
8
8
  end
9
9
 
10
10
  Perpetuity.generate_mapper_for Article do
11
- attribute :title
12
- attribute :body
13
- attribute :author
14
- attribute :comments, embedded: true
15
- attribute :published_at
16
- attribute :views
11
+ attribute :title, type: String
12
+ attribute :body, type: String
13
+ attribute :author, type: User
14
+ attribute :comments, type: Array, embedded: true
15
+ attribute :published_at, type: Time
16
+ attribute :views, type: Integer
17
17
 
18
18
  def published
19
- select { |article| (article.published_at.not_equal? nil) &
19
+ select { |article| (article.published_at != nil) &
20
20
  (article.published_at < Time.now) }
21
21
  end
22
22
 
@@ -27,40 +27,36 @@ Perpetuity.generate_mapper_for Article do
27
27
  end
28
28
 
29
29
  Perpetuity.generate_mapper_for(Comment) do
30
- attribute :body
31
- attribute :author
30
+ attribute :body, type: String
31
+ attribute :author, type: User
32
32
  end
33
33
 
34
34
  Perpetuity.generate_mapper_for Book do
35
- id { title.gsub(/\W+/, '-').downcase }
36
- attribute :title
37
- attribute :authors
35
+ id(String) { title.gsub(/\W+/, '-').downcase }
36
+ attribute :title, type: String
37
+ attribute :authors, type: Array
38
38
  end
39
39
 
40
40
  Perpetuity.generate_mapper_for Message do
41
- attribute :text
41
+ attribute :text, type: String
42
42
  end
43
43
 
44
44
  Perpetuity.generate_mapper_for(Topic) do
45
- attribute :title
46
- attribute :creator
45
+ attribute :title, type: String
46
+ attribute :creator, type: User
47
47
  end
48
48
 
49
49
  Perpetuity.generate_mapper_for(Car) do
50
- attribute :make
51
- attribute :model
52
- attribute :seats
53
-
54
- validate do
55
- present :make
56
- end
50
+ attribute :make, type: String
51
+ attribute :model, type: String
52
+ attribute :seats, type: Integer
57
53
  end
58
54
 
59
55
  Perpetuity.generate_mapper_for CRM::Person do
60
- attribute :name
56
+ attribute :name, type: String
61
57
  end
62
58
 
63
59
  Perpetuity.generate_mapper_for GenericObject do
64
- attribute :referenced_attribute
65
- attribute :embedded_attribute, embedded: true
60
+ attribute :referenced_attribute, type: Object
61
+ attribute :embedded_attribute, type: Object, embedded: true
66
62
  end
@@ -1,3 +1,8 @@
1
1
  class Topic
2
2
  attr_accessor :title, :creator
3
+
4
+ def initialize title, creator
5
+ @title = title
6
+ @creator = creator
7
+ end
3
8
  end
@@ -5,6 +5,6 @@ class User
5
5
  end
6
6
 
7
7
  def == other
8
- name == other.name
8
+ other.is_a?(self.class) && name == other.name
9
9
  end
10
10
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: perpetuity
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.3
4
+ version: 1.0.0.beta
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jamie Gaskins
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-09-16 00:00:00.000000000 Z
11
+ date: 2013-12-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -38,20 +38,6 @@ dependencies:
38
38
  - - ~>
39
39
  - !ruby/object:Gem::Version
40
40
  version: '2.13'
41
- - !ruby/object:Gem::Dependency
42
- name: moped
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - '>='
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - '>='
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
41
  description: Persistence layer for Ruby objects
56
42
  email:
57
43
  - jgaskins@gmail.com
@@ -73,61 +59,38 @@ files:
73
59
  - lib/perpetuity/config.rb
74
60
  - lib/perpetuity/data_injectable.rb
75
61
  - lib/perpetuity/dereferencer.rb
62
+ - lib/perpetuity/duplicator.rb
76
63
  - lib/perpetuity/exceptions/duplicate_key_error.rb
77
64
  - lib/perpetuity/identity_map.rb
78
65
  - lib/perpetuity/mapper.rb
79
66
  - lib/perpetuity/mapper_registry.rb
80
- - lib/perpetuity/mongodb.rb
81
- - lib/perpetuity/mongodb/index.rb
82
- - lib/perpetuity/mongodb/nil_query.rb
83
- - lib/perpetuity/mongodb/query.rb
84
- - lib/perpetuity/mongodb/query_attribute.rb
85
- - lib/perpetuity/mongodb/query_expression.rb
86
- - lib/perpetuity/mongodb/query_intersection.rb
87
- - lib/perpetuity/mongodb/query_union.rb
88
- - lib/perpetuity/mongodb/serializer.rb
89
67
  - lib/perpetuity/rails.rb
90
68
  - lib/perpetuity/rails_model.rb
91
69
  - lib/perpetuity/reference.rb
92
70
  - lib/perpetuity/retrieval.rb
93
- - lib/perpetuity/validations.rb
94
- - lib/perpetuity/validations/length.rb
95
- - lib/perpetuity/validations/presence.rb
96
- - lib/perpetuity/validations/validation_set.rb
97
71
  - lib/perpetuity/version.rb
98
72
  - perpetuity.gemspec
99
73
  - spec/integration/associations_spec.rb
100
74
  - spec/integration/deletion_spec.rb
101
75
  - spec/integration/enumerable_spec.rb
102
76
  - spec/integration/indexing_spec.rb
103
- - spec/integration/mongodb_spec.rb
104
77
  - spec/integration/pagination_spec.rb
105
78
  - spec/integration/persistence_spec.rb
106
79
  - spec/integration/retrieval_spec.rb
107
80
  - spec/integration/serialization_spec.rb
108
81
  - spec/integration/update_spec.rb
109
- - spec/integration/validations_spec.rb
110
82
  - spec/perpetuity/attribute_set_spec.rb
111
83
  - spec/perpetuity/attribute_spec.rb
112
84
  - spec/perpetuity/config_spec.rb
113
85
  - spec/perpetuity/data_injectable_spec.rb
114
86
  - spec/perpetuity/dereferencer_spec.rb
87
+ - spec/perpetuity/duplicator_spec.rb
115
88
  - spec/perpetuity/identity_map_spec.rb
116
89
  - spec/perpetuity/mapper_registry_spec.rb
117
90
  - spec/perpetuity/mapper_spec.rb
118
- - spec/perpetuity/mongodb/index_spec.rb
119
- - spec/perpetuity/mongodb/query_attribute_spec.rb
120
- - spec/perpetuity/mongodb/query_expression_spec.rb
121
- - spec/perpetuity/mongodb/query_intersection_spec.rb
122
- - spec/perpetuity/mongodb/query_spec.rb
123
- - spec/perpetuity/mongodb/query_union_spec.rb
124
- - spec/perpetuity/mongodb/serializer_spec.rb
125
91
  - spec/perpetuity/rails_model_spec.rb
126
92
  - spec/perpetuity/reference_spec.rb
127
93
  - spec/perpetuity/retrieval_spec.rb
128
- - spec/perpetuity/validations/length_spec.rb
129
- - spec/perpetuity/validations/presence_spec.rb
130
- - spec/perpetuity/validations_spec.rb
131
94
  - spec/perpetuity_spec.rb
132
95
  - spec/spec_helper.rb
133
96
  - spec/support/stubbed_rails.rb
@@ -156,9 +119,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
156
119
  version: '0'
157
120
  required_rubygems_version: !ruby/object:Gem::Requirement
158
121
  requirements:
159
- - - '>='
122
+ - - '>'
160
123
  - !ruby/object:Gem::Version
161
- version: '0'
124
+ version: 1.3.1
162
125
  requirements: []
163
126
  rubyforge_project:
164
127
  rubygems_version: 2.0.6
@@ -170,34 +133,23 @@ test_files:
170
133
  - spec/integration/deletion_spec.rb
171
134
  - spec/integration/enumerable_spec.rb
172
135
  - spec/integration/indexing_spec.rb
173
- - spec/integration/mongodb_spec.rb
174
136
  - spec/integration/pagination_spec.rb
175
137
  - spec/integration/persistence_spec.rb
176
138
  - spec/integration/retrieval_spec.rb
177
139
  - spec/integration/serialization_spec.rb
178
140
  - spec/integration/update_spec.rb
179
- - spec/integration/validations_spec.rb
180
141
  - spec/perpetuity/attribute_set_spec.rb
181
142
  - spec/perpetuity/attribute_spec.rb
182
143
  - spec/perpetuity/config_spec.rb
183
144
  - spec/perpetuity/data_injectable_spec.rb
184
145
  - spec/perpetuity/dereferencer_spec.rb
146
+ - spec/perpetuity/duplicator_spec.rb
185
147
  - spec/perpetuity/identity_map_spec.rb
186
148
  - spec/perpetuity/mapper_registry_spec.rb
187
149
  - spec/perpetuity/mapper_spec.rb
188
- - spec/perpetuity/mongodb/index_spec.rb
189
- - spec/perpetuity/mongodb/query_attribute_spec.rb
190
- - spec/perpetuity/mongodb/query_expression_spec.rb
191
- - spec/perpetuity/mongodb/query_intersection_spec.rb
192
- - spec/perpetuity/mongodb/query_spec.rb
193
- - spec/perpetuity/mongodb/query_union_spec.rb
194
- - spec/perpetuity/mongodb/serializer_spec.rb
195
150
  - spec/perpetuity/rails_model_spec.rb
196
151
  - spec/perpetuity/reference_spec.rb
197
152
  - spec/perpetuity/retrieval_spec.rb
198
- - spec/perpetuity/validations/length_spec.rb
199
- - spec/perpetuity/validations/presence_spec.rb
200
- - spec/perpetuity/validations_spec.rb
201
153
  - spec/perpetuity_spec.rb
202
154
  - spec/spec_helper.rb
203
155
  - spec/support/stubbed_rails.rb
@@ -1,230 +0,0 @@
1
- require 'moped'
2
- require 'perpetuity/mongodb/query'
3
- require 'perpetuity/mongodb/nil_query'
4
- require 'perpetuity/mongodb/index'
5
- require 'perpetuity/mongodb/serializer'
6
- require 'set'
7
- require 'perpetuity/exceptions/duplicate_key_error'
8
- require 'perpetuity/attribute'
9
-
10
- module Perpetuity
11
- class MongoDB
12
- attr_accessor :host, :port, :db, :pool_size, :username, :password
13
-
14
- def initialize options
15
- @host = options.fetch(:host, 'localhost')
16
- @port = options.fetch(:port, 27017)
17
- @db = options.fetch(:db)
18
- @pool_size = options.fetch(:pool_size, 5)
19
- @username = options[:username]
20
- @password = options[:password]
21
- @session = nil
22
- @indexes = Hash.new { |hash, key| hash[key] = active_indexes(key) }
23
- @connected = false
24
- end
25
-
26
- def session
27
- @session ||= Moped::Session.new(["#{host}:#{port}"]).with(safe: true)
28
- end
29
-
30
- def connect
31
- session.login(@username, @password) if @username and @password
32
- @connected = true
33
- session
34
- end
35
-
36
- def connected?
37
- !!@connected
38
- end
39
-
40
- def database
41
- session.use db
42
- connect unless connected?
43
- session
44
- end
45
-
46
- def collection klass
47
- database[klass.to_s]
48
- end
49
-
50
- def insert klass, objects
51
- if objects.is_a? Array
52
- objects.each do |object|
53
- object[:_id] = object.delete(:id) || Moped::BSON::ObjectId.new
54
- end
55
-
56
- collection(klass).insert objects
57
- objects.map { |object| object[:_id] }
58
- else
59
- insert(klass, [objects]).first
60
- end
61
-
62
- rescue Moped::Errors::OperationFailure => e
63
- if e.message =~ /duplicate key/
64
- e.message =~ /\$(\w+)_\d.*dup key: { : (.*) }/
65
- key = $1
66
- value = $2.gsub("\\\"", "\"")
67
- raise DuplicateKeyError, "Tried to insert #{klass} with duplicate unique index: #{key} => #{value}"
68
- end
69
- end
70
-
71
- def count klass, criteria=nil_query, &block
72
- q = block_given? ? query(&block).to_db : criteria.to_db
73
- collection(klass).find(q).count
74
- end
75
-
76
- def delete_all klass
77
- collection(klass.to_s).find.remove_all
78
- end
79
-
80
- def first klass
81
- document = collection(klass.to_s).find.limit(1).first
82
- document[:id] = document.delete("_id")
83
-
84
- document
85
- end
86
-
87
- def retrieve klass, criteria, options = {}
88
- # MongoDB uses '_id' as its ID field.
89
- criteria = to_bson_id(criteria.to_db)
90
-
91
- skipped = options.fetch(:skip) { 0 }
92
-
93
- query = collection(klass.to_s)
94
- .find(criteria)
95
- .skip(skipped)
96
- .limit(options[:limit])
97
-
98
- sort(query, options).map do |document|
99
- document[:id] = document.delete("_id")
100
- document
101
- end
102
- end
103
-
104
- def increment klass, id, attribute, count=1
105
- find(klass, id).update '$inc' => { attribute => count }
106
- end
107
-
108
- def find klass, id
109
- collection(klass).find(to_bson_id(_id: id))
110
- end
111
-
112
- def to_bson_id criteria
113
- criteria = criteria.dup
114
-
115
- # Check for both string and symbol ID in criteria
116
- if criteria.has_key?('id')
117
- criteria['_id'] = Moped::BSON::ObjectId(criteria['id']) rescue criteria['id']
118
- criteria.delete 'id'
119
- end
120
-
121
- if criteria.has_key?(:id)
122
- criteria[:_id] = Moped::BSON::ObjectId(criteria[:id]) rescue criteria[:id]
123
- criteria.delete :id
124
- end
125
-
126
- criteria
127
- end
128
-
129
- def sort query, options
130
- return query unless options[:attribute] &&
131
- options[:direction]
132
-
133
- sort_orders = { ascending: 1, descending: -1 }
134
- sort_field = options[:attribute]
135
- sort_direction = options[:direction]
136
- sort_criteria = { sort_field => sort_orders[sort_direction] }
137
- query.sort(sort_criteria)
138
- end
139
-
140
- def all klass
141
- retrieve klass, nil_query, {}
142
- end
143
-
144
- def delete id, klass
145
- collection(klass.to_s).find("_id" => id).remove
146
- end
147
-
148
- def update klass, id, new_data
149
- find(klass, id).update('$set' => new_data)
150
- end
151
-
152
- def can_serialize? value
153
- serializable_types.include? value.class
154
- end
155
-
156
- def drop_collection to_be_dropped
157
- collection(to_be_dropped.to_s).drop
158
- end
159
-
160
- def query &block
161
- Query.new(&block)
162
- end
163
-
164
- def nil_query
165
- NilQuery.new
166
- end
167
-
168
- def negate_query &block
169
- Query.new(&block).negate
170
- end
171
-
172
- def index klass, attribute, options={}
173
- @indexes[klass] ||= Set.new
174
-
175
- index = Index.new(klass, attribute, options)
176
- @indexes[klass] << index
177
- index
178
- end
179
-
180
- def indexes klass
181
- @indexes[klass]
182
- end
183
-
184
- def active_indexes klass
185
- collection(klass).indexes.map do |index|
186
- key = index['key'].keys.first
187
- direction = index['key'][key]
188
- unique = index['unique']
189
- Index.new(klass, Attribute.new(key), order: Index::KEY_ORDERS[direction], unique: unique)
190
- end.to_set
191
- end
192
-
193
- def activate_index! index
194
- attribute = index.attribute.to_s
195
- order = index.order == :ascending ? 1 : -1
196
- unique = index.unique?
197
-
198
- collection(index.collection).indexes.create({attribute => order}, unique: unique)
199
- index.activate!
200
- end
201
-
202
- def remove_index index
203
- coll = collection(index.collection)
204
- db_indexes = coll.indexes.select do |db_index|
205
- db_index['name'] =~ /\A#{index.attribute}/
206
- end.map { |idx| idx['key'] }
207
-
208
- if db_indexes.any?
209
- collection(index.collection).indexes.drop db_indexes.first
210
- end
211
- end
212
-
213
- def serialize object, mapper
214
- Serializer.new(mapper).serialize object
215
- end
216
-
217
- def serialize_changed_attributes object, original, mapper
218
- Serializer.new(mapper).serialize_changes object, original
219
- end
220
-
221
- def unserialize data, mapper
222
- Serializer.new(mapper).unserialize data
223
- end
224
-
225
- private
226
- def serializable_types
227
- @serializable_types ||= [NilClass, TrueClass, FalseClass, Fixnum, Float, String, Array, Hash, Time]
228
- end
229
- end
230
- end