perpetuity 0.4.2 → 0.4.3

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