multiple scope rails - ruby-on-rails

class Anketum < ActiveRecord::Base
has_one :user
class << self
def search(params)
self.scope :h, :conditions => {:height => params[:height]}
#scope :w, :conditions => {:width => params[:width]}
self.h if params[:height]
end
end
end
I need to create multiple scope depend on params[:xxx] present

Judging by your code sample, you're way over-engineering this:
# app/models/anketum.rb
class Anketum < ActiveRecord::Base
end
# app/controller/some_controller.rb
def search
#results = Anketum.scoped
[:width, :height, :any, :other, :searchable, :attribute].each do |key|
#results.where(key => params[key]) if params[key].present?
end
end
Your models should never access the params hash, by the way.

Related

Joint query across 2 models (has_many)

Hi I need help and all insight appreciated. I have two models Auctions and Bids and I want to retrieve the All auctions current_user won, the ones s/he has been outbid on and the ones s/he's winning
Here are the two models:
class Auction < ActiveRecord::Base
extend FriendlyId
friendly_id :guid, use: :slugged
before_save :populate_guid
mount_uploaders :images, ImageUploader
belongs_to :client
has_many :bids, dependent: :destroy
has_one :order, dependent: :destroy
validates_presence_of :title, :lien_price,
:end_time, :collateral_value,
:redemption_date, :current_interest_rate,
:additional_tax, :collateral_details,
:location, :client_id, :starting_bid
validate :end_time_in_the_future, :on => :update
validates_uniqueness_of :guid, case_sensitive: false
def end_time_in_the_future
errors.add(:end_time, "can't be in the past") if self.end_time && self.end_time < Time.now
end
def self.get_active_auctions
where("end_time > ?", Time.now)
end
def self.closed_auctions
where("end_time < ?", Time.now)
end
def highest_bid
self.bids.maximum("amount")
end
def highest_bid_object
self.bids.order(:amount => :desc).limit(1).first
end
def highest_bidder
self.highest_bid_object.user if highest_bid_object
end
def closed?
self.end_time < Time.now
end
private
def populate_guid
if new_record?
while !valid? || self.guid.nil?
self.guid = SecureRandom.random_number(1_000_000_000).to_s(36)
end
end
end
end
and
class Bid < ActiveRecord::Base
extend FriendlyId
friendly_id :guid, use: :slugged
belongs_to :auction
belongs_to :user
before_save :populate_guid
validates_presence_of :amount, :user_id,
:auction_id
#validate :higher_than_current?
validates :amount, :numericality => true
validates_uniqueness_of :guid, case_sensitive: false
def higher_than_current?
if !Bid.where("amount > ? AND auction_id = ?", amount, self.auction.id).empty?
errors.add(:amount, "is too low! It can't be lower than the current bid, sorry.")
end
end
private
def populate_guid
if new_record?
while !valid? || self.guid.nil?
self.guid = SecureRandom.random_number(1_000_000_000).to_s(36)
end
end
end
end
I thought
#auctions = Auction.closed_auctions.where(highest_bidder: current_user)
or
#auctions = Auction.closed_auctions.joins(:bids).where(highest_bidder: current_user)
would work but they both raise an error.
Edit this works
#auctions = Auction.closed_auctions.references(highest_bidder: current_user)
But there's probably a better way.
You probably can't access current_user from controller (devise?). So you need to pass the user as a parameter to the class or instance method. What you should look into are scopes and especially scopes that accept parameters. Scopes could really help you refactor your Auction model (you really don't need any methods that only return a where()), but also solve the inaccessible current_user.
Use it like this in your Auction model:
scope: :highest_bidder -> (current_user) { where(highest_bidder: current_user) }
And call it like this from your controller:
#auctions = Auction.closed_auctions.highest_bidder(current_user)

Rails: Better way to create variables for views

I am totally new to Ruby, and Rails. Currently, I am using helper methods. How can I write the same code as this in my Model 'User' so as to access all these variables from controller and view?
Writting code this way in helper is 100% functional:
module HomeHelper
def init(user_id)
#friends = Array.new
#followers = Array.new
#user = User.find_by_id(user_id) #Get User
#friends = #user.users #Get all his friends
#
#statuses = Array.new #
#friends.each do |friend| #
#statuses += friend.statuses #Get all statuses for 'a' friend, then loop
end #
#statuses += #user.statuses #
#statuses = #statuses.sort_by {|status| status.created_at}.reverse!
#friendsof = Array.new
#filtered_friendsof = Array.new
#friends.each do |friend|
#friendsof += friend.users
end
#friendsof.each do |friendof|
unless (#friends.include?(friendof))
if #user != friendof
#filtered_friendsof << friendof
end
end
end
end
#filtered_friendsof = #filtered_friendsof.uniq
end
Controller
class HomeController < ApplicationController
def index
#user_id=3
end
end
Model:
class User < ActiveRecord::Base
has_many :statuses
has_and_belongs_to_many(:users,
:join_table => "user_connections",
:foreign_key => "user1_id",
:association_foreign_key => "user2_id")
#has_many :user_connections
end
Home controller:
class HomeController < ApplicationController
def index
#user = User.find(3)
end
end
User model:
class User < ActiveRecord::Base
has_many :statuses
has_and_belongs_to_many :friends,
:class_name => 'User'
:join_table => "user_connections",
:foreign_key => "user1_id",
:association_foreign_key => "user2_id"
def combined_statuses
(friends.map(&:statuses) + statuses).flatten.
sort_by {|status| status.created_at}.reverse!
end
end
Now, you don't need your helper method and in your view you can use:
#user.friends # instead of #friends
#user.combined_statuses # instead of #statuses
I'll let you figure out the rest, but I hope you get the general idea of pushing the logic into the model.
Most of that logic belongs in the User model. Nothing else needs to be actually doing those computations, and the User model has access to all the relevant pieces. There are additionally several other improvements that can be made. I'll try to add comments below to indicate these improvements.
Model
class User < ActiveRecord::Base
has_many :statuses
has_and_belongs_to_many :friends, # now you can just say user.friends
:class_name => 'User', # makes more sense semantically
:join_table => "user_connections",
:foreign_key => "user1_id",
:association_foreign_key => "user2_id"
def friends_statuses
(friends.map(&:statuses).flatten + statuses).sort_by!(&:created_at).reverse
# Ruby has many great methods for Arrays you should use.
# You can often avoid instantiating variables like the empty Arrays you have.
end
def second_order_friends
(friends.map(&:friends).flatten.uniq - friends) - [self]
end
end
Controller
class HomeController < ApplicationController
def index
user = User.find(7) # how do you decide which user you're displaying things for?
# this might be better off in 'show' rather than 'index'
# here you can call all the methods you have for 'User', such as:
# user.friends, user.statuses, user.friends_statuses, user.second_order_friends
# to make things accessible in the view, you just need an #variable, e.g.:
#friends = user.friends
#latest_statuses = user.friends_statuses.first(10)
end

"undefined method `metadata' for.." rails

Hi i have this problem running the spec file.
this is the reparator model:
class Reparator < User
include Mongoid::Document
include Mongoid::Timestamps
field :private_reparator, :type => Boolean, :default => true
field :brand_name, :type => String
field :year_of_experience, :type => Integer, :default => 1
has_many :reparations
has_many :skills
validates_presence_of :skills, :year_of_experience
validates :year_of_experience, :numericality => {:greater_than_or_equal_to => 0}
end
This is the skill model:
class Skill
include Mongoid::Document
field :name, :type => String
belongs_to :reparator
validates_presence_of :name
validates_uniqueness_of :name
end
This is the controller:
class ReparatorsController < ApplicationController
respond_to :json
def index
#reparators = Reparator.all
respond_with #reparators
end
def show
#reparator = Reparator.find(params[:id])
respond_with #reparator
end
def create
#reparator = Reparator.new(params[:reparator])
#reparator.skills = params[:skills]
if #reparator.save
respond_with #reparator
else
respond_with #reparator.errors
end
end
def update
#reparator = Reparator.find(params[:id])
if #reparator.update_attributes(params[:reparator])
respond_with #reparator
else
respond_with #reparator.errors
end
end
def destroy
#reparator = Reparator.find(params[:id])
#reparator.destroy
respond_with "Correctly destroyed"
end
end
And this is the spec file for this controller (i'll just put the test that does't pass):
it "Should create an reparator" do
valid_skills = [FactoryGirl.create(:skill).id, FactoryGirl.create(:skill).id]
valid_attributes = {:name => "Vianello",
:email => "maremma#gmail.com",
:address => "viale ciccio",
:private_reparator => true
}
post :create, :reparator => valid_attributes, :skills => valid_skills
assigns(:reparator).should be_a Reparator
assigns(:reparator).should be_persisted
end
And this is the skill Factory girl:
FactoryGirl.define do
factory :skill do
sequence(:name) {|n| "skill#{n}"}
end
end
I think there is a typo in your spec. post :create, :reparator => valid_attributes, :skills => skills_ttributes should be post :create, :reparator => valid_attributes, :skills => skills_attributes instead.
The bad line is this one
#reparator.skills = params[:skills]
params[:skills] is an array of strings (the ids that have been passed) but the skills= method expects to be given actual instances of Skill and so blows up.
As well as skills=, mongoid also gives you a skill_ids= method which allows you to change which objects are associated by just assigning an array of ids. Alternatively, load the skills object your self and then to #reparator.skills = skills

Activity history with Rails audited gem

I have rails 3 application with some models, like Product and User. I'm using "audited" gem to track changes for products, it's simple and nice working.
But I want to make special page where I want to put daily activity history. I need something like Audits.all.order("created_at") for first step, but there is no such model.
Question: How can I get all audits for today for all models?
I think you should query like Audited::Adapters::ActiveRecord::Audit.where("created_at >= ?", Date.today) according to the gem structure
To be able to access today's audits with:
#audits = Audit.today
Create an audit.rb file in app/models/ like:
Audit = Audited.audit_class
class Audit
scope :today, -> do
where("created_at >= ?", Time.zone.today.midnight).reorder(:created_at)
end
end
Audited also provides a few named scopes of its own that may prove useful:
scope :descending, ->{ reorder("version DESC") }
scope :creates, ->{ where({:action => 'create'}) }
scope :updates, ->{ where({:action => 'update'}) }
scope :destroys, ->{ where({:action => 'destroy'}) }
scope :up_until, ->(date_or_time){ where("created_at <= ?", date_or_time) }
scope :from_version, ->(version){ where(['version >= ?', version]) }
scope :to_version, ->(version){ where(['version <= ?', version]) }
scope :auditable_finder, ->(auditable_id, auditable_type){ where(auditable_id: auditable_id, auditable_type: auditable_type) }
my solution is simply to extend the audit object, e.g.
cat lib/audit_extensions.rb
# The audit class is part of audited plugin
# we reopen here to add search functionality
require 'audited'
module AuditExtentions
def self.included(base)
base.send :include, InstanceMethods
base.class_eval do
belongs_to :search_users, :class_name => 'User', :foreign_key => :user_id
scoped_search :on => :username, :complete_value => true
scoped_search :on => :audited_changes, :rename => 'changes'
scoped_search :on => :created_at, :complete_value => true, :rename => :time, :default_order => :desc
scoped_search :on => :action, :complete_value => { :create => 'create', :update => 'update', :delete => 'destroy' }
before_save :ensure_username
end
end
module InstanceMethods
private
def ensure_username
self.username ||= User.current.to_s rescue ""
end
end
end
Audit = Audited.audit_class
Audit.send(:include, AuditExtentions)

Where should I put the code?

I have the models User and StoredItem:
class UserData < ActiveRecord::Base
has_many :stored_items, :dependent => :destroy
end
class StoredItem < ActiveRecord::Base
belongs_to :user
named_scope :lookup, lambda { |id| { :conditions => ['qid = ?', id]}}
end
I need to have two methods to add and remove the items to StoredItem for current user. I put this code to User model:
class UserData < ActiveRecord::Base
has_many :stored_items, :dependent => :destroy
def save_item(params)
if(!self.stored_items.lookup(params[:qid]).exists?)
item = self.stored_items.new(:sid => params[:qid],
:name => params[:qti],
:url => params[:qur],
:group_id => params[:title],
:rating => Integer(params[:rating]))
item.save
end
end
def remove_item(qid)
item = self.stored_items.lookup(qid).first()
item.destroy
end
end
So here is the StoredItem controller:
def save_item
#user = UserData.find_by_login(session[:cuser])
#user.save_item(params)
# ...
end
Is it good architectural decision or it will be better to put this code to StoredItem model and pass the current user into it?
This is a good architectural decision. You need to keep it in the user since the User is the owner of the StoredItem. The user is responsible for its stored items, not the other way around.

Resources