perpetuity 0.4.2 → 0.4.3

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/.gitignore CHANGED
@@ -2,3 +2,4 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ .rbx
data/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ ## Version 0.4.3
2
+
3
+ - Made `Mapper#load_association!` more friendly. It now loads the associated objects for all objects passed in and works with arrays of referenced objects.
4
+ - `Mapper#load_association!` is also now more efficient — the N+1 queries have been optimized. The number of queries it fires off is now equal to the quantity of different classes of associated objects.
5
+ - For example, if a user can have either a `UserProfile` or an `AdminProfile` as its `profile` attribute, Perpetuity will use two queries in `#load_association!` if and only if both types of profiles are used.
6
+ - In that example, if you only query users with a `UserProfile`, only one DB query will be triggered.
7
+
8
+ New query example:
9
+
10
+ ```ruby
11
+ user_mapper = Perpetuity[User]
12
+ users = user_mapper.all.to_a
13
+ user_mapper.load_association! users, :profile
14
+ ```
15
+
16
+ Each of the users in the DB will have their profiles loaded with a single DB query per profile type and stored in their `profile` attributes.
17
+
1
18
  ## Version 0.4.2
2
19
 
3
20
  - Improved speed and stability of `Mapper#first` and `Mapper#all`
data/lib/perpetuity.rb CHANGED
@@ -3,6 +3,7 @@ require "perpetuity/retrieval"
3
3
  require "perpetuity/mongodb"
4
4
  require "perpetuity/config"
5
5
  require "perpetuity/mapper"
6
+ require "perpetuity/mapper_registry"
6
7
 
7
8
  module Perpetuity
8
9
  def self.configure &block
@@ -14,10 +15,16 @@ module Perpetuity
14
15
  end
15
16
 
16
17
  def self.generate_mapper_for klass, &block
17
- Mapper.generate_for klass, &block
18
+ mapper = Class.new(Mapper)
19
+ mapper.map klass, mapper_registry
20
+ mapper.instance_exec &block if block_given?
18
21
  end
19
22
 
20
23
  def self.[] klass
21
- MapperRegistry[klass]
24
+ mapper_registry[klass]
25
+ end
26
+
27
+ def self.mapper_registry
28
+ @mapper_registry ||= MapperRegistry.new
22
29
  end
23
30
  end
@@ -0,0 +1,24 @@
1
+ module Perpetuity
2
+ class IdentityMap
3
+ def initialize objects, attribute, mapper_registry
4
+ @map = Hash[
5
+ objects.map(&attribute)
6
+ .flatten
7
+ .group_by(&:klass)
8
+ .map { |klass, ref|
9
+ [klass,
10
+ Hash[
11
+ mapper_registry[klass].select { |object|
12
+ object.id.in ref.map(&:id).uniq
13
+ }.map { |obj| [obj.id, obj] }
14
+ ]
15
+ ]
16
+ }
17
+ ]
18
+ end
19
+
20
+ def [] klass
21
+ @map[klass]
22
+ end
23
+ end
24
+ end
@@ -2,21 +2,21 @@ require 'perpetuity/attribute_set'
2
2
  require 'perpetuity/attribute'
3
3
  require 'perpetuity/validations'
4
4
  require 'perpetuity/data_injectable'
5
- require 'perpetuity/mapper_registry'
6
5
  require 'perpetuity/serializer'
6
+ require 'perpetuity/identity_map'
7
+ require 'perpetuity/retrieval'
7
8
 
8
9
  module Perpetuity
9
10
  class Mapper
10
11
  include DataInjectable
12
+ attr_reader :mapper_registry
11
13
 
12
- def self.generate_for(klass, &block)
13
- mapper = Class.new(base_class)
14
- mapper.map klass
15
- mapper.instance_exec &block if block_given?
14
+ def initialize registry=Perpetuity.mapper_registry
15
+ @mapper_registry = registry
16
16
  end
17
17
 
18
- def self.map klass
19
- MapperRegistry[klass] = self
18
+ def self.map klass, registry=Perpetuity.mapper_registry
19
+ registry[klass] = self
20
20
  @mapped_class = klass
21
21
  end
22
22
 
@@ -70,11 +70,11 @@ module Perpetuity
70
70
  end
71
71
 
72
72
  def serialize object
73
- Serializer.new(self).serialize(object)
73
+ Serializer.new(self, mapper_registry).serialize(object)
74
74
  end
75
75
 
76
- def self.data_source
77
- Perpetuity.configuration.data_source
76
+ def self.data_source(configuration=Perpetuity.configuration)
77
+ configuration.data_source
78
78
  end
79
79
 
80
80
  def data_source
@@ -102,7 +102,7 @@ module Perpetuity
102
102
  end
103
103
 
104
104
  def retrieve criteria={}
105
- Perpetuity::Retrieval.new mapped_class, criteria
105
+ Perpetuity::Retrieval.new mapped_class, criteria, data_source
106
106
  end
107
107
 
108
108
  def select &block
@@ -119,11 +119,21 @@ module Perpetuity
119
119
  end
120
120
 
121
121
  def load_association! object, attribute
122
- reference = object.send(attribute)
123
- klass = reference.klass
124
- id = reference.id
125
-
126
- inject_attribute object, attribute, MapperRegistry[klass].find(id)
122
+ objects = Array(object)
123
+ identity_map = IdentityMap.new(objects, attribute, mapper_registry)
124
+
125
+ objects.each do |object|
126
+ reference = object.send(attribute)
127
+ if reference.is_a? Array
128
+ refs = reference
129
+ real_objects = refs.map { |ref| identity_map[ref.klass][ref.id] }
130
+ inject_attribute object, attribute, real_objects
131
+ else
132
+ klass = reference.klass
133
+ id = reference.id
134
+ inject_attribute object, attribute, identity_map[klass][id]
135
+ end
136
+ end
127
137
  end
128
138
 
129
139
  def self.id &block
@@ -146,19 +156,12 @@ module Perpetuity
146
156
  end
147
157
 
148
158
  def self.validate &block
149
- @validations ||= ValidationSet.new
150
-
151
159
  validations.instance_exec(&block)
152
160
  end
153
161
 
154
162
  def self.validations
155
163
  @validations ||= ValidationSet.new
156
164
  end
157
-
158
- private
159
- def self.base_class
160
- Mapper
161
- end
162
165
  end
163
166
  end
164
167
 
@@ -1,16 +1,18 @@
1
1
  module Perpetuity
2
2
  class MapperRegistry
3
- @mappers = Hash.new { |_, klass| raise KeyError, "No mapper for #{klass}" }
3
+ def initialize
4
+ @mappers = Hash.new { |_, klass| raise KeyError, "No mapper for #{klass}" }
5
+ end
4
6
 
5
- def self.has_mapper? klass
7
+ def has_mapper? klass
6
8
  @mappers.has_key? klass
7
9
  end
8
10
 
9
- def self.[] klass
10
- @mappers[klass].new
11
+ def [] klass
12
+ @mappers[klass].new(self)
11
13
  end
12
14
 
13
- def self.[]= klass, mapper
15
+ def []= klass, mapper
14
16
  @mappers[klass] = mapper
15
17
  end
16
18
  end
@@ -60,31 +60,35 @@ module Perpetuity
60
60
  end
61
61
 
62
62
  def retrieve klass, criteria, options = {}
63
- objects = []
64
-
65
63
  # MongoDB uses '_id' as its ID field.
66
64
  if criteria.has_key?(:id)
67
65
  if criteria[:id].is_a? String
68
- criteria = { _id: BSON::ObjectId.from_string(criteria[:id].to_s) }
66
+ criteria = { _id: (BSON::ObjectId.from_string(criteria[:id].to_s) rescue criteria[:id]) }
69
67
  else
70
68
  criteria[:_id] = criteria.delete(:id)
71
69
  end
72
70
  end
73
71
 
74
- sort_field = options[:attribute]
75
- sort_direction = options[:direction]
76
- sort_criteria = [[sort_field, sort_direction]]
77
72
  other_options = { limit: options[:limit] }
78
73
  if options[:page]
79
74
  other_options = other_options.merge skip: (options[:page] - 1) * options[:limit]
80
75
  end
76
+ cursor = database.collection(klass.to_s).find(criteria, other_options)
81
77
 
82
- database.collection(klass.to_s).find(criteria, other_options).sort(sort_criteria).each do |document|
78
+ sort_cursor(cursor, options).map do |document|
83
79
  document[:id] = document.delete("_id")
84
- objects << document
80
+ document
85
81
  end
82
+ end
86
83
 
87
- objects
84
+ def sort_cursor cursor, options
85
+ return cursor unless options.has_key?(:attribute) &&
86
+ options.has_key?(:direction)
87
+
88
+ sort_field = options[:attribute]
89
+ sort_direction = options[:direction]
90
+ sort_criteria = [[sort_field, sort_direction]]
91
+ cursor.sort(sort_criteria)
88
92
  end
89
93
 
90
94
  def all klass
@@ -1,11 +1,10 @@
1
- require 'perpetuity/mapper_registry'
2
-
3
1
  module Perpetuity
4
2
  class Serializer
5
- attr_reader :mapper
3
+ attr_reader :mapper, :mapper_registry
6
4
 
7
- def initialize(mapper)
5
+ def initialize(mapper, mapper_registry)
8
6
  @mapper = mapper
7
+ @mapper_registry = mapper_registry
9
8
  end
10
9
 
11
10
  def attribute_for object, attribute_name
@@ -17,10 +16,10 @@ module Perpetuity
17
16
  value = attribute_for object, attrib.name
18
17
 
19
18
  serialized_value = if value.is_a? Array
20
- serialize_array(value)
19
+ serialize_array(value, attrib.embedded?)
21
20
  elsif mapper.data_source.can_serialize? value
22
21
  value
23
- elsif MapperRegistry.has_mapper?(value.class)
22
+ elsif mapper_registry.has_mapper?(value.class)
24
23
  serialize_with_foreign_mapper(value, attrib.embedded?)
25
24
  else
26
25
  if attrib.embedded?
@@ -36,7 +35,7 @@ module Perpetuity
36
35
 
37
36
  def serialize_with_foreign_mapper value, embedded = false
38
37
  if embedded
39
- value_mapper = MapperRegistry[value.class]
38
+ value_mapper = mapper_registry[value.class]
40
39
  value_serializer = Serializer.new(value_mapper)
41
40
  attr = value_serializer.serialize(value)
42
41
  attr.merge '__metadata__' => { 'class' => value.class }
@@ -50,18 +49,27 @@ module Perpetuity
50
49
  end
51
50
  end
52
51
 
53
- def serialize_array enum
52
+ def serialize_array enum, embedded
54
53
  enum.map do |value|
55
54
  if value.is_a? Array
56
55
  serialize_array(value)
57
56
  elsif mapper.data_source.can_serialize? value
58
57
  value
59
- elsif MapperRegistry.has_mapper?(value.class)
60
- {
61
- '__metadata__' => {
62
- 'class' => value.class.to_s
58
+ elsif mapper_registry.has_mapper?(value.class)
59
+ if embedded
60
+ {
61
+ '__metadata__' => {
62
+ 'class' => value.class.to_s
63
+ }
64
+ }.merge mapper_registry[value.class].serialize(value)
65
+ else
66
+ {
67
+ '__metadata__' => {
68
+ 'class' => value.class.to_s,
69
+ 'id' => value.id
70
+ }
63
71
  }
64
- }.merge MapperRegistry[value.class].serialize(value)
72
+ end
65
73
  else
66
74
  Marshal.dump(value)
67
75
  end
@@ -1,5 +1,6 @@
1
1
  require 'perpetuity/validations/length'
2
2
  require 'perpetuity/validations/presence'
3
+ require 'set'
3
4
 
4
5
  module Perpetuity
5
6
  class ValidationSet < Set
@@ -1,3 +1,3 @@
1
1
  module Perpetuity
2
- VERSION = "0.4.2"
2
+ VERSION = "0.4.3"
3
3
  end
@@ -2,14 +2,15 @@ require 'perpetuity/mapper_registry'
2
2
 
3
3
  module Perpetuity
4
4
  describe MapperRegistry do
5
- subject { described_class }
6
- let(:mapper) { Class.new }
5
+ let(:registry) { MapperRegistry.new }
6
+ let(:mapper) { Class.new { def initialize(map_reg); end } }
7
+ subject { registry }
7
8
 
8
- before { MapperRegistry[Object] = mapper }
9
+ before { registry[Object] = mapper }
9
10
 
10
11
  it { should have_mapper Object }
11
12
  it 'maps classes to instances of their mappers' do
12
- MapperRegistry[Object].should be_a mapper
13
+ registry[Object].should be_a mapper
13
14
  end
14
15
  end
15
16
  end
@@ -1,25 +1,32 @@
1
+ require 'perpetuity/mapper_registry'
1
2
  require 'perpetuity/mapper'
2
3
 
3
4
  module Perpetuity
4
5
  describe Mapper do
6
+ let(:registry) { MapperRegistry.new}
5
7
  let(:mapper_class) { Class.new(Mapper) }
6
- let(:mapper) { mapper_class.new }
8
+ let(:mapper) { mapper_class.new(registry) }
7
9
  subject { mapper }
8
10
 
9
11
  it { should be_a Mapper }
10
12
 
11
13
  it 'has correct attributes' do
12
- Class.new(Mapper) { attribute :name }.attributes.should eq [:name]
14
+ mapper_class.attribute :name
15
+ mapper_class.attributes.should eq [:name]
13
16
  end
14
17
 
15
18
  it 'returns an empty attribute list when no attributes have been assigned' do
16
- Mapper.attributes.should be_empty
19
+ mapper_class.attributes.should be_empty
17
20
  end
18
21
 
19
22
  it 'can have embedded attributes' do
20
- mapper_with_embedded_attrs = Class.new(Mapper)
21
- mapper_with_embedded_attrs.attribute :comments, embedded: true
22
- mapper_with_embedded_attrs.attribute_set[:comments].should be_embedded
23
+ mapper_class.attribute :comments, embedded: true
24
+ mapper_class.attribute_set[:comments].should be_embedded
25
+ end
26
+
27
+ it 'registers itself with the mapper registry' do
28
+ mapper_class.map Object, registry
29
+ registry[Object].should be_instance_of mapper_class
23
30
  end
24
31
 
25
32
  context 'with unserializable embedded attributes' do
@@ -32,7 +39,7 @@ module Perpetuity
32
39
  object = Object.new
33
40
  object.instance_variable_set '@sub_objects', [unserializable_object]
34
41
  mapper_class.attribute :sub_objects, embedded: true
35
- mapper_class.map Object
42
+ mapper_class.map Object, registry
36
43
  data_source = double(:data_source)
37
44
  mapper.stub(data_source: data_source)
38
45
  data_source.should_receive(:can_serialize?).with(unserializable_object).and_return false
@@ -41,12 +48,66 @@ module Perpetuity
41
48
  end
42
49
  end
43
50
 
44
- describe 'subclassing Mapper' do
45
- let!(:mapper_subclass) { Class.new(Mapper) { map String } }
46
- let(:mapper) { mapper_subclass.new }
51
+ describe 'talking to the data source' do
52
+ let(:data_source) { double }
53
+ before do
54
+ mapper_class.stub(data_source: data_source)
55
+ mapper_class.map Object, registry
56
+ end
57
+
58
+ specify 'mappers use the data source that the mapper class uses' do
59
+ mapper.data_source.should be data_source
60
+ end
61
+
62
+ it 'inserts objects into a data source' do
63
+ mapper_class.attribute :my_attribute
64
+ obj = Object.new
65
+ obj.instance_variable_set '@my_attribute', 'foo'
66
+ data_source.should_receive(:can_serialize?).with('foo') { true }
67
+ data_source.should_receive(:insert)
68
+ .with(Object,
69
+ { 'my_attribute' => 'foo' })
70
+ .and_return('bar')
71
+
72
+ mapper.insert(obj).should be == 'bar'
73
+ end
74
+
75
+ it 'counts objects of its mapped class in the data source' do
76
+ data_source.should_receive(:count).with(Object) { 4 }
77
+ mapper.count.should be == 4
78
+ end
79
+
80
+ it 'finds an object by ID' do
81
+ returned_object = double
82
+ criteria = { id: 1 }
83
+ options = {:attribute=>nil, :direction=>nil, :limit=>nil, :page=>nil}
84
+ data_source.should_receive(:retrieve)
85
+ .with(Object, criteria, options) { [returned_object] }
86
+
87
+ mapper.find(1).should be == returned_object
88
+ end
89
+
90
+ it 'saves an object' do
91
+ mapper_class.attribute :foo
92
+ object = Object.new
93
+ object.define_singleton_method(:id) { 1 }
94
+ object.instance_variable_set '@foo', 'bar'
95
+ data_source.should_receive(:can_serialize?).with('bar') { true }
96
+ data_source.should_receive(:update).with Object, 1, { 'foo' => 'bar' }
97
+
98
+ mapper.save object
99
+ end
100
+
101
+ it 'deletes an object from the data source' do
102
+ object = Object.new
103
+
104
+ data_source.should_receive(:delete).with object, Object
105
+ mapper.delete object
106
+ end
47
107
 
48
- it 'can explicitly map a class' do
49
- MapperRegistry[String].should be_instance_of mapper_subclass
108
+ it 'deletes all objects it manages' do
109
+ data_source.should_receive(:delete_all).with(Object)
110
+ mapper.delete_all
50
111
  end
51
112
  end
52
113
  end
@@ -1,6 +1,62 @@
1
1
  require 'perpetuity/serializer'
2
+ require 'perpetuity/mapper'
3
+ require 'perpetuity/mapper_registry'
4
+ require 'support/test_classes/book'
5
+ require 'support/test_classes/user'
2
6
 
3
7
  module Perpetuity
4
8
  describe Serializer do
9
+ let(:dave) { User.new('Dave') }
10
+ let(:andy) { User.new('Andy') }
11
+ let(:authors) { [dave, andy] }
12
+ let(:book) { Book.new('The Pragmatic Programmer', authors) }
13
+ let(:mapper_registry) { MapperRegistry.new }
14
+ let!(:book_mapper) do
15
+ registry = mapper_registry
16
+ Class.new(Perpetuity::Mapper) do
17
+ map Book, registry
18
+ attribute :title
19
+ attribute :authors
20
+ end
21
+ end
22
+ let!(:user_mapper) do
23
+ registry = mapper_registry
24
+ Class.new(Perpetuity::Mapper) do
25
+ map User, registry
26
+ attribute :name
27
+ end
28
+ end
29
+ let(:data_source) { double('Data Source') }
30
+ let(:serializer) { Serializer.new(mapper_registry[Book], mapper_registry) }
31
+
32
+ before do
33
+ dave.stub(id: 1)
34
+ andy.stub(id: 2)
35
+ user_mapper.stub(data_source: data_source)
36
+ book_mapper.stub(data_source: data_source)
37
+ end
38
+
39
+ it 'serializes an array of non-embedded attributes as references' do
40
+ data_source.should_receive(:can_serialize?).with(book.title).and_return true
41
+ data_source.should_receive(:can_serialize?).with(dave).and_return false
42
+ data_source.should_receive(:can_serialize?).with(andy).and_return false
43
+ serializer.serialize(book).should be == {
44
+ 'title' => book.title,
45
+ 'authors' => [
46
+ {
47
+ '__metadata__' => {
48
+ 'class' => 'User',
49
+ 'id' => dave.id
50
+ }
51
+ },
52
+ {
53
+ '__metadata__' => {
54
+ 'class' => 'User',
55
+ 'id' => andy.id
56
+ }
57
+ }
58
+ ]
59
+ }
60
+ end
5
61
  end
6
62
  end
@@ -3,7 +3,7 @@ require 'perpetuity'
3
3
  mongodb = Perpetuity::MongoDB.new db: 'perpetuity_gem_test'
4
4
  Perpetuity.configure { data_source mongodb }
5
5
 
6
- require 'test_classes'
6
+ require 'support/test_classes'
7
7
 
8
8
  describe Perpetuity do
9
9
  describe 'mapper generation' do
@@ -265,12 +265,43 @@ describe Perpetuity do
265
265
  its(:id) { should be == user.id }
266
266
  end
267
267
 
268
- it 'can retrieve associated objects' do
268
+ it 'can retrieve a one-to-one association' do
269
269
  retrieved_topic = topic_mapper.find(topic.id)
270
270
 
271
271
  topic_mapper.load_association! retrieved_topic, :creator
272
272
  retrieved_topic.creator.name.should eq 'Flump'
273
273
  end
274
+
275
+ describe 'associations with many objects' do
276
+ let(:pragprogs) { [User.new('Dave'), User.new('Andy')] }
277
+ let(:cuke_authors) { [User.new('Matt'), User.new('Aslak')] }
278
+ let(:pragprog_book) { Book.new("PragProg #{Time.now.to_f}", pragprogs) }
279
+ let(:cuke_book) { Book.new("Cucumber Book #{Time.now.to_f}", cuke_authors) }
280
+ let(:book_mapper) { Perpetuity[Book] }
281
+
282
+ before do
283
+ pragprogs.each { |author| Perpetuity[User].insert author }
284
+ book_mapper.insert pragprog_book
285
+ end
286
+
287
+ it 'can retrieve a one-to-many association' do
288
+ persisted_book = book_mapper.find(pragprog_book.id)
289
+ book_mapper.load_association! persisted_book, :authors
290
+
291
+ persisted_book.authors.first.name.should be == 'Dave'
292
+ persisted_book.authors.last.name.should be == 'Andy'
293
+ end
294
+
295
+ it 'can retrieve a many-to-many association' do
296
+ cuke_authors.each { |author| Perpetuity[User].insert author }
297
+ book_mapper.insert cuke_book
298
+ book_ids = [pragprog_book, cuke_book].map(&:id)
299
+
300
+ books = book_mapper.select { |book| book.id.in book_ids }.to_a
301
+ book_mapper.load_association! books, :authors
302
+ books.map(&:authors).flatten.map(&:name).should include *%w(Dave Andy Matt Aslak)
303
+ end
304
+ end
274
305
  end
275
306
 
276
307
  describe 'updating' do
@@ -1,32 +1,17 @@
1
- class User
2
- attr_accessor :name
3
- def initialize name="Foo"
4
- @name = name
5
- end
1
+ %w( user article comment book message topic car ).each do |file|
2
+ require "support/test_classes/#{file}"
6
3
  end
7
4
 
8
5
  Perpetuity.generate_mapper_for User do
9
6
  attribute :name
10
7
  end
11
8
 
12
- class Article
13
- attr_accessor :title, :body, :author, :comments, :published_at, :views
14
- def initialize title="Title", body="Body", author=nil, published_at=Time.now, views=0
15
- @title = title
16
- @body = body
17
- @author = author
18
- @comments = []
19
- @published_at = published_at
20
- @views = views
21
- end
22
- end
23
-
24
9
  class ArticleMapper < Perpetuity::Mapper
25
10
  map Article
26
11
  attribute :title
27
12
  attribute :body
28
13
  attribute :author
29
- attribute :comments
14
+ attribute :comments, embedded: true
30
15
  attribute :published_at
31
16
  attribute :views
32
17
 
@@ -41,62 +26,26 @@ class ArticleMapper < Perpetuity::Mapper
41
26
  end
42
27
  end
43
28
 
44
- class Comment
45
- attr_accessor :body, :author
46
- def initialize body='Body', author=nil
47
- @body = body
48
- @author = author
49
- end
50
- end
51
-
52
29
  Perpetuity.generate_mapper_for(Comment) do
53
30
  attribute :body
54
31
  attribute :author
55
32
  end
56
33
 
57
- class Book
58
- attr_accessor :title
59
- def initialize title="Foo Bar"
60
- @title = title
61
- end
62
- end
63
-
64
34
  Perpetuity.generate_mapper_for Book do
65
35
  id { title.gsub(/\W+/, '-').downcase }
66
36
  attribute :title
67
- end
68
-
69
- class Message
70
- def initialize text="My Message!"
71
- self.text = text
72
- end
73
-
74
- def text
75
- @text.reverse
76
- end
77
-
78
- def text= new_text
79
- @text = new_text.reverse
80
- end
37
+ attribute :authors
81
38
  end
82
39
 
83
40
  Perpetuity.generate_mapper_for Message do
84
41
  attribute :text
85
42
  end
86
43
 
87
- class Topic
88
- attr_accessor :title, :creator
89
- end
90
-
91
44
  Perpetuity.generate_mapper_for(Topic) do
92
45
  attribute :title
93
46
  attribute :creator
94
47
  end
95
48
 
96
- class Car
97
- attr_accessor :make, :model, :seats
98
- end
99
-
100
49
  Perpetuity.generate_mapper_for(Car) do
101
50
  attribute :make
102
51
  attribute :model
@@ -0,0 +1,11 @@
1
+ class Article
2
+ attr_accessor :title, :body, :author, :comments, :published_at, :views
3
+ def initialize title="Title", body="Body", author=nil, published_at=Time.now, views=0
4
+ @title = title
5
+ @body = body
6
+ @author = author
7
+ @comments = []
8
+ @published_at = published_at
9
+ @views = views
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ class Book
2
+ attr_accessor :title, :authors
3
+ def initialize title="Foo Bar", authors=[]
4
+ @title = title
5
+ @authors = authors
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ class Car
2
+ attr_accessor :make, :model, :seats
3
+ end
@@ -0,0 +1,7 @@
1
+ class Comment
2
+ attr_accessor :body, :author
3
+ def initialize body='Body', author=nil
4
+ @body = body
5
+ @author = author
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ class Message
2
+ def initialize text="My Message!"
3
+ self.text = text
4
+ end
5
+
6
+ def text
7
+ @text.reverse
8
+ end
9
+
10
+ def text= new_text
11
+ @text = new_text.reverse
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ class Topic
2
+ attr_accessor :title, :creator
3
+ end
@@ -0,0 +1,6 @@
1
+ class User
2
+ attr_accessor :name
3
+ def initialize name="Foo"
4
+ @name = name
5
+ end
6
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: perpetuity
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-06 00:00:00.000000000 Z
12
+ date: 2013-01-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -95,6 +95,7 @@ files:
95
95
  - lib/perpetuity/attribute_set.rb
96
96
  - lib/perpetuity/config.rb
97
97
  - lib/perpetuity/data_injectable.rb
98
+ - lib/perpetuity/identity_map.rb
98
99
  - lib/perpetuity/mapper.rb
99
100
  - lib/perpetuity/mapper_registry.rb
100
101
  - lib/perpetuity/mongodb.rb
@@ -133,7 +134,14 @@ files:
133
134
  - spec/perpetuity/validations/presence_spec.rb
134
135
  - spec/perpetuity/validations_spec.rb
135
136
  - spec/perpetuity_spec.rb
136
- - spec/test_classes.rb
137
+ - spec/support/test_classes.rb
138
+ - spec/support/test_classes/article.rb
139
+ - spec/support/test_classes/book.rb
140
+ - spec/support/test_classes/car.rb
141
+ - spec/support/test_classes/comment.rb
142
+ - spec/support/test_classes/message.rb
143
+ - spec/support/test_classes/topic.rb
144
+ - spec/support/test_classes/user.rb
137
145
  homepage: https://github.com/jgaskins/perpetuity.git
138
146
  licenses: []
139
147
  post_install_message:
@@ -146,12 +154,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
146
154
  - - ! '>='
147
155
  - !ruby/object:Gem::Version
148
156
  version: '0'
157
+ segments:
158
+ - 0
159
+ hash: -4673501112155482
149
160
  required_rubygems_version: !ruby/object:Gem::Requirement
150
161
  none: false
151
162
  requirements:
152
163
  - - ! '>='
153
164
  - !ruby/object:Gem::Version
154
165
  version: '0'
166
+ segments:
167
+ - 0
168
+ hash: -4673501112155482
155
169
  requirements: []
156
170
  rubyforge_project:
157
171
  rubygems_version: 1.8.24
@@ -179,4 +193,11 @@ test_files:
179
193
  - spec/perpetuity/validations/presence_spec.rb
180
194
  - spec/perpetuity/validations_spec.rb
181
195
  - spec/perpetuity_spec.rb
182
- - spec/test_classes.rb
196
+ - spec/support/test_classes.rb
197
+ - spec/support/test_classes/article.rb
198
+ - spec/support/test_classes/book.rb
199
+ - spec/support/test_classes/car.rb
200
+ - spec/support/test_classes/comment.rb
201
+ - spec/support/test_classes/message.rb
202
+ - spec/support/test_classes/topic.rb
203
+ - spec/support/test_classes/user.rb