Rails sort scope by custom method - ruby-on-rails

I've got a scope which does the fetching
scope :between, -> (start, endd) {
where(:start_time => start..endd}
}
and a custom method
def time_of_day
Helper.time_of_day(start_time || est_start_time )
end
I want to be able to group the results by a custom method
Class.between
Class.between.time_of_start
So far, I've tried
def self.custom_sort
self.group_by { |a| a.time_of_start }
end
But I get
NoMethodError: undefined method `by_time' for Instance::ActiveRecord_Relation:0x00000005537e60>
I know I can do things like
Class.between.group_by { |a| a.time_of_start}
But I want to define a custom 'scope'

Try adding .all like:
self.all.group_by { |a| a.time_of_start }
Reference

Try this:
scope :between, -> {
where(...)
}
scope :grouped, -> {
group_by { |a| a.custom_method }
}
private
def custom_method
#some manipulation
end
The you can call:
Class.between.grouped

Related

ActiveAdmin issue with Scoped Resource

I have the following Scope on a resource page:
scope("Current Active Event Registrations") { |scope| Event.current_active_event_registrations }
The error I keep getting when viewing the page is:
undefined method `except' for nil:NilClass
c = c.except :select, :order
The code in Event looks like:
class Event < ActiveRecord::Base
has_many :registrations
scope :active_event, -> { where(active: true) }
scope :not_expired_active, -> { active_event.where('end_at > ?', DateTime.now) }
after_save :check_for_other_active_events
def random_winner
self.registrations.order("RANDOM()").first
end
def self.current_active_event_registrations
events = self.not_expired_active
events.first.registrations unless events.blank?
all if events.blank?
end
private
def check_for_other_active_events
Event.where('id != ?', self.id).update_all(active: false) if self.active
end
end
I am just wanting to add in a custom scope to my Registration Resource page in my ActiveAdmin backend.
I'm using Rails 4 and the latest ActiveAdmin
def self.current_active_event_registrations
events = self.not_expired_active
if events.blank?
all
else
events.first.registrations
end
end

Refactor class method to rails 4 scope

I have the following method that I'd like to move to named scope
def self.running_promotion
Supplier.all.select { |s| s.has_running_promotion? == true }
end
Not sure how to use lambda with rails 4 scope or if this is possible. I tried
scope :on_sale, -> { where({ |s| s.has_running_promotion? == true }) }
if has_running_promotion is field in your table you could write:
scope :on_sale, -> { where(has_running_promotion: true) }

Rails 4 scope with argument

Upgrading Rails 3.2. to Rails 4. I have the following scope:
# Rails 3.2
scope :by_post_status, lambda { |post_status| where("post_status = ?", post_status) }
scope :published, by_post_status("public")
scope :draft, by_post_status("draft")
# Rails 4.1.0
scope :by_post_status, -> (post_status) { where('post_status = ?', post_status) }
But I couldn't find out how to do the 2nd and 3rd lines. How can I create another scope from the first scope?
Very simple, just same lambda without arguments:
scope :by_post_status, -> (post_status) { where('post_status = ?', post_status) }
scope :published, -> { by_post_status("public") }
scope :draft, -> { by_post_status("draft") }
or more shorted:
%i[published draft].each do |type|
scope type, -> { by_post_status(type.to_s) }
end
From the Rails edge docs
"Rails 4.0 requires that scopes use a callable object such as a Proc or lambda:"
scope :active, where(active: true)
# becomes
scope :active, -> { where active: true }
With this in mind, you can easily rewrite you code as such:
scope :by_post_status, lambda { |post_status| where('post_status = ?', post_status) }
scope :published, lambda { by_post_status("public") }
scope :draft, lambda { by_post_status("draft") }
In the event that you have many different statuses that you wish to support and find this to be cumbersome, the following may suit you:
post_statuses = %I[public draft private published ...]
scope :by_post_status, -> (post_status) { where('post_status = ?', post_status) }
post_statuses.each {|s| scope s, -> {by_post_status(s.to_s)} }

rspec: undefined method 'latest'

I am testing a single method in the model. It's called last_photo, I filled in the data of the database and try to return the first element, but I have an error udefined method 'latest'. What could it be? How can I fix it?
Method latest this:
scope :latest, -> { order('created_at DESC') }
def last_photo
#last_photo ||= user_updates.latest.where("photo_front IS NOT NULL and photo_front != ''").first.try(:photo_front)
end
context "instance method" do
let(:user) { create :user }
context "last photo" do
before { create_list(:user_update, 3, user: user) }
let(:user_updates){ UserUpdate.all }
describe "#last_photo" do
subject { user.last_photo }
it { should eq user_updates.latest.first.photo_front }
end
describe "#last_photo_side" do
subject { user.last_photo_side }
it { should eq user_updates.latest.first.photo_side}
end
end
end
Thanks.
I bet UserUpdate.all returns an array. So you cant chain scopes on it.
Replace with:
let(:user_updates){ UserUpdate.scoped }

How do I stub a method of an instance only if a specific instance variable has a value?

I have an object MyObject:
class MyObject
def initialize(options = {})
#stat_to_load = options[:stat_to_load] || 'test'
end
def results
[]
end
end
I want to stub the results method only if stat_to_load = "times". How can I do that? I tried:
MyObject.any_instance.stubs(:initialize).with({
:stat_to_load => "times"
}).stubs(:results).returns(["klala"])
but it does not work. Any idea?
So, I think there is probably a simpler way to test what you're trying to test, but without more context I don't know what to recommend. However, here is some proof-of-concept code to show that what you want to do can be done:
describe "test" do
class TestClass
attr_accessor :opts
def initialize(opts={})
#opts = opts
end
def bar
[]
end
end
let!(:stubbed) do
TestClass.new(args).tap{|obj| obj.stub(:bar).and_return("bar")}
end
let!(:unstubbed) { TestClass.new(args) }
before :each do
TestClass.stub(:new) do |args|
case args
when { :foo => "foo" }
stubbed
else
unstubbed
end
end
end
subject { TestClass.new(args) }
context "special arguments" do
let(:args) { { :foo => "foo" } }
its(:bar) { should eq "bar" }
its(:opts) { should eq({ :foo => "foo" }) }
end
context "no special arguments" do
let(:args) { { :baz => "baz" } }
its(:bar) { should eq [] }
its(:opts) { should eq({ :baz => "baz" }) }
end
end
test
special arguments
bar
should == bar
opts
should == {:foo=>"foo"}
no special arguments
bar
should == []
opts
should == {:baz=>"baz"}
Finished in 0.01117 seconds
4 examples, 0 failures
However I'm making a lot of use of special subject/let context blocks here. See http://benscheirman.com/2011/05/dry-up-your-rspec-files-with-subject-let-blocks/ for more on that subject.
Try out below, this should work as expected:
Here, Basically we are actually stubbing new instance getting created and also stubbing results method of the instance which is getting returned.
options = {:stat_to_load => "times"}
MyObject.stubs(:new).with(options)
.returns(MyObject.new(options).stubs(:results).return(["klala"]))
You could use plain old Ruby inside your test to achieve this.
MyObject.class_eval do
alias_method :original_results, :results
define_method(:results?) do
if stats_to_load == "times"
["klala"]
else
original_results
end
end
end

Resources