Public Instance methods
Create an optimized finder method using a dataset placeholder literalizer. This pre-computes the SQL
to use for the query, except for given arguments.
There are two ways to use this. The recommended way is to pass a symbol that represents a model class method that returns a dataset:
def Artist.by_name(name) where(name: name) end Artist.finder :by_name
This creates an optimized first_by_name method, which you can call normally:
Artist.first_by_name("Joe")
The alternative way to use this to pass your own block:
Artist.finder(name: :first_by_name){|pl, ds| ds.where(name: pl.arg).limit(1)}
Note that if you pass your own block, you are responsible for manually setting limits if necessary (as shown above).
Options:
:arity |
When using a symbol method name, this specifies the arity of the method. This should be used if if the method accepts an arbitrary number of arguments, or the method has default argument values. Note that if the method is defined as a dataset method, the class method |
:name |
The name of the method to create. This must be given if you pass a block. If you use a symbol, this defaults to the symbol prefixed by the type. |
:mod |
The module in which to create the finder method. Defaults to the singleton class of the model. |
:type |
The type of query to run. Can be :first, :each, :all, or :get, defaults to :first. |
Caveats:
This doesn’t handle all possible cases. For example, if you have a method such as:
def Artist.by_name(name) name ? where(name: name) : exclude(name: nil) end
Then calling a finder without an argument will not work as you expect.
Artist.finder :by_name Artist.by_name(nil).first # WHERE (name IS NOT NULL) Artist.first_by_name(nil) # WHERE (name IS NULL)
See Dataset::PlaceholderLiteralizer for additional caveats.
# File lib/sequel/plugins/finder.rb 101 def finder(meth=OPTS, opts=OPTS, &block) 102 if block 103 raise Error, "cannot pass both a method name argument and a block of Model.finder" unless meth.is_a?(Hash) 104 raise Error, "cannot pass two option hashes to Model.finder" unless opts.equal?(OPTS) 105 opts = meth 106 raise Error, "must provide method name via :name option when passing block to Model.finder" unless meth_name = opts[:name] 107 end 108 109 type = opts.fetch(:type, :first) 110 unless prepare = opts[:prepare] 111 raise Error, ":type option to Model.finder must be :first, :all, :each, or :get" unless FINDER_TYPES.include?(type) 112 end 113 limit1 = type == :first || type == :get 114 meth_name ||= opts[:name] || :"#{type}_#{meth}" 115 116 argn = lambda do |model| 117 if arity = opts[:arity] 118 arity 119 else 120 method = block || model.method(meth) 121 (method.arity < 0 ? method.arity.abs - 1 : method.arity) 122 end 123 end 124 125 loader_proc = if prepare 126 proc do |model| 127 args = prepare_method_args('$a', argn.call(model)) 128 ds = if block 129 model.instance_exec(*args, &block) 130 else 131 model.public_send(meth, *args) 132 end 133 ds = ds.limit(1) if limit1 134 model_name = model.name 135 if model_name.to_s.empty? 136 model_name = model.object_id 137 else 138 model_name = model_name.gsub(/\W/, '_') 139 end 140 ds.prepare(type, :"#{model_name}_#{meth_name}") 141 end 142 else 143 proc do |model| 144 n = argn.call(model) 145 block ||= lambda do |pl, model2| 146 args = (0...n).map{pl.arg} 147 ds = model2.public_send(meth, *args) 148 ds = ds.limit(1) if limit1 149 ds 150 end 151 152 Sequel::Dataset::PlaceholderLiteralizer.loader(model, &block) 153 end 154 end 155 156 @finder_loaders[meth_name] = loader_proc 157 mod = opts[:mod] || singleton_class 158 if prepare 159 def_prepare_method(mod, meth_name) 160 else 161 def_finder_method(mod, meth_name, type) 162 end 163 end
# File lib/sequel/plugins/finder.rb 165 def freeze 166 @finder_loaders.freeze 167 @finder_loaders.each_key{|k| finder_for(k)} if @dataset 168 @finders.freeze 169 super 170 end
Similar to finder, but uses a prepared statement instead of a placeholder literalizer. This makes the SQL
used static (cannot vary per call), but allows binding argument values instead of literalizing them into the SQL
query string.
If a block is used with this method, it is instance_execed by the model, and should accept the desired number of placeholder arguments.
The options are the same as the options for finder, with the following exception:
:type |
Specifies the type of prepared statement to create |
# File lib/sequel/plugins/finder.rb 183 def prepared_finder(meth=OPTS, opts=OPTS, &block) 184 if block 185 raise Error, "cannot pass both a method name argument and a block of Model.finder" unless meth.is_a?(Hash) 186 meth = meth.merge(:prepare=>true) 187 else 188 opts = opts.merge(:prepare=>true) 189 end 190 finder(meth, opts, &block) 191 end