perpetuity 0.7.3 → 1.0.0.beta

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