module Sequel::Plugins::Finder::ClassMethods

  1. lib/sequel/plugins/finder.rb

Methods

Public Instance

  1. finder
  2. freeze
  3. prepared_finder

Public Instance methods

finder (meth=OPTS, opts=OPTS, &block)

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 Sequel creates accepts an arbitrary number of arguments, so you should use this option in that case. If you want to handle multiple possible arities, you need to call the finder method multiple times with unique :arity and :name methods each time.

: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.

[show source]
    # 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
freeze ()
[show source]
    # 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
prepared_finder (meth=OPTS, opts=OPTS, &block)

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

[show source]
    # 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