active_repository 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 605ad373409e52ed436bd85e89904760fe97e049
4
- data.tar.gz: 39716333dfbdaa9d447bb8be5f384148d8815b4d
3
+ metadata.gz: b4e5ff5317491bb88742d8486544d1ed31a60181
4
+ data.tar.gz: 0acb8ac3cd7df689eb9c811b6b7c4b04683f46eb
5
5
  SHA512:
6
- metadata.gz: 2841548917d159c2ac40c46fc2aaea7d724cd3479a4842b6e71ce53b7ed076e3781539a5a2e54391b83cc9bae310267450ac179b9340057a767de655944e7b14
7
- data.tar.gz: cd3890fb5aa63a1cbcd90c533121710eb2b68a9839b92dcbdd0b4db0d267d7c0dce11e2e6eb625b6193faf50e64c03bb6213deac7c924fff2175cd4d8cac9381
6
+ metadata.gz: 94c813e8ebae6c3837693041bd3780b557f9e52e759b0d81e7be118568202cae8962b1d70ea2173cc80f5f5ed34bd5f75b32682a523d455fce6c052ba7794328
7
+ data.tar.gz: cc1b12d0d86d3399318bc4f0b2dd673baedf1dfbcff94f96f98a290488daf677baf5722926de82200d7a675ce7b58db083b397fb119f40d3034f9ce984492b64
data/README.md CHANGED
@@ -2,27 +2,27 @@
2
2
 
3
3
  [[![Coverage Status](https://coveralls.io/repos/efreesen/active_repository/badge.png)](https://coveralls.io/r/efreesen/active_repository)![Build Status](https://secure.travis-ci.org/efreesen/active_repository.png)](http://travis-ci.org/efreesen/active_repository)[![Dependency Status](https://gemnasium.com/efreesen/active_repository.png)](https://gemnasium.com/efreesen/active_repository) [![Code Climate](https://codeclimate.com/github/efreesen/active_repository.png)](https://codeclimate.com/github/efreesen/active_repository)
4
4
 
5
- ActiveRepository was designed so you can build your Business Models without depending on any ORM. It by default saves your data in memory using ActiveHash (https://github.com/zilkey/active_hash). Then when you decide which ORM you want to use you only have to connect ActiveRepository with it.
5
+ ActiveRepository was designed so you can build your Business Models without any dependence on persistence. It by default saves your data in memory using ActiveHash (https://github.com/zilkey/active_hash). Then when you decide which kind of persistence you want to use, all you have to do is connect ActiveRepository with it.
6
6
 
7
- Currently it only works with ActiveRecord and/or Mongoid.
7
+ Currently it only works with ActiveRecord and/or Mongoid, but it is easy to add adaptors. If you need any adaptor, just open an issue!
8
8
 
9
- It also has the advantage of letting you test directly in memory, with no need to save data on disk, which gives a great boost to your test suite speed.
9
+ With it you can always run your tests directly into memory, boosting your suite's speed. If you need to run all tests or a single spec using persistence you can do it too.
10
10
 
11
- Here are some data for comparison:
11
+ Check out our benchmark:
12
12
 
13
13
  * **ActiveRepository:**
14
- Finished in **0.63357** seconds;
15
- 78 examples, 0 failures
14
+ Finished in **2.96** seconds
15
+ 90 examples, 0 failures
16
16
 
17
17
  * **ActiveRecord:**
18
- Finished in **3.78** seconds;
19
- 78 examples, 0 failures
18
+ Finished in **6.29** seconds
19
+ 90 examples, 0 failures
20
20
 
21
21
  * **Mongoid:**
22
- Finished in **5.25** seconds;
23
- 78 examples, 0 failures
22
+ Finished in **7.01** seconds
23
+ 90 examples, 0 failures
24
24
 
25
- With ActiveRepository you can make associations with ActiveRecord, Mongoid and ActiveRepository seamlessly.
25
+ In ActiveRepository you will always work with ActiveRepository objects, so you can create relations between ActiveRecord and Mongoid seamlessly. You can even use Mongoid's Origin query format or keep with the SQL format no matter what kind of persistence you are using, we convert it for you!
26
26
 
27
27
  ## Requirements
28
28
 
@@ -46,45 +46,69 @@ Or install it yourself as:
46
46
 
47
47
  ## Usage
48
48
 
49
- To use it you should inherit ActiveRepository::Base:
49
+ To use it your class should inherit ActiveRepository::Base:
50
50
 
51
- class User < ActiveRepository::Base
51
+ class UserRepository < ActiveRepository::Base
52
+ persistence_class = User
53
+ save_in_memory = false
52
54
  end
53
55
 
54
- ActiveRepository::Base has two class attributes to help it identify where it is going to persist data
56
+ ActiveRepository::Base has two class attributes to help it identify where it is going to persist data.
55
57
 
56
- ###model_class
58
+ ###persistence_class
57
59
 
58
- This attribute is used to identify the class responsible for persisting data, it should be the ActiveRecord model or the Mongoid Document.
60
+ This attribute is used to identify the class responsible for persisting data, it should be the ActiveRecord model or the Mongoid Document. Let's say your ActiveRecord Model is called User, using the example above, all database actions would be passed to User class, and you can extract all your business logic to the UserRepository class.
59
61
 
60
62
  ###save_in_memory
61
63
 
62
- This attribute is used to persist data directly into memory. When set to true, it ignores the model_class attribute value and save in the memory, if set to false it user model_class to persist data.
64
+ This attribute is used to persist data directly into memory. When set to true, it ignores the persistence_class attribute and save in memory. If set to false it goes back to persistence_class. You can use it to keep your tests saving in memory, or set it to false manually if a test need to touch the database.
63
65
 
64
- P.S.: Just be careful, the set_save_in_memory method should always be called after set_model_class method.
66
+ If using Rails you can even tie it to your environment, so in tests it is set to true and otherwise it is set to false, like this:
65
67
 
66
- class User < ActiveRepository::Base
67
- # Defines the class responsible for persisting data
68
- set_model_class(UserModel)
68
+ class UserRepository < ActiveRepository::Base
69
+ persistence_class = User
70
+ save_in_memory = Rails.env.test?
71
+ end
72
+
73
+ ###postfix
74
+
75
+ ActiveRepository also has an attribute to help you keep your code clean, the postfix. It can be used to define a pattern for Persistence classes so you don't need to keep declaring it everywhere. When using it, your persistence_class name would be \<repository_class_name\> + \<postfix\>.
76
+
77
+ Here is an example, let's say you have a bunch of Mongoid Documents and you don't want to declare persistence_class for each repository. So you can create a Base Repository and declare the postfix:
69
78
 
70
- # Set this to true in order to ignore model_class attribute and persist in memory
71
- set_save_in_memory(true)
79
+ class BaseRepository < ActiveRepository::Base
80
+ # Defines the postfix
81
+ postfix "Document"
82
+
83
+ save_in_memory = false
72
84
  end
73
85
 
74
- Then, you have only to set the fields it is going to use:
86
+ You have to rename your Mongoid Documents to the defined pattern, like this:
75
87
 
76
- class User < ActiveRepository::Base
77
- # Defines the fields of the class
78
- fields :name, :email, :birthdate
88
+ class UserDocument
89
+ include Mongoid::Document
90
+ end
79
91
 
80
- set_model_class(UserModel)
92
+ And you can create your repositories inheriting from BaseRepository:
81
93
 
82
- set_save_in_memory(true)
94
+ class User < BaseRepository
83
95
  end
84
96
 
85
- Now you are all set and ready to go. It is just using ActiveRepository as if it was your ActiveRecord model or Mongoid Document.
97
+ Then you are good to go!!!
98
+
99
+ ###Setting fields
100
+
101
+ After defining the persistence options, you can set the fields it is going to use:
102
+
103
+ class UserRepository < ActiveRepository::Base
104
+ # Defines the fields of the class
105
+ fields :name, :email, :birthdate
106
+
107
+ persistence_class = User
108
+ save_in_memory = false
109
+ end
86
110
 
87
- You can check an example project here: https://github.com/efreesen/sports_betting_engine
111
+ Now you are all set and ready to go. Your business logic is decoupled from the persistence tier!
88
112
 
89
113
  ## Contributing
90
114
 
@@ -31,7 +31,7 @@ Gem::Specification.new do |gem|
31
31
 
32
32
  gem.add_runtime_dependency(%q<active_hash>, [">= 1.2.3"])
33
33
  gem.add_runtime_dependency(%q<activemodel>, [">= 3.2"])
34
- gem.add_runtime_dependency(%q<sql_query_executor>, [">= 0.3.1"])
34
+ gem.add_runtime_dependency(%q<sql_query_executor>, [">= 0.3.4"])
35
35
  gem.add_development_dependency(%q<pry>)
36
36
  gem.add_development_dependency(%q<rspec>, [">= 2.2.0"])
37
37
  gem.add_development_dependency(%q<activerecord>, [">= 3.2"])
@@ -1,42 +1,42 @@
1
1
  class DefaultAdapter
2
2
  class << self
3
3
  def all(klass)
4
- klass.get_model_class.all
4
+ klass.persistence_class.all
5
5
  end
6
6
 
7
7
  def delete(klass, id)
8
- object = klass.get_model_class.where(id: id).first
8
+ object = klass.persistence_class.where(id: id).first
9
9
  object.delete if object
10
10
  end
11
11
 
12
12
  def delete_all(klass)
13
- klass.get_model_class.delete_all
13
+ klass.persistence_class.delete_all
14
14
  end
15
15
 
16
16
  def exists?(klass, id)
17
- klass.get_model_class.exists?(id)
17
+ klass.persistence_class.exists?(id)
18
18
  end
19
19
 
20
20
  def find(klass, id)
21
21
  id = normalize_id(id) if id
22
22
 
23
- klass.get_model_class.find(id)
23
+ klass.persistence_class.find(id)
24
24
  end
25
25
 
26
26
  def first(klass)
27
- klass.get_model_class.first
27
+ klass.persistence_class.first
28
28
  end
29
29
 
30
30
  def last(klass)
31
- klass.get_model_class.last
31
+ klass.persistence_class.last
32
32
  end
33
33
 
34
34
  def create(klass, attributes)
35
- object = klass.get_model_class.create(attributes)
35
+ object = klass.persistence_class.create(attributes)
36
36
  end
37
37
 
38
38
  def update_attribute(klass, id, key, value)
39
- object = id.nil? ? klass.get_model_class.new(key.to_sym => value) : klass.get_model_class.find(id)
39
+ object = id.nil? ? klass.persistence_class.new(key.to_sym => value) : klass.persistence_class.find(id)
40
40
 
41
41
  ret = object.update_attribute(key, value)
42
42
 
@@ -44,7 +44,7 @@ class DefaultAdapter
44
44
  end
45
45
 
46
46
  def update_attributes(klass, id, attributes)
47
- object = id.nil? ? klass.get_model_class.new : klass.get_model_class.find(id)
47
+ object = id.nil? ? klass.persistence_class.new : klass.persistence_class.find(id)
48
48
 
49
49
  ret = object.update_attributes(attributes)
50
50
 
@@ -52,7 +52,7 @@ class DefaultAdapter
52
52
  end
53
53
 
54
54
  def where(klass, query)
55
- klass.get_model_class.where(query.to_sql)
55
+ klass.persistence_class.where(query.to_sql)
56
56
  end
57
57
 
58
58
  private
@@ -11,7 +11,7 @@ class MongoidAdapter < DefaultAdapter
11
11
  # end
12
12
 
13
13
  def exists?(klass, id)
14
- klass.get_model_class.where(:id => id).present?
14
+ klass.persistence_class.where(:id => id).present?
15
15
  end
16
16
 
17
17
  # def find(klass, id)
@@ -47,7 +47,7 @@ class MongoidAdapter < DefaultAdapter
47
47
  # end
48
48
 
49
49
  def where(klass, query)
50
- klass.get_model_class.where(query.selector)
50
+ klass.persistence_class.where(query.selector)
51
51
  end
52
52
  end
53
53
  end
@@ -4,7 +4,7 @@ require 'active_repository/adapters/mongoid_adapter'
4
4
  class PersistenceAdapter
5
5
  class << self
6
6
  def get_adapter(klass)
7
- modules = klass.get_model_class.included_modules.map(&:to_s)
7
+ modules = klass.persistence_class.included_modules.map(&:to_s)
8
8
  if modules.include?("Mongoid::Document")
9
9
  MongoidAdapter
10
10
  else
@@ -56,8 +56,4 @@ class PersistenceAdapter
56
56
  get_adapter(klass).where(klass, args)
57
57
  end
58
58
  end
59
-
60
- def method_missing(sym, *args, &block)
61
- get_adapter(args.first).send(sym, args)
62
- end
63
59
  end
@@ -3,6 +3,7 @@
3
3
  # Author:: Caio Torres (mailto:efreesen@gmail.com)
4
4
  # License:: GPL
5
5
 
6
+
6
7
  module ActiveRepository
7
8
  module Associations
8
9
  def self.included(base)
@@ -14,33 +15,23 @@ module ActiveRepository
14
15
  def has_many(association_id, options = {})
15
16
  define_method(association_id) do
16
17
  options = {
17
- class_name: association_id.to_s.classify,
18
- foreign_key: self.class.to_s.foreign_key
18
+ class_name: association_id.to_s.classify
19
19
  }.merge(options)
20
20
 
21
+ foreign_key = self.class.to_s.foreign_key
21
22
  klass = options[:class_name].constantize
22
- objects = []
23
23
 
24
- klass.where(options[:foreign_key] => id)
24
+ klass.where(foreign_key => id)
25
25
  end
26
26
  end
27
27
 
28
28
  # Defines "has one" type relation between ActiveRepository objects
29
29
  def has_one(association_id, options = {})
30
- define_method(association_id) do
31
- options = {
32
- class_name: association_id.to_s.classify,
33
- foreign_key: self.class.to_s.foreign_key
34
- }.merge(options)
35
-
36
- scope = options[:class_name].constantize
37
-
38
- scope = scope.where(options[:conditions]) if options[:conditions]
39
-
40
- send_params = scope.respond_to?(:find_by) ? ["find_by", id: id] : ["find_by_id", id]
30
+ options = {
31
+ class_name: association_id.to_s.classify
32
+ }.merge(options)
41
33
 
42
- scope.send(*send_params)
43
- end
34
+ has_one_methods(association_id, options)
44
35
  end
45
36
 
46
37
  # Defines "belongs to" type relation between ActiveRepository objects
@@ -52,22 +43,85 @@ module ActiveRepository
52
43
 
53
44
  field options[:foreign_key].to_sym
54
45
 
46
+ belongs_to_methods(association_id, options)
47
+ end
48
+
49
+ private
50
+ def has_one_methods(association_id, options)
51
+ define_has_one_method(association_id, options)
52
+ define_has_one_setter(association_id, options)
53
+ define_has_one_create(association_id, options)
54
+ end
55
+
56
+ def belongs_to_methods(association_id, options)
57
+ define_belongs_to_method(association_id, options)
58
+ define_belongs_to_setter(association_id, options)
59
+ define_belongs_to_create(association_id, options)
60
+ end
61
+
62
+ def define_has_one_method(association_id, options)
63
+ define_method(association_id) do
64
+ foreign_key = self.class.to_s.foreign_key
65
+ klass = options[:class_name].constantize
66
+
67
+ klass.where(foreign_key => self.id).first
68
+ end
69
+ end
70
+
71
+ def define_has_one_setter(association_id, options)
72
+ define_method("#{association_id}=") do |object|
73
+ primary_key = self.send(self.class.primary_key)
74
+ foreign_key = self.class.to_s.foreign_key
75
+ association = self.send(association_id)
76
+
77
+ association.update_attribute(foreign_key, nil) if association
78
+
79
+ object.update_attribute(foreign_key, primary_key) if object
80
+ end
81
+ end
82
+
83
+ def define_has_one_create(association_id, options)
84
+ define_method("create_#{association_id}") do |attributes|
85
+ primary_key = self.send(self.class.primary_key)
86
+ foreign_key = self.class.to_s.foreign_key
87
+ association = self.send(association_id)
88
+ klass = options[:class_name].constantize
89
+
90
+ association.update_attribute(foreign_key, nil) if
91
+ self.send("#{association_id}=", nil)
92
+
93
+ klass.create(attributes.merge(foreign_key => primary_key))
94
+ end
95
+ end
96
+
97
+ def define_belongs_to_method(association_id, options)
55
98
  define_method(association_id) do
56
99
  klass = options[:class_name].constantize
57
- id = send(options[:foreign_key])
100
+ foreign_key = send(options[:foreign_key])
58
101
 
59
- if id.present?
60
- object = klass.respond_to?(:find_by) ? klass.find_by(id: id) : klass.find_by_id(id)
61
- else
62
- nil
63
- end
102
+ klass.where(klass.primary_key => foreign_key).first
64
103
  end
104
+ end
65
105
 
106
+ def define_belongs_to_setter(association_id, options)
66
107
  define_method("#{association_id}=") do |new_value|
67
108
  attributes.delete(association_id.to_sym)
68
109
  send("#{options[:foreign_key]}=", (new_value.try(:id) ? new_value.id : new_value))
69
110
  end
70
111
  end
112
+
113
+ def define_belongs_to_create(association_id, options)
114
+ define_method("create_#{association_id}") do |attributes|
115
+ klass = options[:class_name].constantize
116
+
117
+ object = klass.create(attributes)
118
+
119
+ self.update_attribute(options[:foreign_key], object.id)
120
+
121
+ object
122
+ end
123
+ end
124
+
71
125
  end
72
126
  end
73
127
  end
@@ -47,8 +47,8 @@ module ActiveRepository
47
47
  # Using ActiveRecord/Mongoid to persist objects:
48
48
  #
49
49
  # class SaveInORMOrODMTest < ActiveRepository::Base
50
- # SaveInORMOrODMTest.set_model_class(ORMOrODMModelClass)
51
- # SaveInORMOrODMTest.set_save_in_memory(false)
50
+ # SaveInORMOrODMTest.persistence_class = ORMOrODMModelClass
51
+ # SaveInORMOrODMTest.save_in_memory = false
52
52
  # end
53
53
  #
54
54
  # Author:: Caio Torres (mailto:efreesen@gmail.com)
@@ -62,12 +62,11 @@ module ActiveRepository
62
62
  include ActiveRepository::Associations
63
63
  include ActiveRepository::Writers::InstanceMethods
64
64
 
65
- class_attribute :model_class, :save_in_memory, :instance_writer => false
65
+ class_attribute :model_class, :instance_writer => false
66
+ class_attribute :save_in_memory, :postfix, :instance_writer => true
66
67
 
67
68
  after_validation :set_timestamps
68
69
 
69
- self.save_in_memory = true if self.save_in_memory == nil
70
-
71
70
  # Returns all persisted objects
72
71
  def self.all
73
72
  (repository? ? super : PersistenceAdapter.all(self).map { |object| serialize!(object.attributes) })
@@ -88,10 +87,16 @@ module ActiveRepository
88
87
  repository? ? find_by(id: id).present? : PersistenceAdapter.exists?(self, id)
89
88
  end
90
89
 
90
+ def self.persistence_class
91
+ return self if save_in_memory? || (postfix.nil? && self.model_class.nil?)
92
+ return "#{self}#{postfix.classify}".constantize if postfix.present?
93
+ self.model_class.to_s.constantize
94
+ end
95
+
91
96
  # Returns the Class responsible for persisting the objects
92
97
  def self.get_model_class
93
- return self if self.model_class.nil? || self.save_in_memory?
94
- save_in_memory? ? self : self.model_class
98
+ puts '[deprecation warning] This method is going to be deprecated, use "persistence_class" instead.'
99
+ persistence_class
95
100
  end
96
101
 
97
102
  # Searches all objects that matches #field_name field with the #args value(s)
@@ -121,21 +126,29 @@ module ActiveRepository
121
126
  end
122
127
  end
123
128
 
124
- # Returns a array with the field names of the Class
129
+ # Returns an array with the field names of the Class
125
130
  def self.serialized_attributes
126
131
  field_names.map &:to_s
127
132
  end
128
133
 
134
+ def self.persistence_class=(value)
135
+ self.model_class = value
136
+ end
137
+
129
138
  # Sets the class attribute model_class, responsible to persist the ActiveRepository objects
130
139
  def self.set_model_class(value)
131
- self.model_class = value if model_class.nil?
140
+ puts '[deprecation warning] This method is going to be deprecated, use "persistence_class=" instead.'
141
+ persistence_class = value
142
+ end
132
143
 
133
- self.set_save_in_memory(repository?)
144
+ def self.save_in_memory?
145
+ self.save_in_memory == nil ? true : self.save_in_memory
134
146
  end
135
147
 
136
148
  # Sets the class attribute save_in_memory, set it to true to ignore model_class attribute
137
149
  # and persist objects in memory
138
150
  def self.set_save_in_memory(value)
151
+ puts '[deprecation warning] This method is going to be deprecated, use "save_in_memory=" instead.'
139
152
  self.save_in_memory = value
140
153
  end
141
154
 
@@ -151,8 +164,6 @@ module ActiveRepository
151
164
 
152
165
  result_set = ActiveRepository::ResultSet.new(self)
153
166
 
154
- # binding.pry
155
-
156
167
  result_set.where(args)
157
168
 
158
169
  # if repository?
@@ -168,8 +179,13 @@ module ActiveRepository
168
179
  # end
169
180
  end
170
181
 
182
+ def persistence_class
183
+ self.class.persistence_class
184
+ end
185
+
171
186
  def get_model_class
172
- self.class.get_model_class
187
+ puts '[deprecation warning] This method is going to be deprecated, use "persistence_class" instead.'
188
+ self.class.persistence_class
173
189
  end
174
190
 
175
191
  # Persists the object using the class defined on the model_class attribute, if none defined it
@@ -183,15 +199,15 @@ module ActiveRepository
183
199
  # Gathers the persisted object from database and updates self with it's attributes.
184
200
  def reload
185
201
  object = self.id.present? ?
186
- get_model_class.where(id: self.id).first_or_initialize :
202
+ persistence_class.where(id: self.id).first_or_initialize :
187
203
  self
188
204
 
189
205
  serialize! object.attributes
190
206
  end
191
207
 
192
208
  def save(force=false)
193
- if self.class == get_model_class
194
- object = get_model_class.where(id: self.id).first_or_initialize
209
+ if self.class == persistence_class
210
+ object = persistence_class.where(id: self.id).first_or_initialize
195
211
 
196
212
  if force || self.id.nil?
197
213
  self.id = nil if self.id.nil?
@@ -223,10 +239,10 @@ module ActiveRepository
223
239
  # Find related object on the database and updates it with attributes in self, if it didn't
224
240
  # find it on database it creates a new one.
225
241
  def convert(attribute="id")
226
- klass = get_model_class
242
+ klass = persistence_class
227
243
  object = klass.where(attribute.to_sym => self.send(attribute)).first
228
244
 
229
- object ||= get_model_class.new
245
+ object ||= persistence_class.new
230
246
 
231
247
  attributes = self.attributes
232
248
 
@@ -243,7 +259,7 @@ module ActiveRepository
243
259
 
244
260
  private
245
261
  def self.repository?
246
- self == get_model_class
262
+ self == persistence_class
247
263
  end
248
264
 
249
265
  # Updates created_at and updated_at
@@ -1,7 +1,7 @@
1
1
  # Module containing methods responsible for searching ActiveRepository objects
2
2
  module ActiveRepository #:nodoc:
3
3
  module Finders #:nodoc:
4
- # Searches for a object containing the id in #id
4
+ # Searches for an object containing the id in #id
5
5
  def find(id)
6
6
  begin
7
7
  if repository?
@@ -2,13 +2,21 @@ class ActiveRepository::ResultSet
2
2
  def initialize(klass, query={}, attributes={})
3
3
  @klass = klass
4
4
  convert_query(query)
5
- @attributes = query.is_a?(Hash) ? attributes.merge(query) : attributes
5
+ @attributes = attributes.merge(SqlQueryExecutor::Query::Normalizers::QueryNormalizer.attributes_from_query(query))
6
6
  end
7
7
 
8
8
  def all
9
9
  @query ? get_result(@query) : @klass.all
10
10
  end
11
11
 
12
+ def build(attributes)
13
+ @klass.new(@attributes.merge(attributes))
14
+ end
15
+
16
+ def create(attributes)
17
+ @klass.create(@attributes.merge(attributes))
18
+ end
19
+
12
20
  def count
13
21
  all.size
14
22
  end
@@ -51,7 +59,7 @@ class ActiveRepository::ResultSet
51
59
 
52
60
  private
53
61
  def convert_query(query)
54
- @query = SqlQueryExecutor::Query::QueryNormalizer.clean_query(query)
62
+ @query = SqlQueryExecutor::Query::Normalizers::QueryNormalizer.clean_query(query)
55
63
  end
56
64
 
57
65
  def get_result(args)
@@ -70,7 +78,7 @@ private
70
78
  end
71
79
 
72
80
  def join_query(query, separator)
73
- query = SqlQueryExecutor::Query::QueryNormalizer.clean_query(query)
81
+ query = SqlQueryExecutor::Query::Normalizers::QueryNormalizer.clean_query(query)
74
82
  query.blank? ? @query : (@query.blank? ? query : "(#{@query}) #{separator} (#{query})")
75
83
  end
76
84
  end
@@ -11,62 +11,15 @@ module ActiveModel
11
11
  end
12
12
 
13
13
  def validate_each(record, attribute, value)
14
- duplicate = record.get_model_class.where("id != ?", record.id).select do |object|
14
+ duplicate = record.class.where("id is not ?", (record.id ? record.id : 'null')).all.select do |object|
15
15
  object.id != record.id && object.send(attribute) == record.send(attribute)
16
16
  end
17
17
 
18
18
  record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope, :conditions).merge(:value => value)) if duplicate.any?
19
19
  end
20
-
21
- protected
22
-
23
- # The check for an existing value should be run from a class that
24
- # isn't abstract. This means working down from the current class
25
- # (self), to the first non-abstract class. Since classes don't know
26
- # their subclasses, we have to build the hierarchy between self and
27
- # the record's class.
28
- def find_finder_class_for(record) #:nodoc:
29
- class_hierarchy = [record.class]
30
-
31
- while class_hierarchy.first != @klass
32
- class_hierarchy.prepend(class_hierarchy.first.superclass)
33
- end
34
-
35
- class_hierarchy.detect { |klass| klass.respond_to?(:abstract_class?) ? !klass.abstract_class? : true }
36
- end
37
-
38
- def build_relation(klass, table, attribute, value) #:nodoc:
39
- column, attribute, value = get_reflection_attributes(klass)
40
-
41
- value = column.limit ? value.to_s[0, column.limit] : value.to_s if !value.nil? && column.text?
42
-
43
- if !options[:case_sensitive] && value && column.text?
44
- # will use SQL LOWER function before comparison, unless it detects a case insensitive collation
45
- relation = klass.connection.case_insensitive_comparison(table, attribute, column, value)
46
- else
47
- value = klass.connection.case_sensitive_modifier(value) unless value.nil?
48
- relation = table[attribute].eq(value)
49
- end
50
-
51
- relation
52
- end
53
-
54
- private
55
- def get_reflection_attributes(klass, attribute, value)
56
- reflection = klass.reflect_on_association(attribute)
57
- column = klass.columns_hash[reflection.foreign_key]
58
-
59
- if reflection
60
- attribute = reflection.foreign_key
61
- value = value.attributes[reflection.primary_key_column.name]
62
- else
63
- column = klass.columns_hash[attribute.to_s]
64
- end
65
-
66
- [column, attribute, value]
67
- end
68
20
  end
69
21
 
22
+ private
70
23
  module ClassMethods
71
24
  # Validates whether the value of the specified attributes are unique
72
25
  # across the system. Useful for making sure that only one user
@@ -167,7 +120,7 @@ module ActiveModel
167
120
  # | # title!
168
121
  #
169
122
  # This could even happen if you use transactions with the 'serializable'
170
- # isolation level. The best way to work around this problem is to add a unique
123
+ # isolation level. The best way to work around this problem is to add an unique
171
124
  # index to the database table using
172
125
  # ActiveRecord::ConnectionAdapters::SchemaStatements#add_index. In the
173
126
  # rare case that a race condition occurs, the database will guarantee
@@ -1,3 +1,3 @@
1
1
  module ActiveRepository
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.1"
3
3
  end
@@ -79,7 +79,7 @@ module ActiveHash
79
79
  end
80
80
 
81
81
  def eql?(other)
82
- (other.instance_of?(self.class) || other.instance_of?(get_model_class)) && id.present? && (id == other.id) && (!self.respond_to?(:created_at) || (created_at == other.created_at))
82
+ (other.instance_of?(self.class) || other.instance_of?(persistence_class)) && id.present? && (id == other.id) && (!self.respond_to?(:created_at) || (created_at == other.created_at))
83
83
  end
84
84
 
85
85
  alias == eql?
@@ -7,7 +7,7 @@ module ActiveRepository
7
7
  object = self.new(attributes)
8
8
 
9
9
  if object.present? && object.valid?
10
- if get_model_class == self
10
+ if persistence_class == self
11
11
  object.id = nil unless exists?(object.id)
12
12
 
13
13
  object.save
@@ -35,7 +35,7 @@ module ActiveRepository
35
35
  # Deletes self from the repository.
36
36
  def delete
37
37
  klass = self.class
38
- if klass.get_model_class == klass
38
+ if klass.persistence_class == klass
39
39
  super
40
40
  else
41
41
  PersistenceAdapter.delete(klass, self.id)
@@ -47,7 +47,7 @@ module ActiveRepository
47
47
  ret = true
48
48
  key = key.to_sym
49
49
 
50
- if self.class == get_model_class
50
+ if self.class == persistence_class
51
51
  object = self.class.where(:id => self.id).first_or_initialize
52
52
 
53
53
  self.send("#{key}=", value)
@@ -68,7 +68,7 @@ module ActiveRepository
68
68
  def update_attributes(attributes)
69
69
  attributes = attributes.symbolize_keys if attributes.respond_to?(:symbolize_keys)
70
70
  klass = self.class
71
- model_class = get_model_class
71
+ model_class = persistence_class
72
72
 
73
73
  if klass == model_class
74
74
  attributes.each do |key, value|
@@ -18,6 +18,7 @@ describe ActiveRepository::Base, "associations" do
18
18
  end
19
19
 
20
20
  class City < ActiveRepository::Base
21
+ fields :name
21
22
  end
22
23
 
23
24
  class Author < ActiveRepository::Base
@@ -68,7 +69,7 @@ describe ActiveRepository::Base, "associations" do
68
69
  end
69
70
  end
70
71
 
71
- context "with ActiveHash children" do
72
+ context "with ActiveRepository children" do
72
73
  before do
73
74
  Author.field :city_id
74
75
  @included_author_1 = Author.create :city_id => 1
@@ -92,7 +93,6 @@ describe ActiveRepository::Base, "associations" do
92
93
  end
93
94
 
94
95
  describe "#belongs_to" do
95
-
96
96
  context "with an ActiveRecord parent" do
97
97
  it "find the correct records" do
98
98
  City.belongs_to :country
@@ -108,7 +108,7 @@ describe ActiveRepository::Base, "associations" do
108
108
  end
109
109
  end
110
110
 
111
- context "with an ActiveHash parent" do
111
+ context "with an ActiveRepository parent" do
112
112
  it "find the correct records" do
113
113
  Author.belongs_to :city
114
114
  city = City.create
@@ -164,6 +164,21 @@ describe ActiveRepository::Base, "associations" do
164
164
  author.city_id.should == @city.id
165
165
  end
166
166
  end
167
+
168
+ describe '#create_association' do
169
+ before do
170
+ Author.belongs_to :city
171
+ end
172
+
173
+ it "creates a city related to author" do
174
+ author = Author.new
175
+ city = author.create_city(name: 'Metropolis')
176
+
177
+ author.city.name.should == 'Metropolis'
178
+ author.city_id.should == city.id
179
+ author.city.should == city
180
+ end
181
+ end
167
182
  end
168
183
 
169
184
  describe "#has_one" do
@@ -184,7 +199,7 @@ describe ActiveRepository::Base, "associations" do
184
199
  end
185
200
  end
186
201
 
187
- context "with ActiveHash children" do
202
+ context "with ActiveRepository children" do
188
203
  before do
189
204
  City.has_one :author
190
205
  Author.field :city_id
@@ -201,6 +216,34 @@ describe ActiveRepository::Base, "associations" do
201
216
  city.author.should be_nil
202
217
  end
203
218
  end
219
+
220
+ describe '#create_association' do
221
+ before do
222
+ City.has_one :author
223
+ Author.field :city_id
224
+ Author.field :name
225
+ end
226
+
227
+ it "creates a city related to author" do
228
+ city = City.create
229
+ author = city.create_author(name: 'Clark Kent')
230
+
231
+ city.author.name.should == 'Clark Kent'
232
+ author.city_id.should == city.id
233
+ city.author.should == author
234
+ end
235
+
236
+ it "replaces existing relation" do
237
+ city = City.create
238
+ old_author = city.create_author(name: 'Clark Kent')
239
+ author = city.create_author(name: 'Bruce Wayne')
240
+
241
+ Author.where(city_id: city.id).count.should == 1
242
+ city.author.name.should == 'Bruce Wayne'
243
+ author.city_id.should == city.id
244
+ city.author.should == author
245
+ end
246
+ end
204
247
  end
205
248
 
206
249
  describe "#marked_for_destruction?" do
@@ -227,8 +270,8 @@ describe ActiveRepository::Base, "associations" do
227
270
  end
228
271
 
229
272
  class State < ActiveRepository::Base
230
- State.set_model_class(StateModel)
231
- State.set_save_in_memory(false)
273
+ State.persistence_class = StateModel
274
+ State.save_in_memory = false
232
275
  belongs_to :country
233
276
  has_many :cities
234
277
 
@@ -244,8 +287,8 @@ describe ActiveRepository::Base, "associations" do
244
287
  end
245
288
 
246
289
  class City < ActiveRepository::Base
247
- City.set_model_class(CityModel)
248
- City.set_save_in_memory(false)
290
+ City.persistence_class = CityModel
291
+ City.save_in_memory = false
249
292
  belongs_to :state
250
293
  has_many :regions
251
294
  end
@@ -9,6 +9,7 @@ describe ActiveRepository, "Base" do
9
9
 
10
10
  before do
11
11
  class Country < ActiveRepository::Base
12
+ validates_presence_of :name
12
13
  end
13
14
  end
14
15
 
@@ -16,11 +17,51 @@ describe ActiveRepository, "Base" do
16
17
  Object.send :remove_const, :Country
17
18
  end
18
19
 
20
+ describe 'postfix' do
21
+ before do
22
+ class CountryPersistence
23
+ end
24
+ end
25
+
26
+ context 'when postfix is present' do
27
+ before do
28
+ Country.postfix = "persistence"
29
+ Country.save_in_memory = false
30
+ end
31
+
32
+ it 'in persistence_class returns class name and postfix' do
33
+ Country.persistence_class.to_s.should == "CountryPersistence"
34
+ end
35
+ end
36
+
37
+ context 'when postfix and persistence_class are present' do
38
+ before do
39
+ Country.postfix = "persistence"
40
+ Country.persistence_class = "CountryModel"
41
+ Country.save_in_memory = false
42
+ end
43
+
44
+ it 'in persistence_class returns class name and postfix' do
45
+ Country.persistence_class.to_s.should == "CountryPersistence"
46
+ end
47
+ end
48
+
49
+ context 'whe postfix and savi_in_memory are present' do
50
+ before do
51
+ Country.postfix = "persistence"
52
+ end
53
+
54
+ it 'in persistence_class returns class name and postfix' do
55
+ Country.persistence_class.to_s.should == "Country"
56
+ end
57
+ end
58
+ end
59
+
19
60
  context "in_memory", :in_memory do
20
61
  before do
21
62
  Country.fields :name, :monarch, :language, :created_at, :updated_at
22
- Country.set_model_class(Country)
23
- Country.set_save_in_memory(true)
63
+ Country.persistence_class = Country
64
+ Country.save_in_memory = true
24
65
 
25
66
  Country.create(:id => 1, :name => "US", :language => 'English')
26
67
  Country.create(:id => 2, :name => "Canada", :language => 'English', :monarch => "The Crown of England")
@@ -63,6 +104,7 @@ describe ActiveRepository, "Base" do
63
104
  it_behaves_like '.transaction'
64
105
  it_behaves_like '.delete_all'
65
106
  it_behaves_like '#delete'
107
+ it_behaves_like 'uniqueness'
66
108
  end
67
109
 
68
110
  context "active_record", :active_record do
@@ -81,8 +123,8 @@ describe ActiveRepository, "Base" do
81
123
  end
82
124
  end
83
125
 
84
- Country.set_model_class(CountryModel)
85
- Country.set_save_in_memory(false)
126
+ Country.persistence_class = CountryModel
127
+ Country.save_in_memory = false
86
128
 
87
129
  Country.create(:id => 1, :name => "US", :language => 'English')
88
130
  Country.create(:id => 2, :name => "Canada", :language => 'English', :monarch => "The Crown of England")
@@ -129,6 +171,7 @@ describe ActiveRepository, "Base" do
129
171
  it_behaves_like '.transaction'
130
172
  it_behaves_like '.delete_all'
131
173
  it_behaves_like '#delete'
174
+ it_behaves_like 'uniqueness'
132
175
  end
133
176
 
134
177
  context "mongoid", :mongoid do
@@ -150,8 +193,8 @@ describe ActiveRepository, "Base" do
150
193
  field :created_at
151
194
  end
152
195
 
153
- Country.set_model_class(CountryModel)
154
- Country.set_save_in_memory(false)
196
+ Country.persistence_class = CountryModel
197
+ Country.save_in_memory = false
155
198
 
156
199
  Country.delete_all
157
200
 
@@ -200,5 +243,6 @@ describe ActiveRepository, "Base" do
200
243
  it_behaves_like '.transaction'
201
244
  it_behaves_like '.delete_all'
202
245
  it_behaves_like '#delete'
246
+ it_behaves_like 'uniqueness'
203
247
  end
204
248
  end
@@ -1,7 +1,6 @@
1
1
  require 'spec_helper'
2
2
  require 'active_repository'
3
3
  require 'active_repository/result_set'
4
- require 'pry'
5
4
 
6
5
  describe ActiveRepository::ResultSet, :result_set do
7
6
  before do
@@ -85,7 +84,7 @@ describe ActiveRepository::ResultSet, :result_set do
85
84
  end
86
85
 
87
86
  describe '#and' do
88
- it 'is a alias for #where' do
87
+ it 'is an alias for #where' do
89
88
  expect(described_class.instance_method(:and)).to eq described_class.instance_method(:where)
90
89
  end
91
90
  end
@@ -112,6 +111,48 @@ describe ActiveRepository::ResultSet, :result_set do
112
111
  end
113
112
  end
114
113
 
114
+ describe '#build' do
115
+ subject { ActiveRepository::ResultSet.new(Country) }
116
+
117
+ it "returns a Country object" do
118
+ expect(subject.build(name: 'Canada')).to be_a(Country)
119
+ end
120
+
121
+ it 'returns a new_record' do
122
+ result = subject.build(continent: 'America')
123
+
124
+ expect(result).to be_new_record
125
+ end
126
+
127
+ it 'merges attributes' do
128
+ result_set = subject.where(name: 'Canada')
129
+ result = result_set.build(continent: 'America')
130
+
131
+ expect(result.attributes).to eq(name: 'Canada', continent: 'America')
132
+ end
133
+ end
134
+
135
+ describe '#create' do
136
+ subject { ActiveRepository::ResultSet.new(Country) }
137
+
138
+ it "returns a Country object" do
139
+ expect(subject.create(name: 'Canada')).to be_a(Country)
140
+ end
141
+
142
+ it 'returns a persisted object' do
143
+ result = subject.create(continent: 'America')
144
+
145
+ expect(result).not_to be_new_record
146
+ end
147
+
148
+ it 'merges attributes' do
149
+ result_set = subject.where(name: 'Canada')
150
+ result = result_set.create(continent: 'America')
151
+
152
+ expect(result.attributes).to eq(name: 'Canada', continent: 'America', id: result.id)
153
+ end
154
+ end
155
+
115
156
  describe '#all' do
116
157
  before do
117
158
  Country.delete_all
@@ -243,7 +284,7 @@ describe ActiveRepository::ResultSet, :result_set do
243
284
  it 'returns a new object with specified attributes' do
244
285
  object = subject.where("name = 'Poland'").first_or_initialize
245
286
 
246
- expect(object.attributes).to eq({})
287
+ expect(object.attributes).to eq(name: 'Poland')
247
288
  end
248
289
  end
249
290
  end
@@ -271,13 +312,13 @@ describe ActiveRepository::ResultSet, :result_set do
271
312
  it 'returns a new object with all Hashes as attributes' do
272
313
  object = subject.where(name: 'Poland').and("continent = 'Europe'").first_or_initialize
273
314
 
274
- expect(object.attributes).to eq(name: 'Poland')
315
+ expect(object.attributes).to eq(name: 'Poland', continent: 'Europe')
275
316
  end
276
317
 
277
318
  it 'returns a new object with all Hashes as attributes' do
278
319
  object = subject.where("name = 'Poland'").and("continent = 'Europe'").first_or_initialize
279
320
 
280
- expect(object.attributes).to eq({})
321
+ expect(object.attributes).to eq(name: 'Poland', continent: 'Europe')
281
322
  end
282
323
  end
283
324
  end
@@ -323,7 +364,7 @@ describe ActiveRepository::ResultSet, :result_set do
323
364
  it 'returns a new object with specified attributes' do
324
365
  object = subject.where("name = 'Poland'").first_or_create
325
366
 
326
- expect(object.attributes).to eq(id: 5)
367
+ expect(object.attributes).to eq(name: 'Poland', id: 5)
327
368
  end
328
369
  end
329
370
  end
@@ -351,13 +392,13 @@ describe ActiveRepository::ResultSet, :result_set do
351
392
  it 'returns a new object with all Hashes as attributes' do
352
393
  object = subject.where(name: 'Poland').and("continent = 'Europe'").first_or_create
353
394
 
354
- expect(object.attributes).to eq(id: 5, name: 'Poland')
395
+ expect(object.attributes).to eq(id: 5, name: 'Poland', continent: 'Europe')
355
396
  end
356
397
 
357
398
  it 'returns a new object with all Hashes as attributes' do
358
399
  object = subject.where("name = 'Poland'").and("continent = 'Europe'").first_or_create
359
400
 
360
- expect(object.attributes).to eq(id: 5)
401
+ expect(object.attributes).to eq(:name=>"Poland", :continent=>"Europe", :id=>5)
361
402
  end
362
403
  end
363
404
  end
@@ -88,7 +88,7 @@ shared_examples ".where" do
88
88
  results.last.name.should == "UK"
89
89
  end
90
90
 
91
- it "filters the records from a AR-like conditions hash" do
91
+ it "filters the records from an AR-like conditions hash" do
92
92
  first_id = Country.first.id
93
93
 
94
94
  results = Country.where(:name => 'US').all
@@ -194,7 +194,7 @@ shared_examples ".find_by" do
194
194
  end
195
195
  end
196
196
 
197
- context 'with a existing name' do
197
+ context 'with an existing name' do
198
198
  it "returns found element" do
199
199
  Country.find_by(name: 'Canada').should == Country.all[1]
200
200
  end
@@ -236,7 +236,7 @@ shared_examples ".find_by!" do
236
236
  end
237
237
  end
238
238
 
239
- context 'with a existing name' do
239
+ context 'with an existing name' do
240
240
  it "returns found element" do
241
241
  Country.find_by!(name: 'Canada').should == Country.all[1]
242
242
  end
@@ -441,7 +441,7 @@ end
441
441
 
442
442
  shared_examples "#to_param" do
443
443
  it "should return id as a string" do
444
- country = Country.create
444
+ country = Country.create(name: 'Brazil')
445
445
  country.to_param.should == country.id.to_s
446
446
  end
447
447
  end
@@ -449,7 +449,7 @@ end
449
449
  shared_examples "#persisted?" do
450
450
  it "should return true if the object has been saved" do
451
451
  Country.delete_all
452
- Country.create(:id => 2).should be_persisted
452
+ Country.create(:id => 2, name: 'Brazil').should be_persisted
453
453
  end
454
454
 
455
455
  it "should return false if the object has not been saved" do
@@ -563,6 +563,12 @@ shared_examples "#save" do
563
563
  Country.delete_all
564
564
  end
565
565
 
566
+ it "does not add object to the collection if it is not valid" do
567
+ country = Country.new :monarch => "King", :language => "bar"
568
+ country.save.should be_false
569
+ Country.count.should == 0
570
+ end
571
+
566
572
  it "adds the new object to the data collection" do
567
573
  Country.all.should be_empty
568
574
  country = Country.new :name => "foo", :monarch => "King", :language => "bar"
@@ -589,7 +595,7 @@ shared_examples ".create" do
589
595
 
590
596
  it "works with no args" do
591
597
  Country.all.should be_empty
592
- country = Country.create
598
+ country = Country.create(name: 'Brazil')
593
599
 
594
600
  country.id.should == Country.last.id
595
601
  end
@@ -656,7 +662,7 @@ end
656
662
 
657
663
  shared_examples "#valid?" do
658
664
  it "should return true" do
659
- Country.new.should be_valid
665
+ Country.new(name: 'Brazil').should be_valid
660
666
  end
661
667
  end
662
668
 
@@ -704,7 +710,7 @@ shared_examples "#delete" do
704
710
  end
705
711
 
706
712
  it "removes a record" do
707
- country = Country.create
713
+ country = Country.create(name: 'Brazil')
708
714
 
709
715
  Country.count.should == 1
710
716
 
@@ -720,8 +726,8 @@ shared_examples ".delete_all" do
720
726
  end
721
727
 
722
728
  it "clears out all record" do
723
- country1 = Country.create
724
- country2 = Country.create
729
+ country1 = Country.create(name: 'Brazil')
730
+ country2 = Country.create(name: 'Brazil')
725
731
 
726
732
  countries_attributes = Country.all.map(&:attributes)
727
733
  expected_attributes = [country1, country2].map(&:attributes)
@@ -740,4 +746,21 @@ shared_examples ".delete_all" do
740
746
  Country.delete_all
741
747
  Country.all.should be_empty
742
748
  end
749
+ end
750
+
751
+ shared_examples "uniqueness" do
752
+ before do
753
+ Country.delete_all
754
+ Country.validates_uniqueness_of :name
755
+ end
756
+
757
+ it "does not accept duplicated ids" do
758
+ country1 = Country.create(name: 'Brazil')
759
+ country2 = Country.create(name: 'Brazil')
760
+
761
+ country1.should be_valid
762
+ country2.should_not be_valid
763
+ Country.count.should == 1
764
+ Country.all.should == [country1]
765
+ end
743
766
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_repository
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Caio Torres
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-10 00:00:00.000000000 Z
11
+ date: 2014-03-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: active_hash
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - '>='
46
46
  - !ruby/object:Gem::Version
47
- version: 0.3.1
47
+ version: 0.3.4
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - '>='
53
53
  - !ruby/object:Gem::Version
54
- version: 0.3.1
54
+ version: 0.3.4
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: pry
57
57
  requirement: !ruby/object:Gem::Requirement