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