rubysequel

How to return all Dataset by Sequel query


Rails scope returns all(ActiveRecord::Relation) instead of nil. So that I can use method chain when condition is nil.

class MyClass < ApplicationRecord
  scope :my_filter, ->(condition) { where(condition: condition) if condition.present? }
end

MyClass.where(foo: 'foo').my_filter(condition).order(:date)

I want to implement similar functionality in Sequel, but this code doesn't work well because all in Sequel returns an Array. How can I write a functionality in Sequel similar to Rails scope?

class MyClass < Sequel::Model
  dataset_module do
    def my_filter(condition)
      return all if condition.nil?
      where(condition: condition) 
    end
  end
end

Solution

  • Since where returns a Dataset I am assuming you would also like "all" to return the same object.

    As you have identified all will return an Array; however you can retrieve the Dataset by using either clone or self

    So we can just change your implementation to:

    class MyClass < Sequel::Model
      datase_module do
        def my_filter(condition)
          return clone if condition.nil?
          where(condition: condition) 
        end
      end
    end
    

    Working Example: https://replit.com/@engineersmnky/SequelGemRailsScope#main.rb

    Additional Info:

    I chose clone because most query methods utilize this method (similar to spawn in ActiveRecord).

    Dataset#clone looks like this

    def clone(opts = nil || (return self))
      c = super(:freeze=>false)
      c.opts.merge!(opts)
      unless opts.each_key{|o| break if COLUMN_CHANGE_OPTS.include?(o)}
        c.clear_columns_cache
      end
      c.freeze
    end
    

    clone's method signature shortcuts execution if no condition is passed and simply returns self.

    As stated in the comments

    return self used above because clone is called by almost all other query methods, and it is the fastest approach

    However the comments do also state

    This method should generally not be called directly by user code.

    So you could choose to just use return self instead.*

    *I implemented both options in my Example above to show their identical functionality plus that method signature is pretty interesting