active_repository 0.0.1 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ .rbenv-version
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ rvm:
2
+ - 1.9.3
3
+ - jruby-19mode
4
+
5
+ matrix:
6
+ allow_failures:
7
+ - rvm: jruby-19mode
8
+
9
+ services:
10
+ - mongodb
data/README.md CHANGED
@@ -1,14 +1,34 @@
1
1
  # ActiveRepository
2
2
 
3
- ActiveRepository is 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) gem. Then when you decides which ORM you want to use you only have to connect ActiveRepository with it. Actually it only works with ActiveRecord, we are working to give mongoid suppoort.
3
+ [![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/badge.png)](https://codeclimate.com/github/efreesen/active_repository)
4
4
 
5
- It also has the advantage of letting you test directly in memory, no need to save data in disk. This gives a great boost on your test suite speed.
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.
6
+
7
+ Currently it only works with ActiveRecord and/or Mongoid.
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.
10
+
11
+ Here are some data for comparison:
12
+
13
+ * **ActiveRepository:**
14
+ Finished in **0.63357** seconds;
15
+ 78 examples, 0 failures
16
+
17
+ * **ActiveRecord:**
18
+ Finished in **3.78** seconds;
19
+ 78 examples, 0 failures
20
+
21
+ * **Mongoid:**
22
+ Finished in **5.25** seconds;
23
+ 78 examples, 0 failures
24
+
25
+ With ActiveRepository you can make associations with ActiveRecord, Mongoid and ActiveRepository seamlessly.
6
26
 
7
27
  ## Requirements
8
28
 
9
29
  ### Ruby
10
30
 
11
- ActiveRepository requires Ruby version **>= 1.9.2**.
31
+ ActiveRepository requires Ruby version **>= 1.9.3**.
12
32
 
13
33
  ## Installation
14
34
 
data/Rakefile CHANGED
@@ -1,2 +1,12 @@
1
1
  #!/usr/bin/env rake
2
2
  require "bundler/gem_tasks"
3
+
4
+ desc 'Default: run rspec tests.'
5
+ task :default => [:travis]
6
+
7
+ task :travis do
8
+ cmd = "rspec spec"
9
+ puts "Starting to run `#{cmd}`..."
10
+ system("export DISPLAY=:99.0 && bundle exec rspec spec -c")
11
+ raise "#{cmd} failed!" unless $?.exitstatus == 0
12
+ end
@@ -16,8 +16,12 @@ Gem::Specification.new do |gem|
16
16
 
17
17
  gem.add_runtime_dependency(%q<active_hash>, [">= 0.9.12"])
18
18
  gem.add_runtime_dependency(%q<activemodel>, [">= 3.2.6"])
19
- gem.add_development_dependency(%q<rspec>, ["~> 2.2.0"])
20
- gem.add_development_dependency(%q<sqlite3>)
19
+ gem.add_development_dependency(%q<rspec>, [">= 2.2.0"])
21
20
  gem.add_development_dependency(%q<activerecord>)
22
- gem.add_development_dependency(%q<mongoid>)
21
+ gem.add_development_dependency(%q<mongoid>, ["= 3.0.11"])
22
+ gem.add_development_dependency('rake')
23
+ gem.add_development_dependency(%q<sqlite3>) unless RUBY_PLATFORM == 'java'
24
+ gem.add_development_dependency(%q<jdbc-sqlite3>) if RUBY_PLATFORM == 'java'
25
+ gem.add_development_dependency(%q<jruby-openssl>) if RUBY_PLATFORM == 'java'
26
+ gem.add_development_dependency(%q<activerecord-jdbcsqlite3-adapter>) if RUBY_PLATFORM == 'java'
23
27
  end
@@ -3,7 +3,7 @@ module ActiveRepository
3
3
 
4
4
  module ActiveRecordExtensions
5
5
 
6
- def belongs_to_active_hash(association_id, options = {})
6
+ def belongs_to_active_repository(association_id, options = {})
7
7
  options = {
8
8
  :class_name => association_id.to_s.classify,
9
9
  :foreign_key => association_id.to_s.foreign_key
@@ -33,7 +33,6 @@ module ActiveRepository
33
33
 
34
34
  module Methods
35
35
  def has_many(association_id, options = {})
36
-
37
36
  define_method(association_id) do
38
37
  options = {
39
38
  :class_name => association_id.to_s.classify,
@@ -48,8 +47,6 @@ module ActiveRepository
48
47
  else
49
48
  objects = klass.send("find_all_by_#{options[:foreign_key]}", id)
50
49
  end
51
-
52
- objects.map{ |o| self.serialize!(o.attributes) }
53
50
  end
54
51
  end
55
52
 
@@ -79,13 +76,11 @@ module ActiveRepository
79
76
  field options[:foreign_key].to_sym
80
77
 
81
78
  define_method(association_id) do
82
- klass = self.class.get_model_class
79
+ klass = options[:class_name].constantize
83
80
  id = send(options[:foreign_key])
84
81
 
85
82
  if id.present?
86
83
  object = klass.find_by_id(id)
87
-
88
- object.nil? || object.class == self.class ? object : self.class.serialize!(object.attributes)
89
84
  else
90
85
  nil
91
86
  end
@@ -2,95 +2,26 @@ require 'active_repository/associations'
2
2
  require 'active_repository/uniqueness'
3
3
  require 'active_repository/write_support'
4
4
  require 'active_repository/sql_query_executor'
5
+ require 'active_repository/finders'
6
+ require 'active_repository/writers'
5
7
 
6
8
  module ActiveRepository
7
9
 
8
10
  class Base < ActiveHash::Base
9
11
  extend ActiveModel::Callbacks
12
+ extend ActiveRepository::Finders
13
+ extend ActiveRepository::Writers
10
14
  include ActiveModel::Validations
11
15
  include ActiveModel::Validations::Callbacks
12
16
  include ActiveRepository::Associations
17
+ include ActiveRepository::Writers::InstanceMethods
13
18
 
14
- # TODO: implement first, last,
15
-
16
- class_attribute :model_class, :save_in_memory
19
+ class_attribute :model_class, :save_in_memory, :instance_writer => false
17
20
 
18
21
  before_validation :set_timestamps
19
22
 
20
23
  fields :created_at, :updated_at
21
24
 
22
- def self.define_custom_find_by_field(field_name)
23
- method_name = :"find_by_#{field_name}"
24
- unless has_singleton_method?(method_name)
25
- the_meta_class.instance_eval do
26
- define_method(method_name) do |*args|
27
- object = get_model_class.send(method_name)
28
- object.nil? ? nil : serialize!(object.attributes)
29
- end
30
- end
31
- end
32
- end
33
-
34
- def self.define_custom_find_by_field(field_name)
35
- method_name = :"find_by_#{field_name}"
36
- the_meta_class.instance_eval do
37
- define_method(method_name) do |*args|
38
- object = nil
39
-
40
- if self == get_model_class
41
- object = self.where(field_name.to_sym => args.first).first
42
- else
43
- object = get_model_class.send(method_name, args)
44
- end
45
-
46
- object.nil? ? nil : serialize!(object.attributes)
47
- end
48
- end
49
- end
50
-
51
- def self.define_custom_find_all_by_field(field_name)
52
- method_name = :"find_all_by_#{field_name}"
53
- the_meta_class.instance_eval do
54
- define_method(method_name) do |*args|
55
- objects = []
56
-
57
- if self == get_model_class
58
- objects = self.where(field_name.to_sym => args.first)
59
- else
60
- objects = get_model_class.send(method_name, args)
61
- end
62
-
63
- objects.empty? ? [] : objects.map{ |object| serialize!(object.attributes) }
64
- end
65
- end
66
- end
67
-
68
- def self.find(id)
69
- begin
70
- if self == get_model_class
71
- super(id)
72
- else
73
- object = get_model_class.find(id)
74
-
75
- if object.is_a?(Array)
76
- object.map { |o| serialize!(o.attributes) }
77
- else
78
- serialize!(object.attributes)
79
- end
80
- end
81
- rescue Exception => e
82
- message = ""
83
-
84
- if id.is_a?(Array)
85
- message = "Couldn't find all #{self} objects with IDs (#{id.join(', ')})"
86
- else
87
- message = "Couldn't find #{self} with ID=#{id}"
88
- end
89
-
90
- raise ActiveHash::RecordNotFound.new(message)
91
- end
92
- end
93
-
94
25
  def reload
95
26
  serialize! self.class.get_model_class.find(self.id).attributes
96
27
  end
@@ -99,46 +30,12 @@ module ActiveRepository
99
30
  if self == get_model_class
100
31
  !find_by_id(id).nil?
101
32
  else
102
- get_model_class.exists?(id)
103
- end
104
- end
105
-
106
- def self.find_by_id(id)
107
- if self == get_model_class
108
- super(id)
109
- else
110
- get_model_class.find_by_id(id)
111
- end
112
- end
113
-
114
- def self.find_or_create(attributes)
115
- object = get_model_class.where(attributes).first
116
-
117
- object = model_class.create(attributes) if object.nil?
118
-
119
- serialize!(object.attributes)
120
- end
121
-
122
- def self.create(attributes={})
123
- object = get_model_class.new(attributes)
124
-
125
- object.id = nil if get_model_class.exists?(object.id)
126
-
127
- object.save
128
-
129
- serialize!(object.attributes) unless object.class.name == self
130
- end
131
-
132
- def update_attributes(attributes)
133
- object = self.class.get_model_class.find(self.id)
134
-
135
- attributes.each do |k,v|
136
- object.send("#{k.to_s}=", v) unless k == :id
33
+ if mongoid?
34
+ find_by_id(id).present?
35
+ else
36
+ get_model_class.exists?(id)
37
+ end
137
38
  end
138
-
139
- object.save
140
-
141
- self.attributes = object.attributes
142
39
  end
143
40
 
144
41
  def self.all
@@ -184,32 +81,17 @@ module ActiveRepository
184
81
  end
185
82
  end
186
83
 
187
- def self.first
188
- get("first")
189
- end
190
-
191
- def self.last
192
- get("last")
193
- end
194
-
195
- def self.get(position)
196
- if self == get_model_class
197
- id = get_model_class.all.map(&:id).sort.send(position)
198
-
199
- self.find id
200
- else
201
- serialize! get_model_class.send(position).attributes
202
- end
203
- end
204
-
205
84
  def convert(attribute="id")
206
- object = self.class.get_model_class.send("find_by_#{attribute}", self.send(attribute))
85
+ klass = self.class.get_model_class
86
+ object = klass.where(attribute.to_sym => self.send(attribute)).first
207
87
 
208
- object = self.class.get_model_class.new if object.nil?
88
+ object ||= self.class.get_model_class.new
209
89
 
210
- self.attributes.each do |k,v|
211
- object.send("#{k.to_s}=", v) unless k == :id
212
- end
90
+ attributes = self.attributes
91
+
92
+ attributes.delete(:id)
93
+
94
+ object.attributes = attributes
213
95
 
214
96
  object.save
215
97
 
@@ -218,26 +100,21 @@ module ActiveRepository
218
100
  object
219
101
  end
220
102
 
221
- def attributes=(new_attributes)
222
- new_attributes.each do |k,v|
223
- self.send("#{k.to_s}=", v)
224
- end
225
- end
226
-
227
103
  def serialize!(attributes)
228
104
  unless attributes.nil?
229
- attributes.each do |k,v|
230
- self.send("#{k.to_s}=", v)
231
- end
105
+ self.attributes = attributes
232
106
  end
233
107
 
234
108
  self
235
109
  end
236
110
 
237
- def self.serialize!(attributes)
238
- object = self.new
239
-
240
- object.serialize!(attributes)
111
+ def self.serialize!(other)
112
+ case other.class.to_s
113
+ when "Hash" then self.new.serialize!(other)
114
+ when "Array" then other.map { |o| serialize!(o.attributes) }
115
+ when "Moped::BSON::Document" then self.new.serialize!(other)
116
+ else self.new.serialize!(other.attributes)
117
+ end
241
118
  end
242
119
 
243
120
  def self.serialized_attributes
@@ -249,9 +126,14 @@ module ActiveRepository
249
126
  end
250
127
 
251
128
  def self.get_model_class
129
+ return self if self.save_in_memory.nil?
252
130
  save_in_memory? ? self : self.model_class
253
131
  end
254
132
 
133
+ def save_in_memory?
134
+ self.save_in_memory.nil? ? true : save_in_memory
135
+ end
136
+
255
137
  protected
256
138
  def model_class
257
139
  self.model_class
@@ -262,5 +144,13 @@ module ActiveRepository
262
144
  self.created_at = DateTime.now.utc if self.new_record?
263
145
  self.updated_at = DateTime.now.utc
264
146
  end
147
+
148
+ def self.mongoid?
149
+ get_model_class.included_modules.include?(Mongoid::Document)
150
+ end
151
+
152
+ def mongoid?
153
+ self.class.mongoid?
154
+ end
265
155
  end
266
156
  end
@@ -0,0 +1,101 @@
1
+ module ActiveRepository
2
+ module Finders
3
+ def define_custom_find_by_field(field_name)
4
+ method_name = :"find_all_by_#{field_name}"
5
+ the_meta_class.instance_eval do
6
+ define_method(method_name) do |*args|
7
+ object = nil
8
+
9
+ object = self.find_by_field(field_name.to_sym, args)
10
+
11
+ object.nil? ? nil : serialize!(object.attributes)
12
+ end
13
+ end
14
+ end
15
+
16
+ def define_custom_find_all_by_field(field_name)
17
+ method_name = :"find_all_by_#{field_name}"
18
+ the_meta_class.instance_eval do
19
+ define_method(method_name) do |*args|
20
+ objects = []
21
+
22
+ objects = self.find_all_by_field(field_name.to_sym, args)
23
+
24
+ objects.empty? ? [] : objects.map{ |object| serialize!(object.attributes) }
25
+ end
26
+ end
27
+ end
28
+
29
+ def find_by_field(field_name, args)
30
+ self.find_all_by_field(field_name, args).first
31
+ end
32
+
33
+ def find_all_by_field(field_name, args)
34
+ objects = []
35
+
36
+ if self == get_model_class
37
+ objects = self.where(field_name.to_sym => args.first)
38
+ else
39
+ if mongoid?
40
+ objects = get_model_class.where(field_name.to_sym => args.first)
41
+ else
42
+ method_name = :"find_all_by_#{field_name}"
43
+ objects = get_model_class.send(method_name, args)
44
+ end
45
+ end
46
+
47
+ objects
48
+ end
49
+
50
+ def find(id)
51
+ begin
52
+ if self == get_model_class
53
+ super(id)
54
+ else
55
+ object = (id == :all) ? all : get_model_class.find(id)
56
+
57
+ serialize!(object)
58
+ end
59
+ rescue Exception => e
60
+ message = "Couldn't find #{self} with ID=#{id}"
61
+ message = "Couldn't find all #{self} objects with IDs (#{id.join(', ')})" if id.is_a?(Array)
62
+
63
+ raise ActiveHash::RecordNotFound.new(message)
64
+ end
65
+ end
66
+
67
+ def find_by_id(id)
68
+ if self == get_model_class
69
+ super(id)
70
+ else
71
+ object = nil
72
+
73
+ if mongoid?
74
+ object = get_model_class.where(:id => id).entries.first
75
+ else
76
+ object = get_model_class.find_by_id(id)
77
+ end
78
+
79
+ object.nil? ? nil : serialize!(object.attributes)
80
+ end
81
+ end
82
+
83
+ def first
84
+ get("first")
85
+ end
86
+
87
+ def last
88
+ get("last")
89
+ end
90
+
91
+ private
92
+ def get(position)
93
+ if self == get_model_class
94
+ all.sort_by!{ |o| o.id }.send(position)
95
+ else
96
+ object = get_model_class.send(position)
97
+ serialize! object.attributes
98
+ end
99
+ end
100
+ end
101
+ end
@@ -75,23 +75,35 @@ module ActiveHash
75
75
  def execute_sub_query(klass, sub_query)
76
76
  case @operator
77
77
  when "between"
78
- klass.all.select do |o|
79
- field, first_attr, second_attr = convert_attrs(o.send(sub_query.first), sub_query[2], sub_query[4])
80
-
81
- (field >= first_attr && field <= second_attr)
82
- end
78
+ execute_between(klass, sub_query)
83
79
  when "is"
84
- klass.all.select do |o|
85
- field = o.send(sub_query.first).blank?
86
-
87
- sub_query.size == 3 ? field : !field
88
- end
80
+ execute_is(klass, sub_query)
89
81
  else
90
- klass.all.select do |o|
91
- field, attribute = convert_attrs(o.send(sub_query.first), sub_query[2])
82
+ execute_operator(klass, sub_query)
83
+ end
84
+ end
85
+
86
+ def execute_between(klass, sub_query)
87
+ klass.all.select do |o|
88
+ field, first_attr, second_attr = convert_attrs(o.send(sub_query.first), sub_query[2], sub_query[4])
89
+
90
+ (field >= first_attr && field <= second_attr)
91
+ end
92
+ end
93
+
94
+ def execute_is(klass, sub_query)
95
+ klass.all.select do |o|
96
+ field = o.send(sub_query.first).blank?
97
+
98
+ sub_query.size == 3 ? field : !field
99
+ end
100
+ end
101
+
102
+ def execute_operator(klass, sub_query)
103
+ klass.all.select do |o|
104
+ field, attribute = convert_attrs(o.send(sub_query.first), sub_query[2])
92
105
 
93
- field.blank? ? false : field.send(@operator, attribute)
94
- end
106
+ field.blank? ? false : field.send(@operator, attribute)
95
107
  end
96
108
  end
97
109
 
@@ -1,3 +1,3 @@
1
1
  module ActiveRepository
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -18,22 +18,16 @@ end
18
18
  module ActiveHash
19
19
  class Base
20
20
  def self.insert(record)
21
- if self.all.map(&:to_s).include?(record.to_s)
22
- record_index.delete(record.id.to_s)
21
+ record_id = record.id.to_s
22
+ record_hash = record.hash
23
+
24
+ if self.all.map(&:hash).include?(record_hash)
25
+ record_index.delete(record_id)
23
26
  self.all.delete(record)
24
27
  end
25
28
 
26
- if record_index[record.id.to_s].nil? || !self.all.map(&:to_s).include?(record.to_s)
27
- @records ||= []
28
- record.attributes[:id] ||= next_id
29
-
30
- validate_unique_id(record) if dirty
31
- mark_dirty
32
-
33
- if record.valid?
34
- add_to_record_index({ record.id.to_s => @records.length })
35
- @records << record
36
- end
29
+ if record_index[record_id].nil? || !self.all.map(&:hash).include?(record_hash)
30
+ insert_record(record)
37
31
  end
38
32
  end
39
33
 
@@ -51,6 +45,11 @@ module ActiveHash
51
45
  raise IdError.new("Duplicate Id found for record #{record.attributes}") if record_index.has_key?(record.id.to_s)
52
46
  end
53
47
 
48
+ def update_attribute(key, value)
49
+ self.send("#{key}=", value)
50
+ self.save(:validate => false)
51
+ end
52
+
54
53
  def readonly?
55
54
  false
56
55
  end
@@ -62,9 +61,13 @@ module ActiveHash
62
61
  true
63
62
  end
64
63
 
64
+ def to_param
65
+ id.present? ? id.to_s : nil
66
+ end
67
+
65
68
  def persisted?
66
69
  other = self.class.find_by_id(id)
67
- other.present? && other.created_at
70
+ other.present?
68
71
  end
69
72
 
70
73
  def eql?(other)
@@ -72,5 +75,19 @@ module ActiveHash
72
75
  end
73
76
 
74
77
  alias == eql?
78
+
79
+ private
80
+ def self.insert_record(record)
81
+ @records ||= []
82
+ record.attributes[:id] ||= next_id
83
+
84
+ validate_unique_id(record) if dirty
85
+ mark_dirty
86
+
87
+ if record.valid?
88
+ add_to_record_index({ record.id.to_s => @records.length })
89
+ @records << record
90
+ end
91
+ end
75
92
  end
76
93
  end
@@ -0,0 +1,67 @@
1
+ module ActiveRepository
2
+ module Writers
3
+ def find_or_create(attributes)
4
+ object = get_model_class.where(attributes).first
5
+
6
+ object = model_class.create(attributes) if object.nil?
7
+
8
+ serialize!(object.attributes)
9
+ end
10
+
11
+ def create(attributes={})
12
+ object = get_model_class.new(attributes)
13
+
14
+ object.id = nil if exists?(object.id)
15
+
16
+ if get_model_class == self
17
+ object.save
18
+ else
19
+ repository = serialize!(object.attributes)
20
+ repository.valid? ? (object = get_model_class.create(attributes)) : false
21
+ end
22
+
23
+ serialize!(object.attributes) unless object.class.name == self
24
+ end
25
+
26
+ module InstanceMethods
27
+ def update_attributes(attributes)
28
+ object = nil
29
+ if mongoid?
30
+ object = self.class.get_model_class.find(self.id)
31
+ else
32
+ object = self.class.get_model_class.find(self.id)
33
+ end
34
+
35
+ attributes.each do |k,v|
36
+ object.update_attribute("#{k.to_s}", v) unless k == :id
37
+ end
38
+
39
+ self.reload
40
+ end
41
+
42
+ def update_attribute(key, value)
43
+ if self.class == self.class.get_model_class
44
+ super(key,value)
45
+ else
46
+ object = self.class.get_model_class.find(self.id)
47
+
48
+ if mongoid?
49
+ super(key,value)
50
+ key = key.to_s == 'id' ? '_id' : key.to_s
51
+ end
52
+
53
+ object.update_attribute(key, value)
54
+ object.save
55
+ end
56
+
57
+ self.reload
58
+ end
59
+
60
+ def attributes=(new_attributes)
61
+ new_attributes.each do |k,v|
62
+ self.send("#{k.to_s == '_id' ? 'id' : k.to_s}=", v)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end