activerecord-deprecated_finders 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/.travis.yml +8 -0
- data/Gemfile +10 -0
- data/LICENSE +22 -0
- data/README.md +15 -0
- data/Rakefile +28 -0
- data/activerecord-deprecated_finders.gemspec +21 -0
- data/lib/active_record/deprecated_finders.rb +10 -0
- data/lib/active_record/deprecated_finders/association_builder.rb +78 -0
- data/lib/active_record/deprecated_finders/base.rb +151 -0
- data/lib/active_record/deprecated_finders/collection_proxy.rb +32 -0
- data/lib/active_record/deprecated_finders/dynamic_matchers.rb +204 -0
- data/lib/active_record/deprecated_finders/relation.rb +176 -0
- data/lib/active_record/deprecated_finders/version.rb +5 -0
- data/test/associations_test.rb +67 -0
- data/test/calculate_test.rb +15 -0
- data/test/default_scope_test.rb +35 -0
- data/test/dynamic_methods_test.rb +133 -0
- data/test/find_in_batches_test.rb +18 -0
- data/test/finder_options_test.rb +86 -0
- data/test/finder_test.rb +69 -0
- data/test/helper.rb +37 -0
- data/test/scope_test.rb +17 -0
- data/test/scoped_test.rb +20 -0
- data/test/update_all_test.rb +24 -0
- data/test/with_scope_test.rb +36 -0
- metadata +131 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Jon Leighton
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# Active Record Deprecated Finders
|
2
|
+
|
3
|
+
This gem will be used to extract and deprecate old-style finder option
|
4
|
+
hashes in Active Record:
|
5
|
+
|
6
|
+
``` ruby
|
7
|
+
Post.find(:all, conditions: { published_on: 2.weeks.ago }, limit: 5)
|
8
|
+
```
|
9
|
+
|
10
|
+
It will be a dependency of Rails 4.0 to provide the deprecated
|
11
|
+
functionality.
|
12
|
+
|
13
|
+
It will be removed as a dependency in Rails 4.1, but users can manually
|
14
|
+
include it in their Gemfile and it will continue to be maintained until
|
15
|
+
Rails 5.
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
Rake::TestTask.new do |t|
|
6
|
+
t.libs = ["test"]
|
7
|
+
t.pattern = "test/**/*_test.rb"
|
8
|
+
t.ruby_opts = ['-w']
|
9
|
+
end
|
10
|
+
|
11
|
+
task :default => :test
|
12
|
+
|
13
|
+
specname = "activerecord-deprecated_finders.gemspec"
|
14
|
+
deps = `git ls-files`.split("\n") - [specname]
|
15
|
+
|
16
|
+
task :gemspec => specname
|
17
|
+
|
18
|
+
file specname => deps do
|
19
|
+
files = `git ls-files`.split("\n")
|
20
|
+
test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
|
23
|
+
require 'erb'
|
24
|
+
|
25
|
+
File.open specname, 'w:utf-8' do |f|
|
26
|
+
f.write ERB.new(File.read("#{specname}.erb")).result(binding)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/active_record/deprecated_finders/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Jon Leighton"]
|
6
|
+
gem.email = ["j@jonathanleighton.com"]
|
7
|
+
gem.description = %q{This gem contains deprecated finder APIs extracted from Active Record.}
|
8
|
+
gem.summary = %q{This gem contains deprecated finder APIs extracted from Active Record.}
|
9
|
+
gem.homepage = "https://github.com/rails/activerecord-deprecated_finders"
|
10
|
+
|
11
|
+
gem.files = [".gitignore",".travis.yml","Gemfile","LICENSE","README.md","Rakefile","activerecord-deprecated_finders.gemspec","lib/active_record/deprecated_finders.rb","lib/active_record/deprecated_finders/association_builder.rb","lib/active_record/deprecated_finders/base.rb","lib/active_record/deprecated_finders/collection_proxy.rb","lib/active_record/deprecated_finders/dynamic_matchers.rb","lib/active_record/deprecated_finders/relation.rb","lib/active_record/deprecated_finders/version.rb","test/associations_test.rb","test/calculate_test.rb","test/default_scope_test.rb","test/dynamic_methods_test.rb","test/find_in_batches_test.rb","test/finder_options_test.rb","test/finder_test.rb","test/helper.rb","test/scope_test.rb","test/scoped_test.rb","test/update_all_test.rb","test/with_scope_test.rb"]
|
12
|
+
gem.test_files = ["test/associations_test.rb","test/calculate_test.rb","test/default_scope_test.rb","test/dynamic_methods_test.rb","test/find_in_batches_test.rb","test/finder_options_test.rb","test/finder_test.rb","test/helper.rb","test/scope_test.rb","test/scoped_test.rb","test/update_all_test.rb","test/with_scope_test.rb"]
|
13
|
+
gem.executables = []
|
14
|
+
gem.name = "activerecord-deprecated_finders"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = ActiveRecord::DeprecatedFinders::VERSION
|
17
|
+
|
18
|
+
gem.add_development_dependency 'minitest', '>= 3'
|
19
|
+
gem.add_development_dependency 'activerecord', '~> 4.0.0.beta'
|
20
|
+
gem.add_development_dependency 'sqlite3', '~> 1.3'
|
21
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'active_support/lazy_load_hooks'
|
2
|
+
require 'active_record/deprecated_finders/version'
|
3
|
+
|
4
|
+
ActiveSupport.on_load(:active_record) do
|
5
|
+
require 'active_record/deprecated_finders/base'
|
6
|
+
require 'active_record/deprecated_finders/relation'
|
7
|
+
require 'active_record/deprecated_finders/dynamic_matchers'
|
8
|
+
require 'active_record/deprecated_finders/collection_proxy'
|
9
|
+
require 'active_record/deprecated_finders/association_builder'
|
10
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'active_record/associations/builder/association'
|
2
|
+
require 'active_support/core_ext/module/aliasing'
|
3
|
+
require 'active_support/deprecation'
|
4
|
+
|
5
|
+
module ActiveRecord::Associations::Builder
|
6
|
+
class DeprecatedOptionsProc
|
7
|
+
attr_reader :options
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
options[:includes] = options.delete(:include) if options[:include]
|
11
|
+
options[:where] = options.delete(:conditions) if options[:conditions]
|
12
|
+
options[:extending] = options.delete(:extend) if options[:extend]
|
13
|
+
|
14
|
+
@options = options
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_proc
|
18
|
+
options = self.options
|
19
|
+
proc do |owner|
|
20
|
+
if options[:where].respond_to?(:to_proc)
|
21
|
+
context = owner || self
|
22
|
+
where(context.instance_eval(&options[:where]))
|
23
|
+
.merge!(options.except(:where))
|
24
|
+
else
|
25
|
+
merge(options)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def arity
|
31
|
+
1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Association
|
36
|
+
DEPRECATED_OPTIONS = [:readonly, :order, :limit, :group, :having,
|
37
|
+
:offset, :select, :uniq, :include, :conditions, :extend]
|
38
|
+
|
39
|
+
self.valid_options += [:select, :conditions, :include, :extend, :readonly]
|
40
|
+
|
41
|
+
def initialize_with_deprecated_options(model, name, scope, options)
|
42
|
+
if scope.is_a?(Hash)
|
43
|
+
options = scope
|
44
|
+
deprecated_options = options.slice(*DEPRECATED_OPTIONS)
|
45
|
+
|
46
|
+
if deprecated_options.empty?
|
47
|
+
scope = nil
|
48
|
+
else
|
49
|
+
ActiveSupport::Deprecation.warn(
|
50
|
+
"The following options in your #{model.name}.#{macro} :#{name} declaration are deprecated: " \
|
51
|
+
"#{deprecated_options.keys.map(&:inspect).join(',')}. Please use a scope block instead. " \
|
52
|
+
"For example, the following:\n" \
|
53
|
+
"\n" \
|
54
|
+
" has_many :spam_comments, conditions: { spam: true }, class_name: 'Comment'\n" \
|
55
|
+
"\n" \
|
56
|
+
"should be rewritten as the following:\n" \
|
57
|
+
"\n" \
|
58
|
+
" has_many :spam_comments, -> { where spam: true }, class_name: 'Comment'\n"
|
59
|
+
)
|
60
|
+
scope = DeprecatedOptionsProc.new(deprecated_options)
|
61
|
+
options = options.except(*DEPRECATED_OPTIONS)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
initialize_without_deprecated_options(model, name, scope, options)
|
66
|
+
end
|
67
|
+
|
68
|
+
alias_method_chain :initialize, :deprecated_options
|
69
|
+
end
|
70
|
+
|
71
|
+
class CollectionAssociation
|
72
|
+
include Module.new {
|
73
|
+
def valid_options
|
74
|
+
super + [:order, :group, :having, :limit, :offset, :uniq]
|
75
|
+
end
|
76
|
+
}
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'active_support/deprecation'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module DeprecatedFinders
|
5
|
+
class ScopeWrapper
|
6
|
+
def self.wrap(klass, scope)
|
7
|
+
if scope.is_a?(Hash)
|
8
|
+
ActiveSupport::Deprecation.warn(
|
9
|
+
"Calling #scope or #default_scope with a hash is deprecated. Please use a lambda " \
|
10
|
+
"containing a scope. E.g. scope :red, -> { where(color: 'red') }"
|
11
|
+
)
|
12
|
+
|
13
|
+
new(klass, scope)
|
14
|
+
elsif !scope.is_a?(Relation) && scope.respond_to?(:call)
|
15
|
+
new(klass, scope)
|
16
|
+
else
|
17
|
+
scope
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(klass, scope)
|
22
|
+
@klass = klass
|
23
|
+
@scope = scope
|
24
|
+
end
|
25
|
+
|
26
|
+
def call(*args)
|
27
|
+
if @scope.respond_to?(:call)
|
28
|
+
result = @scope.call(*args)
|
29
|
+
|
30
|
+
if result.is_a?(Hash)
|
31
|
+
msg = "Returning a hash from a #scope or #default_scope block is deprecated. Please " \
|
32
|
+
"return an actual scope object instead. E.g. scope :red, -> { where(color: 'red') } " \
|
33
|
+
"rather than scope :red, -> { { conditions: { color: 'red' } } }. "
|
34
|
+
|
35
|
+
if @scope.respond_to?(:source_location)
|
36
|
+
msg << "(The scope was defined at #{@scope.source_location.join(':')}.)"
|
37
|
+
end
|
38
|
+
|
39
|
+
ActiveSupport::Deprecation.warn(msg)
|
40
|
+
end
|
41
|
+
else
|
42
|
+
result = @scope
|
43
|
+
end
|
44
|
+
|
45
|
+
if result.is_a?(Hash)
|
46
|
+
@klass.unscoped.apply_finder_options(result, true)
|
47
|
+
else
|
48
|
+
result
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def default_scope(scope = {}, &block)
|
54
|
+
if block_given?
|
55
|
+
super ScopeWrapper.new(self, block), &nil
|
56
|
+
else
|
57
|
+
super ScopeWrapper.wrap(self, scope)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def scoped(options = nil)
|
62
|
+
ActiveSupport::Deprecation.warn("Model.scoped is deprecated. Please use Model.all instead.")
|
63
|
+
options ? all.apply_finder_options(options, true) : all
|
64
|
+
end
|
65
|
+
|
66
|
+
def all(options = nil)
|
67
|
+
options ? super().all(options) : super()
|
68
|
+
end
|
69
|
+
|
70
|
+
def scope(name, body = {}, &block)
|
71
|
+
super(name, ScopeWrapper.wrap(self, body), &block)
|
72
|
+
end
|
73
|
+
|
74
|
+
def with_scope(scope = {}, action = :merge)
|
75
|
+
ActiveSupport::Deprecation.warn(
|
76
|
+
"ActiveRecord::Base#with_scope and #with_exclusive_scope are deprecated. " \
|
77
|
+
"Please use ActiveRecord::Relation#scoping instead. (You can use #merge " \
|
78
|
+
"to merge multiple scopes together.)"
|
79
|
+
)
|
80
|
+
|
81
|
+
# If another Active Record class has been passed in, get its current scope
|
82
|
+
scope = scope.current_scope if !scope.is_a?(Relation) && scope.respond_to?(:current_scope)
|
83
|
+
|
84
|
+
previous_scope = self.current_scope
|
85
|
+
|
86
|
+
if scope.is_a?(Hash)
|
87
|
+
# Dup first and second level of hash (method and params).
|
88
|
+
scope = scope.dup
|
89
|
+
scope.each do |method, params|
|
90
|
+
scope[method] = params.dup unless params == true
|
91
|
+
end
|
92
|
+
|
93
|
+
scope.assert_valid_keys([ :find, :create ])
|
94
|
+
relation = construct_finder_arel(scope[:find] || {})
|
95
|
+
relation.default_scoped = true unless action == :overwrite
|
96
|
+
|
97
|
+
if previous_scope && previous_scope.create_with_value && scope[:create]
|
98
|
+
scope_for_create = if action == :merge
|
99
|
+
previous_scope.create_with_value.merge(scope[:create])
|
100
|
+
else
|
101
|
+
scope[:create]
|
102
|
+
end
|
103
|
+
|
104
|
+
relation = relation.create_with(scope_for_create)
|
105
|
+
else
|
106
|
+
scope_for_create = scope[:create]
|
107
|
+
scope_for_create ||= previous_scope.create_with_value if previous_scope
|
108
|
+
relation = relation.create_with(scope_for_create) if scope_for_create
|
109
|
+
end
|
110
|
+
|
111
|
+
scope = relation
|
112
|
+
end
|
113
|
+
|
114
|
+
scope = previous_scope.merge(scope) if previous_scope && action == :merge
|
115
|
+
scope.scoping { yield }
|
116
|
+
end
|
117
|
+
|
118
|
+
protected
|
119
|
+
|
120
|
+
# Works like with_scope, but discards any nested properties.
|
121
|
+
def with_exclusive_scope(method_scoping = {}, &block)
|
122
|
+
if method_scoping.values.any? { |e| e.is_a?(ActiveRecord::Relation) }
|
123
|
+
raise ArgumentError, <<-MSG
|
124
|
+
New finder API can not be used with_exclusive_scope. You can either call unscoped to get an anonymous scope not bound to the default_scope:
|
125
|
+
|
126
|
+
User.unscoped.where(:active => true)
|
127
|
+
|
128
|
+
Or call unscoped with a block:
|
129
|
+
|
130
|
+
User.unscoped do
|
131
|
+
User.where(:active => true).all
|
132
|
+
end
|
133
|
+
|
134
|
+
MSG
|
135
|
+
end
|
136
|
+
with_scope(method_scoping, :overwrite, &block)
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
def construct_finder_arel(options = {}, scope = nil)
|
142
|
+
relation = options.is_a?(Hash) ? unscoped.apply_finder_options(options, true) : options
|
143
|
+
relation = scope.merge(relation) if scope
|
144
|
+
relation
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
class Base
|
149
|
+
extend DeprecatedFinders
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class CollectionProxy
|
4
|
+
module InterceptDynamicInstantiators
|
5
|
+
def method_missing(method, *args, &block)
|
6
|
+
match = DynamicMatchers::Method.match(klass, method)
|
7
|
+
|
8
|
+
if match && match.is_a?(DynamicMatchers::Instantiator)
|
9
|
+
scoping do
|
10
|
+
klass.send(method, *args) do |record|
|
11
|
+
proxy_association.add_to_target(record)
|
12
|
+
yield record if block_given?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
else
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.inherited(subclass)
|
22
|
+
subclass.class_eval do
|
23
|
+
# Ensure this get included first
|
24
|
+
include ActiveRecord::Delegation::ClassSpecificRelation
|
25
|
+
|
26
|
+
# Now override the method_missing definition
|
27
|
+
include InterceptDynamicInstantiators
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
require 'active_support/deprecation'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module DynamicMatchers
|
5
|
+
module DeprecatedFinder
|
6
|
+
def body
|
7
|
+
<<-CODE
|
8
|
+
result = #{super}
|
9
|
+
result && block_given? ? yield(result) : result
|
10
|
+
CODE
|
11
|
+
end
|
12
|
+
|
13
|
+
def result
|
14
|
+
"all.apply_finder_options(options, true).#{super}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def signature
|
18
|
+
"#{super}, options = {}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module DeprecationWarning
|
23
|
+
def body
|
24
|
+
"#{deprecation_warning}\n#{super}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def deprecation_warning
|
28
|
+
%{ActiveSupport::Deprecation.warn("This dynamic method is deprecated. Please use e.g. #{deprecation_alternative} instead.")}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module FindByDeprecationWarning
|
33
|
+
def body
|
34
|
+
<<-CODE
|
35
|
+
if block_given?
|
36
|
+
ActiveSupport::Deprecation.warn("Calling find_by or find_by! methods with a block is deprecated with no replacement.")
|
37
|
+
end
|
38
|
+
|
39
|
+
unless options.empty?
|
40
|
+
ActiveSupport::Deprecation.warn(
|
41
|
+
"Calling find_by or find_by! methods with options is deprecated. " \
|
42
|
+
"Build a scope instead, e.g. User.where('age > 21').find_by_name('Jon')."
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
#{super}
|
47
|
+
CODE
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class FindBy
|
52
|
+
include DeprecatedFinder
|
53
|
+
include FindByDeprecationWarning
|
54
|
+
end
|
55
|
+
|
56
|
+
class FindByBang
|
57
|
+
include DeprecatedFinder
|
58
|
+
include FindByDeprecationWarning
|
59
|
+
end
|
60
|
+
|
61
|
+
class FindAllBy < Method
|
62
|
+
Method.matchers << self
|
63
|
+
include Finder
|
64
|
+
include DeprecatedFinder
|
65
|
+
include DeprecationWarning
|
66
|
+
|
67
|
+
def self.prefix
|
68
|
+
"find_all_by"
|
69
|
+
end
|
70
|
+
|
71
|
+
def finder
|
72
|
+
"where"
|
73
|
+
end
|
74
|
+
|
75
|
+
def result
|
76
|
+
"#{super}.to_a"
|
77
|
+
end
|
78
|
+
|
79
|
+
def deprecation_alternative
|
80
|
+
"Post.where(...).all"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class FindLastBy < Method
|
85
|
+
Method.matchers << self
|
86
|
+
include Finder
|
87
|
+
include DeprecatedFinder
|
88
|
+
include DeprecationWarning
|
89
|
+
|
90
|
+
def self.prefix
|
91
|
+
"find_last_by"
|
92
|
+
end
|
93
|
+
|
94
|
+
def finder
|
95
|
+
"where"
|
96
|
+
end
|
97
|
+
|
98
|
+
def result
|
99
|
+
"#{super}.last"
|
100
|
+
end
|
101
|
+
|
102
|
+
def deprecation_alternative
|
103
|
+
"Post.where(...).last"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class ScopedBy < Method
|
108
|
+
Method.matchers << self
|
109
|
+
include Finder
|
110
|
+
include DeprecationWarning
|
111
|
+
|
112
|
+
def self.prefix
|
113
|
+
"scoped_by"
|
114
|
+
end
|
115
|
+
|
116
|
+
def body
|
117
|
+
"#{deprecation_warning} \n where(#{attributes_hash})"
|
118
|
+
end
|
119
|
+
|
120
|
+
def deprecation_alternative
|
121
|
+
"Post.where(...)"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class Instantiator < Method
|
126
|
+
include DeprecationWarning
|
127
|
+
|
128
|
+
def self.dispatch(klass, attribute_names, instantiator, args, block)
|
129
|
+
if args.length == 1 && args.first.is_a?(Hash)
|
130
|
+
attributes = args.first.stringify_keys
|
131
|
+
conditions = attributes.slice(*attribute_names)
|
132
|
+
rest = [attributes.except(*attribute_names)]
|
133
|
+
else
|
134
|
+
raise ArgumentError, "too few arguments" unless args.length >= attribute_names.length
|
135
|
+
|
136
|
+
conditions = Hash[attribute_names.map.with_index { |n, i| [n, args[i]] }]
|
137
|
+
rest = args.drop(attribute_names.length)
|
138
|
+
end
|
139
|
+
|
140
|
+
klass.where(conditions).first ||
|
141
|
+
klass.create_with(conditions).send(instantiator, *rest, &block)
|
142
|
+
end
|
143
|
+
|
144
|
+
def signature
|
145
|
+
"*args, &block"
|
146
|
+
end
|
147
|
+
|
148
|
+
def body
|
149
|
+
<<-CODE
|
150
|
+
#{deprecation_warning}
|
151
|
+
#{self.class}.dispatch(self, #{attribute_names.inspect}, #{instantiator.inspect}, args, block)
|
152
|
+
CODE
|
153
|
+
end
|
154
|
+
|
155
|
+
def instantiator
|
156
|
+
raise NotImplementedError
|
157
|
+
end
|
158
|
+
|
159
|
+
def deprecation_alternative
|
160
|
+
"Post.#{self.class.prefix}#{self.class.suffix}(name: 'foo')"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class FindOrInitializeBy < Instantiator
|
165
|
+
Method.matchers << self
|
166
|
+
|
167
|
+
def self.prefix
|
168
|
+
"find_or_initialize_by"
|
169
|
+
end
|
170
|
+
|
171
|
+
def instantiator
|
172
|
+
"new"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
class FindOrCreateBy < Instantiator
|
177
|
+
Method.matchers << self
|
178
|
+
|
179
|
+
def self.prefix
|
180
|
+
"find_or_create_by"
|
181
|
+
end
|
182
|
+
|
183
|
+
def instantiator
|
184
|
+
"create"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
class FindOrCreateByBang < Instantiator
|
189
|
+
Method.matchers << self
|
190
|
+
|
191
|
+
def self.prefix
|
192
|
+
"find_or_create_by"
|
193
|
+
end
|
194
|
+
|
195
|
+
def self.suffix
|
196
|
+
"!"
|
197
|
+
end
|
198
|
+
|
199
|
+
def instantiator
|
200
|
+
"create!"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|