I want to search the tracks either by "all of" the filters, or by "any of" the filters. So here is what I got:
tracks_controller.rb
def search
if params[:scope] == "any_of"
Track.search do
any_of do
with(:name, "Thriller")
with(:artist, "Michael Jackson")
end
with(:user_id, current_user.id)
end
elsif params[:scope] == "all_of"
Track.search do
all_of do
with(:name, "Thriller")
with(:artist, "Michael Jackson")
end
with(:user_id, current_user.id)
end
end
It works as expected. But how to refactor the code to make it DRY?
Here it is Sir:
def search
Track.search do
mode = (params[:scope] == 'any_of' ? method(:any_of) : method(:all_of)) # don't use method(params[:scope]) for obvious security reason)
mode.call do
with(:name, "Thriller")
with(:artist, "Michael Jackson")
end
with(:user_id, current_user.id)
end
end
Related
Want to achieve the following code using metaprogramming.
#resource = {}
#voters = {}
#is_upvoted = {}
def resource(comment)
#resource[comment.id]
end
def voters(comment)
#voters[comment.id]
end
def is_upvoted(comment)
#is_upvoted[comment.id]
end
How can I create these methods using ruby metaprogramming and access the hash?
Can you tell me what is wrong in my code ?
['resource', 'voters', 'is_upvoted'].each do |attribute|
define_method("#{attribute}") do |comment|
instance_variable_set("##{attribute}", comment.id)
end
end
This bit seems redundant:
#resource = {}
#voters = {}
#is_upvoted = {}
Since you're already looping an array to do your metaprogramming.
You might try something like:
class Foo
%w(
resource
voters
is_upvoted
).each do |attr_sym|
define_method attr_sym do |comment|
instance_variable_set("##{attr_sym}", {}) unless instance_variable_get("##{attr_sym}")
instance_variable_get("##{attr_sym}")[comment.id]
end
end
end
Which I believe will give you methods roughly like:
class Foo
def resource(comment)
#resource ||= {}
#resource[comment.id]
end
end
Personally, it seems not great to me to have comment.id in your method. Because what if someday you want to use a different attribute (or something else altogether) as the key?
So, I think I would do:
class Foo
%w(
resource
voters
is_upvoted
).each do |attr_sym|
define_method attr_sym do |key|
instance_variable_set("##{attr_sym}", {}) unless instance_variable_get("##{attr_sym}")
instance_variable_get("##{attr_sym}")[key]
end
end
end
Now, it seems like you're going to want an easy way to set key-value pairs on your instance variable, so I guess I would try something like:
class Foo
%w(
resource
voters
is_upvoted
).each do |attr_sym|
define_method attr_sym do |key=nil|
instance_variable_set("##{attr_sym}", {}) unless instance_variable_get("##{attr_sym}")
hsh = instance_variable_get("##{attr_sym}")
return hsh[key] if key
hsh
end
end
end
In which case you should be able to do (assuming you have a #comment variable that responds to id):
#comment.id
=> 1
foo = Foo.new
=> #<Foo:0x000056536d7504b0>
foo.resource
=> {}
foo.resource[#comment.id] = :bar
=> :bar
foo.resource
=> {1=>:bar}
foo.resource[#comment.id]
=> :bar
Can you tell me what is wrong in my code ?
It's doing the equivalent of this:
def resource(comment)
#resource = comment.id
end
instance_variable_get would be a better choice.
This is how I used it and it works
['resource', 'voters', 'is_upvoted'].each do |attribute|
define_method("#{attribute}") do |comment|
instance_variable_get("##{attribute}")[comment.id]
end
end
I want to merge two profiles into one. What is the best way to do this in Rails.
I have two profiles say user1 and user2 and there are at least 30 tables associated with them.
Now i want to merge them together so that there should be one profile say user1 and user2 should get deleted but all the associated data of user2 now should associate with user1.
For example: suppose user2 has two contacts and user1 has 3 contacts after merging user user1 should have 5 contacts.
Something like this
#user1 = User.find(1);
#user2 = User.find(2);
Contact.where("user_id = ?", #user2.id).update_all(:user_id => #user1.id)
#user2.destroy
in case of generalize solution
place file /lib/acts_as_user_merge.rb
module UserMerge
module ActsAsUserMerge
module Base
def self.included(klass)
klass.class_eval do
extend Config
end
end
end
module Config
def acts_as_user_merge
include ::UserMerge::ActsAsUserMerge::InstanceMethods
end
end
module InstanceMethods
def merge(user)
models = Array.new
models_names = User.reflections.collect{|a, b| b.class_name if b.macro==:has_many}.compact
models_names.each do |name|
models << Object.const_get name
end
models.each do |model|
model.where("user_id = ?", user.id).update_all(:user_id => self.id)
end
user.destroy
end
end
end
end
::ActiveRecord::Base.send :include, ::UserMerge::ActsAsUserMerge::Base
how to use
User < ActiveRecord::Base
has_many ...
acts_as_user_merge
end
#user1.merge(#user2)
kinda messy and not tested but should give you an idea
Something like that
def merge_users(dead, user)
User.reflections.each do |assoc, reflection|
foreign_key = reflection.foreign_key
case reflection.macro
when :has_many, :has_one then
unless reflection.options[:through]
reflection.klass.where(foreign_key => dead.id).update_all(foreign_key => user.id) # if id is a primary key
end
if options[:as] # polymorphic
if reflection.macro == :has_many
dead.send("#{options[:as].pluralize}")).each { |r| user.send("#{options[:as].pluralize}<<", r) }
else
user.send("#{options[:as]}=", dead.send("#{options[:as]}"))
user.save
end
end
when :belongs_to then
if options[:polymorphic]
user.send("#{assoc}=", deaf.send(assoc))
user.save
else
user.update_attribute(foreign_key, dead.send(foreign_key))
end
when :has_and_belongs_to_many then
dead.send("#{assoc}")).each { |r| user.send("#{assoc}<<", r) }
end
end
end
merge_users(dead_user, user)
dead_user.destroy
This article discusses this matter in depth, and provides the working code for it: http://ewout.name/2010/04/generic-deep-merge-for-activerecord/
let(:product) { FactoryGirl.create(:product) }
it "should blah" do
product.name = "a"
product.save!
post :update, id: product.id, product: { name: "x" }
# assuming :update changes the product's name to params[:name]
product.reload.name.should == "x"
end
The should always fails unless I do something like
Product.find(product.id).name.should == "x"
Am I misusing let?
If I work with #product created within before :each and #product.reload it's fine.
If you break during execution of a spec and call self.class.method(:let).source, you get something like:
def let(name, &block)
define_method(name) do
__memoized.fetch(name) {|k| __memoized[k] = instance_eval(&block) }
end
end
Upon further inspection, __memoized is just a simple Hash. I don't think there's anything fancy going on here. What precise versions of rails and rspec were you using?
require 'rubygems'
require 'action_view'
require 'active_support'
class MenuItem
include ActionView::Helpers::TagHelper,
ActionView::Helpers::UrlHelper
attr_accessor :children, :link
cattr_accessor :request
def initialize(title, link, level, link_opts={})
#title, #link, #level, #link_opts = title, link, level, link_opts
#children = []
end
def add(title, link, link_opts={}, &block)
returning(MenuItem.new(title, link, #level +1, link_opts)) do |adding|
#children << adding
yield adding if block_given?
end
end
def to_s
content_tag(:li, content_tag(:div, link_to(#title, #link, #link_opts), :class => "menu_header_level_"+#level.to_s) + child_output, ({:class => 'active'} if active?)).html_safe
end
def level_class
"menu_level_#{#level}"
end
def child_output
children.empty? ? '' : content_tag(:ul, #children.collect(&:to_s).join.html_safe, :class => level_class)
end
def active?
children.any?(&:active?) || on_current_page?
end
def on_current_page?
# set it for current_page? defined in UrlHelper
# current_page?(#link)
false
end
# def request
# ##request
# end
end
class SemanticMenu < MenuItem
def initialize(rq, opts={},&block)
##request = rq
#opts = {:class => 'menu'}.merge opts
#level = 0
#children = []
yield self if block_given?
end
def to_s
content_tag(:ul, #children.collect(&:to_s).join.html_safe, #opts).html_safe
end
end
Hello. I am trying to change the behaviour of the Semantic-Menu root. When I click one of the roots, the menu drops down and displays all the children. What I would like is happen is when I click, it goes to a default page and then display the children. Semantic-menu seems to allow links only to lower levels and not the main ones. Roots links only work when they don't have children.
The code below is the one that is in the plug-in in Ruby. and I think is the one that needs to be modified. There the html code but I don't think it has to do with it.
Can you please tell me what need to be added in other to make to father trigger their links?
Thank you.
I don't know the direct answer to your question, but SemanticMenu seems outdated.
Check out the SimpleNavigation gem: https://github.com/andi/simple-navigation/wiki
In one particular Railcasts episode Ryan talks about advanced search and in that he uses some code so as to find the conditions for the search. As its working isn't explained I wanted some clarification regarding it.
def products
#products ||= find_products
end
private
def find_products
Product.find(:all, :conditions => conditions)
end
def keyword_conditions
["products.name LIKE ?", "%#{keywords}%"] unless keywords.blank?
end
def minimum_price_conditions
["products.price >= ?", minimum_price] unless minimum_price.blank?
end
def maximum_price_conditions
["products.price <= ?", maximum_price] unless maximum_price.blank?
end
def category_conditions
["products.category_id = ?", category_id] unless category_id.blank?
end
def conditions
[conditions_clauses.join(' AND '), *conditions_options]
end
def conditions_clauses
conditions_parts.map { |condition| condition.first }
end
def conditions_options
conditions_parts.map { |condition| condition[1..-1] }.flatten
end
def conditions_parts
private_methods(false).grep(/_conditions$/).map { |m| send(m) }.compact
end
I would welcome any information as to how this works especially the method products as he even calls it as products.name etc.
He defines some methods for the conditions in his search form: keyword_conditions, minimum_price_conditions ans so on. products.name means the field name from the table products.
The method
def conditions_parts
private_methods(false).grep(/_conditions$/).map { |m| send(m) }.compact
end
uses reflection to look at the private methods of this class which have the name that ends with _conditions (The regex /_conditions$/) and joins only those that don't return null (compact)
The method
def conditions
[conditions_clauses.join(' AND '), *conditions_options]
end
adds a AND keyword between the conditions and passes the result to Product.find which makes the SQL query and returns the result set.