active_repository 0.0.3 → 0.0.4

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.
@@ -21,13 +21,9 @@ describe ActiveRepository::Base, "associations" do
21
21
  end
22
22
 
23
23
  class City < ActiveRepository::Base
24
- City.set_model_class(self)
25
- City.set_save_in_memory(true)
26
24
  end
27
25
 
28
26
  class Author < ActiveRepository::Base
29
- Author.set_model_class(self)
30
- Author.set_save_in_memory(true)
31
27
  end
32
28
 
33
29
  class Book < ActiveRecord::Base
@@ -261,14 +257,9 @@ describe ActiveRepository::Base, "associations" do
261
257
  describe "Multiple ORM" do
262
258
  before do
263
259
  Object.send :remove_const, :City
264
- # Object.send :remove_const, :Author
265
260
  Object.send :remove_const, :Country
266
- # Object.send :remove_const, :School
267
- # Object.send :remove_const, :Book
268
261
 
269
262
  class Country < ActiveRepository::Base
270
- Country.set_model_class(Country)
271
- Country.set_save_in_memory(true)
272
263
  has_many :states
273
264
  end
274
265
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_repository
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
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: 2012-11-26 00:00:00.000000000 Z
12
+ date: 2012-12-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: active_hash
@@ -80,17 +80,17 @@ dependencies:
80
80
  requirement: !ruby/object:Gem::Requirement
81
81
  none: false
82
82
  requirements:
83
- - - '='
83
+ - - ! '>='
84
84
  - !ruby/object:Gem::Version
85
- version: 3.0.11
85
+ version: '0'
86
86
  type: :development
87
87
  prerelease: false
88
88
  version_requirements: !ruby/object:Gem::Requirement
89
89
  none: false
90
90
  requirements:
91
- - - '='
91
+ - - ! '>='
92
92
  - !ruby/object:Gem::Version
93
- version: 3.0.11
93
+ version: '0'
94
94
  - !ruby/object:Gem::Dependency
95
95
  name: rake
96
96
  requirement: !ruby/object:Gem::Requirement
@@ -130,31 +130,17 @@ executables: []
130
130
  extensions: []
131
131
  extra_rdoc_files: []
132
132
  files:
133
- - .gitignore
134
- - .travis.yml
135
- - Gemfile
136
- - LICENSE
137
133
  - README.md
138
- - Rakefile
139
- - active_repository.gemspec
140
- - lib/active_repository.rb
141
- - lib/active_repository/associations.rb
142
- - lib/active_repository/base.rb
143
- - lib/active_repository/finders.rb
144
- - lib/active_repository/sql_query_executor.rb
145
- - lib/active_repository/uniqueness.rb
146
- - lib/active_repository/version.rb
147
- - lib/active_repository/write_support.rb
148
- - lib/active_repository/writers.rb
134
+ - LICENSE
149
135
  - spec/active_repository/associations_spec.rb
150
136
  - spec/active_repository/base_spec.rb
151
137
  - spec/active_repository/sql_query_executor_spec.rb
152
138
  - spec/spec_helper.rb
153
139
  - spec/support/shared_examples.rb
154
140
  - spec/support/sql_query_shared_examples.rb
155
- - support/mongoid.yml
156
141
  homepage: http://github.com/efreesen/active_repository
157
- licenses: []
142
+ licenses:
143
+ - MIT
158
144
  post_install_message:
159
145
  rdoc_options: []
160
146
  require_paths:
data/.gitignore DELETED
@@ -1,18 +0,0 @@
1
- *.gem
2
- *.rbc
3
- .bundle
4
- .config
5
- .yardoc
6
- Gemfile.lock
7
- InstalledFiles
8
- _yardoc
9
- coverage
10
- doc/
11
- lib/bundler/man
12
- pkg
13
- rdoc
14
- spec/reports
15
- test/tmp
16
- test/version_tmp
17
- tmp
18
- .rbenv-version
data/.travis.yml DELETED
@@ -1,10 +0,0 @@
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/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in active_repository.gemspec
4
- gemspec
data/Rakefile DELETED
@@ -1,12 +0,0 @@
1
- #!/usr/bin/env rake
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
@@ -1,27 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/active_repository/version', __FILE__)
3
-
4
- Gem::Specification.new do |gem|
5
- gem.authors = ["Caio Torres"]
6
- gem.email = ["efreesen@gmail.com"]
7
- gem.description = %q{An implementation of repository pattern that can connect with any ORM}
8
- gem.summary = %q{An implementation of repository pattern that can connect with any ORM}
9
- gem.homepage = "http://github.com/efreesen/active_repository"
10
-
11
- gem.files = `git ls-files`.split($\)
12
- gem.test_files = gem.files.grep(%r{^(spec)/})
13
- gem.name = "active_repository"
14
- gem.require_paths = ["lib"]
15
- gem.version = ActiveRepository::VERSION
16
-
17
- gem.add_runtime_dependency(%q<active_hash>, [">= 0.9.12"])
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<activerecord>)
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'
27
- end
@@ -1,15 +0,0 @@
1
- require 'active_repository'
2
-
3
- begin
4
- require 'active_model'
5
- require 'active_model/naming'
6
- rescue LoadError
7
- end
8
-
9
- begin
10
- require 'active_hash'
11
- require 'associations/associations'
12
- rescue LoadError
13
- end
14
-
15
- require 'active_repository/base'
@@ -1,97 +0,0 @@
1
- module ActiveRepository
2
- module Associations
3
-
4
- module ActiveRecordExtensions
5
-
6
- def belongs_to_active_repository(association_id, options = {})
7
- options = {
8
- :class_name => association_id.to_s.classify,
9
- :foreign_key => association_id.to_s.foreign_key
10
- }.merge(options)
11
-
12
- define_method(association_id) do
13
- options[:class_name].constantize.find_by_id(send(options[:foreign_key]))
14
- end
15
-
16
- define_method("#{association_id}=") do |new_value|
17
- send "#{options[:foreign_key]}=", new_value ? new_value.id : nil
18
- end
19
-
20
- create_reflection(
21
- :belongs_to,
22
- association_id.to_sym,
23
- options,
24
- options[:class_name].constantize
25
- )
26
- end
27
-
28
- end
29
-
30
- def self.included(base)
31
- base.extend Methods
32
- end
33
-
34
- module Methods
35
- def has_many(association_id, options = {})
36
- define_method(association_id) do
37
- options = {
38
- :class_name => association_id.to_s.classify,
39
- :foreign_key => self.class.to_s.foreign_key
40
- }.merge(options)
41
-
42
- klass = options[:class_name].constantize
43
- objects = []
44
-
45
- if klass.respond_to?(:scoped)
46
- objects = klass.scoped(:conditions => {options[:foreign_key] => id})
47
- else
48
- objects = klass.send("find_all_by_#{options[:foreign_key]}", id)
49
- end
50
- end
51
- end
52
-
53
- def has_one(association_id, options = {})
54
- define_method(association_id) do
55
- options = {
56
- :class_name => association_id.to_s.classify,
57
- :foreign_key => self.class.to_s.foreign_key
58
- }.merge(options)
59
-
60
- scope = options[:class_name].constantize
61
-
62
- if scope.respond_to?(:scoped) && options[:conditions]
63
- scope = scope.scoped(:conditions => options[:conditions])
64
- end
65
- scope.send("find_by_#{options[:foreign_key]}", id)
66
- end
67
- end
68
- #
69
- def belongs_to(association_id, options = {})
70
-
71
- options = {
72
- :class_name => association_id.to_s.classify,
73
- :foreign_key => association_id.to_s.foreign_key
74
- }.merge(options)
75
-
76
- field options[:foreign_key].to_sym
77
-
78
- define_method(association_id) do
79
- klass = options[:class_name].constantize
80
- id = send(options[:foreign_key])
81
-
82
- if id.present?
83
- object = klass.find_by_id(id)
84
- else
85
- nil
86
- end
87
- end
88
-
89
- define_method("#{association_id}=") do |new_value|
90
- attributes[options[:foreign_key].to_sym] = new_value ? new_value.id : nil
91
- end
92
-
93
- end
94
- end
95
-
96
- end
97
- end
@@ -1,156 +0,0 @@
1
- require 'active_repository/associations'
2
- require 'active_repository/uniqueness'
3
- require 'active_repository/write_support'
4
- require 'active_repository/sql_query_executor'
5
- require 'active_repository/finders'
6
- require 'active_repository/writers'
7
-
8
- module ActiveRepository
9
-
10
- class Base < ActiveHash::Base
11
- extend ActiveModel::Callbacks
12
- extend ActiveRepository::Finders
13
- extend ActiveRepository::Writers
14
- include ActiveModel::Validations
15
- include ActiveModel::Validations::Callbacks
16
- include ActiveRepository::Associations
17
- include ActiveRepository::Writers::InstanceMethods
18
-
19
- class_attribute :model_class, :save_in_memory, :instance_writer => false
20
-
21
- before_validation :set_timestamps
22
-
23
- fields :created_at, :updated_at
24
-
25
- def reload
26
- serialize! self.class.get_model_class.find(self.id).attributes
27
- end
28
-
29
- def self.exists?(id)
30
- if self == get_model_class
31
- !find_by_id(id).nil?
32
- else
33
- if mongoid?
34
- find_by_id(id).present?
35
- else
36
- get_model_class.exists?(id)
37
- end
38
- end
39
- end
40
-
41
- def self.all
42
- self == get_model_class ? super : get_model_class.all.map { |object| serialize!(object.attributes) }
43
- end
44
-
45
- def self.delete_all
46
- self == get_model_class ? super : get_model_class.delete_all
47
- end
48
-
49
- def self.where(*args)
50
- raise ArgumentError.new("wrong number of arguments (0 for 1)") if args.empty?
51
- if self == get_model_class
52
- query = ActiveHash::SQLQueryExecutor.args_to_query(args)
53
- super(query)
54
- else
55
- objects = []
56
- args = args.first.is_a?(Hash) ? args.first : args
57
- get_model_class.where(args).each do |object|
58
- objects << self.serialize!(object.attributes)
59
- end
60
-
61
- objects
62
- end
63
- end
64
-
65
- def self.set_model_class(value)
66
- self.model_class = value if model_class.nil?
67
-
68
- field_names.each do |field_name|
69
- define_custom_find_by_field(field_name)
70
- define_custom_find_all_by_field(field_name)
71
- end
72
- end
73
-
74
- def self.set_save_in_memory(value)
75
- self.save_in_memory = value if save_in_memory.nil?
76
- end
77
-
78
- def persist
79
- if self.valid?
80
- save_in_memory? ? save : self.convert
81
- end
82
- end
83
-
84
- def convert(attribute="id")
85
- klass = self.class.get_model_class
86
- object = klass.where(attribute.to_sym => self.send(attribute)).first
87
-
88
- object ||= self.class.get_model_class.new
89
-
90
- attributes = self.attributes
91
-
92
- attributes.delete(:id)
93
-
94
- object.attributes = attributes
95
-
96
- object.save
97
-
98
- self.id = object.id
99
-
100
- object
101
- end
102
-
103
- def serialize!(attributes)
104
- unless attributes.nil?
105
- self.attributes = attributes
106
- end
107
-
108
- self
109
- end
110
-
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
118
- end
119
-
120
- def self.serialized_attributes
121
- field_names.map &:to_s
122
- end
123
-
124
- def self.constantize
125
- self.to_s.constantize
126
- end
127
-
128
- def self.get_model_class
129
- return self if self.save_in_memory.nil?
130
- save_in_memory? ? self : self.model_class
131
- end
132
-
133
- def save_in_memory?
134
- self.save_in_memory.nil? ? true : save_in_memory
135
- end
136
-
137
- protected
138
- def model_class
139
- self.model_class
140
- end
141
-
142
- private
143
- def set_timestamps
144
- self.created_at = DateTime.now.utc if self.new_record?
145
- self.updated_at = DateTime.now.utc
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
155
- end
156
- end
@@ -1,101 +0,0 @@
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
@@ -1,135 +0,0 @@
1
- module ActiveHash
2
- class SQLQueryExecutor
3
- class << self
4
- def execute(klass, query)
5
- @operator, @sub_query, @objects = process_first(klass, query, query.split(" ")[1])
6
-
7
- @operator.nil? ? @objects : @objects.send(@operator, execute(klass, @sub_query)).sort_by{ |o| o.id }
8
- end
9
-
10
- def args_to_query(args)
11
- return args.first if args.size == 1
12
-
13
- query = args.first
14
- param = args.delete(args[1])
15
-
16
- param = convert_param(param)
17
-
18
- args[0] = query.sub("?", param)
19
-
20
- args_to_query(args)
21
- end
22
-
23
- private
24
- def process_first(klass, query, operator)
25
- @operator = operator == "=" ? "==" : operator
26
- @query = sanitize_query(query)
27
- sub_query = divide_query
28
-
29
- binding_operator = get_operator(sub_query)
30
-
31
- objects = execute_sub_query(klass, sub_query)
32
-
33
- sub_query = query.gsub(sub_query.join(" "), "")
34
-
35
- [binding_operator, sub_query, objects]
36
- end
37
-
38
- def sanitize_query(query)
39
- new_query = query
40
- params = query.scan(/([\"'])(.*?)\1/)
41
-
42
- params.each do |quote, param|
43
- new_query = new_query.gsub(quote,"").gsub(param, param.gsub(" ", "_"))
44
- end
45
-
46
- new_query
47
- end
48
-
49
- def divide_query
50
- array = @query.split(" ")
51
- case @operator
52
- when "between"
53
- array[0..5]
54
- when "is"
55
- size = array[2].downcase == "not" ? 4 : 3
56
- array[0..size]
57
- else
58
- array[0..3]
59
- end
60
- end
61
-
62
- def get_operator(attributes)
63
- operator = attributes.size >= 4 ? attributes.last : nil
64
-
65
- case operator
66
- when "or"
67
- "+"
68
- when "and"
69
- "&"
70
- else
71
- nil
72
- end
73
- end
74
-
75
- def execute_sub_query(klass, sub_query)
76
- case @operator
77
- when "between"
78
- execute_between(klass, sub_query)
79
- when "is"
80
- execute_is(klass, sub_query)
81
- else
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])
105
-
106
- field.blank? ? false : field.send(@operator, attribute)
107
- end
108
- end
109
-
110
- def convert_param(param)
111
- case param.class.name
112
- when "String"
113
- param = "'#{param}'"
114
- when "Date"
115
- param = "'#{param.strftime("%Y-%m-%d")}'"
116
- when "Time"
117
- param = "'#{param.strftime("%Y-%m-%d %H:%M:%S %z")}'"
118
- else
119
- param = param.to_s
120
- end
121
- end
122
-
123
- def convert_attrs(field, *attrs)
124
- attrs.each_with_index do |attribute, i|
125
- attribute = attribute.gsub("_", " ")
126
- attrs[i] = field.is_a?(Integer) ? attribute.to_i : attribute
127
- end
128
-
129
- field = field.is_a?(Integer) ? field : field.to_s
130
-
131
- [field, attrs].flatten
132
- end
133
- end
134
- end
135
- end
@@ -1,196 +0,0 @@
1
- module ActiveModel
2
- module Validations
3
- class UniquenessValidator < ActiveModel::EachValidator #:nodoc:
4
- def initialize(options)
5
- super(options.reverse_merge(:case_sensitive => true))
6
- end
7
-
8
- # Unfortunately, we have to tie Uniqueness validators to a class.
9
- def setup(klass)
10
- @klass = klass
11
- end
12
-
13
- def validate_each(record, attribute, value)
14
- finder_class = record.class.get_model_class
15
-
16
- finder_class.all.each do |object|
17
- if object.id != record.id && object.send(attribute) == record.send(attribute)
18
- record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope, :conditions).merge(:value => value))
19
- break
20
- end
21
- end
22
- end
23
-
24
- protected
25
-
26
- # The check for an existing value should be run from a class that
27
- # isn't abstract. This means working down from the current class
28
- # (self), to the first non-abstract class. Since classes don't know
29
- # their subclasses, we have to build the hierarchy between self and
30
- # the record's class.
31
- def find_finder_class_for(record) #:nodoc:
32
- class_hierarchy = [record.class]
33
-
34
- while class_hierarchy.first != @klass
35
- class_hierarchy.prepend(class_hierarchy.first.superclass)
36
- end
37
-
38
- class_hierarchy.detect { |klass| klass.respond_to?(:abstract_class?) ? !klass.abstract_class? : true }
39
- end
40
-
41
- def build_relation(klass, table, attribute, value) #:nodoc:
42
- reflection = klass.reflect_on_association(attribute)
43
- if reflection
44
- column = klass.columns_hash[reflection.foreign_key]
45
- attribute = reflection.foreign_key
46
- value = value.attributes[reflection.primary_key_column.name]
47
- else
48
- column = klass.columns_hash[attribute.to_s]
49
- end
50
- value = column.limit ? value.to_s[0, column.limit] : value.to_s if !value.nil? && column.text?
51
-
52
- if !options[:case_sensitive] && value && column.text?
53
- # will use SQL LOWER function before comparison, unless it detects a case insensitive collation
54
- relation = klass.connection.case_insensitive_comparison(table, attribute, column, value)
55
- else
56
- value = klass.connection.case_sensitive_modifier(value) unless value.nil?
57
- relation = table[attribute].eq(value)
58
- end
59
-
60
- relation
61
- end
62
- end
63
-
64
- module ClassMethods
65
- # Validates whether the value of the specified attributes are unique
66
- # across the system. Useful for making sure that only one user
67
- # can be named "davidhh".
68
- #
69
- # class Person < ActiveRecord::Base
70
- # validates_uniqueness_of :user_name
71
- # end
72
- #
73
- # It can also validate whether the value of the specified attributes are
74
- # unique based on a <tt>:scope</tt> parameter:
75
- #
76
- # class Person < ActiveRecord::Base
77
- # validates_uniqueness_of :user_name, scope: :account_id
78
- # end
79
- #
80
- # Or even multiple scope parameters. For example, making sure that a
81
- # teacher can only be on the schedule once per semester for a particular
82
- # class.
83
- #
84
- # class TeacherSchedule < ActiveRecord::Base
85
- # validates_uniqueness_of :teacher_id, scope: [:semester_id, :class_id]
86
- # end
87
- #
88
- # It is also possible to limit the uniqueness constraint to a set of
89
- # records matching certain conditions. In this example archived articles
90
- # are not being taken into consideration when validating uniqueness
91
- # of the title attribute:
92
- #
93
- # class Article < ActiveRecord::Base
94
- # validates_uniqueness_of :title, conditions: where('status != ?', 'archived')
95
- # end
96
- #
97
- # When the record is created, a check is performed to make sure that no
98
- # record exists in the database with the given value for the specified
99
- # attribute (that maps to a column). When the record is updated,
100
- # the same check is made but disregarding the record itself.
101
- #
102
- # Configuration options:
103
- #
104
- # * <tt>:message</tt> - Specifies a custom error message (default is:
105
- # "has already been taken").
106
- # * <tt>:scope</tt> - One or more columns by which to limit the scope of
107
- # the uniqueness constraint.
108
- # * <tt>:conditions</tt> - Specify the conditions to be included as a
109
- # <tt>WHERE</tt> SQL fragment to limit the uniqueness constraint lookup
110
- # (e.g. <tt>conditions: where('status = ?', 'active')</tt>).
111
- # * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by
112
- # non-text columns (+true+ by default).
113
- # * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the
114
- # attribute is +nil+ (default is +false+).
115
- # * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the
116
- # attribute is blank (default is +false+).
117
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
118
- # if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
119
- # or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
120
- # proc or string should return or evaluate to a +true+ or +false+ value.
121
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to
122
- # determine if the validation should ot occur (e.g. <tt>unless: :skip_validation</tt>,
123
- # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
124
- # method, proc or string should return or evaluate to a +true+ or +false+
125
- # value.
126
- #
127
- # === Concurrency and integrity
128
- #
129
- # Using this validation method in conjunction with ActiveRecord::Base#save
130
- # does not guarantee the absence of duplicate record insertions, because
131
- # uniqueness checks on the application level are inherently prone to race
132
- # conditions. For example, suppose that two users try to post a Comment at
133
- # the same time, and a Comment's title must be unique. At the database-level,
134
- # the actions performed by these users could be interleaved in the following manner:
135
- #
136
- # User 1 | User 2
137
- # ------------------------------------+--------------------------------------
138
- # # User 1 checks whether there's |
139
- # # already a comment with the title |
140
- # # 'My Post'. This is not the case. |
141
- # SELECT * FROM comments |
142
- # WHERE title = 'My Post' |
143
- # |
144
- # | # User 2 does the same thing and also
145
- # | # infers that his title is unique.
146
- # | SELECT * FROM comments
147
- # | WHERE title = 'My Post'
148
- # |
149
- # # User 1 inserts his comment. |
150
- # INSERT INTO comments |
151
- # (title, content) VALUES |
152
- # ('My Post', 'hi!') |
153
- # |
154
- # | # User 2 does the same thing.
155
- # | INSERT INTO comments
156
- # | (title, content) VALUES
157
- # | ('My Post', 'hello!')
158
- # |
159
- # | # ^^^^^^
160
- # | # Boom! We now have a duplicate
161
- # | # title!
162
- #
163
- # This could even happen if you use transactions with the 'serializable'
164
- # isolation level. The best way to work around this problem is to add a unique
165
- # index to the database table using
166
- # ActiveRecord::ConnectionAdapters::SchemaStatements#add_index. In the
167
- # rare case that a race condition occurs, the database will guarantee
168
- # the field's uniqueness.
169
- #
170
- # When the database catches such a duplicate insertion,
171
- # ActiveRecord::Base#save will raise an ActiveRecord::StatementInvalid
172
- # exception. You can either choose to let this error propagate (which
173
- # will result in the default Rails exception page being shown), or you
174
- # can catch it and restart the transaction (e.g. by telling the user
175
- # that the title already exists, and asking him to re-enter the title).
176
- # This technique is also known as optimistic concurrency control:
177
- # http://en.wikipedia.org/wiki/Optimistic_concurrency_control
178
- #
179
- # The bundled ActiveRecord::ConnectionAdapters distinguish unique index
180
- # constraint errors from other types of database errors by throwing an
181
- # ActiveRecord::RecordNotUnique exception. For other adapters you will
182
- # have to parse the (database-specific) exception message to detect such
183
- # a case.
184
- #
185
- # The following bundled adapters throw the ActiveRecord::RecordNotUnique exception:
186
- #
187
- # * ActiveRecord::ConnectionAdapters::MysqlAdapter
188
- # * ActiveRecord::ConnectionAdapters::Mysql2Adapter
189
- # * ActiveRecord::ConnectionAdapters::SQLite3Adapter
190
- # * ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
191
- def validates_uniqueness_of(*attr_names)
192
- validates_with UniquenessValidator, _merge_attributes(attr_names)
193
- end
194
- end
195
- end
196
- end
@@ -1,3 +0,0 @@
1
- module ActiveRepository
2
- VERSION = "0.0.3"
3
- end
@@ -1,93 +0,0 @@
1
- require 'active_hash'
2
- require 'active_repository/sql_query_executor'
3
-
4
- begin
5
- klass = Module.const_get(ActiveRecord::Rollback)
6
- unless klass.is_a?(Class)
7
- raise "Not defined"
8
- end
9
- rescue
10
- module ActiveRecord
11
- class ActiveRecordError < StandardError
12
- end
13
- class Rollback < ActiveRecord::ActiveRecordError
14
- end
15
- end
16
- end
17
-
18
- module ActiveHash
19
- class Base
20
- def self.insert(record)
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)
26
- self.all.delete(record)
27
- end
28
-
29
- if record_index[record_id].nil? || !self.all.map(&:hash).include?(record_hash)
30
- insert_record(record)
31
- end
32
- end
33
-
34
- def self.where(query)
35
- if query.is_a?(String)
36
- return ActiveHash::SQLQueryExecutor.execute(self, query)
37
- else
38
- (@records || []).select do |record|
39
- query.all? { |col, match| record[col] == match }
40
- end
41
- end
42
- end
43
-
44
- def self.validate_unique_id(record)
45
- raise IdError.new("Duplicate Id found for record #{record.attributes}") if record_index.has_key?(record.id.to_s)
46
- end
47
-
48
- def update_attribute(key, value)
49
- self.send("#{key}=", value)
50
- self.save(:validate => false)
51
- end
52
-
53
- def readonly?
54
- false
55
- end
56
-
57
- def save(*args)
58
- record = self.class.find_by_id(self.id)
59
-
60
- self.class.insert(self) if record.nil? && record != self
61
- true
62
- end
63
-
64
- def to_param
65
- id.present? ? id.to_s : nil
66
- end
67
-
68
- def persisted?
69
- other = self.class.find_by_id(id)
70
- other.present?
71
- end
72
-
73
- def eql?(other)
74
- (other.instance_of?(self.class) || other.instance_of?(self.class.get_model_class)) && id.present? && (id == other.id) && (created_at == other.created_at)
75
- end
76
-
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
92
- end
93
- end
@@ -1,67 +0,0 @@
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
data/support/mongoid.yml DELETED
@@ -1,6 +0,0 @@
1
- development:
2
- sessions:
3
- default:
4
- database: active_repository
5
- hosts:
6
- - localhost:27017