Skip to content

Instantly share code, notes, and snippets.

@GeekOnCoffee
Created July 5, 2012 13:18
Show Gist options
  • Save GeekOnCoffee/3053598 to your computer and use it in GitHub Desktop.
Save GeekOnCoffee/3053598 to your computer and use it in GitHub Desktop.
From 03c3f527b01951900aa88e2bd3bfd23fc1400f86 Mon Sep 17 00:00:00 2001
From: Ryan Bigg <[email protected]>
Date: Thu, 14 Jun 2012 16:58:53 +1000
Subject: [PATCH] Explicitly define scopes as being searchable This stops
people using class methods or scopes that shouldn't be used
for searching.
Props to joernchen from Phenoelit for informing us about this!
---
app/models/product.rb | 9 +++++++++
app/models/product_group.rb | 2 +-
lib/scopes/product.rb | 34 +++++++++++++++++-----------------
3 files changed, 27 insertions(+), 18 deletions(-)
diff --git a/app/models/product.rb b/app/models/product.rb
index b309008..541ede0 100644
--- a/app/models/product.rb
+++ b/app/models/product.rb
@@ -60,6 +60,15 @@ class Product < ActiveRecord::Base
alias :options :product_option_types
+ cattr_accessor :search_scopes do
+ []
+ end
+
+ def self.add_search_scope(name, &block)
+ self.named_scope name.intern, &block
+ search_scopes << name.intern
+ end
+
include ::Scopes::Product
# default product scope only lists available and non-deleted products
diff --git a/app/models/product_group.rb b/app/models/product_group.rb
index 8e89853..2238f5f 100644
--- a/app/models/product_group.rb
+++ b/app/models/product_group.rb
@@ -90,7 +90,7 @@ class ProductGroup < ActiveRecord::Base
end
def add_scope(scope_name, arguments=[])
- if scope_name.to_s !~ /eval|send|system|[^a-z0-9_!?]/
+ if Product.search_scopes.include?(scope_name.intern)
self.product_scopes << ProductScope.new({
:name => scope_name.to_s,
:arguments => [*arguments]
diff --git a/lib/scopes/product.rb b/lib/scopes/product.rb
index 766d2a9..e7ae9cd 100644
--- a/lib/scopes/product.rb
+++ b/lib/scopes/product.rb
@@ -40,23 +40,23 @@ module Scopes::Product
]
# default product scope only lists available and non-deleted products
- ::Product.named_scope :active, lambda { |*args|
+ ::Product.add_search_scope :active, lambda { |*args|
Product.not_deleted.available(args.first).scope(:find)
}
- ::Product.named_scope :not_deleted, {
+ ::Product.add_search_scope :not_deleted, {
:conditions => "products.deleted_at is null"
}
- ::Product.named_scope :available, lambda { |*args|
+ ::Product.add_search_scope :available, lambda { |*args|
{ :conditions => ["products.available_on <= ?", args.first || Time.zone.now] }
}
- ::Product.named_scope :keywords, lambda{|query|
+ ::Product.add_search_scope :keywords, lambda{|query|
return {} if query.blank?
Spree::Config.searcher.get_products_conditions_for(query)
}
- ::Product.named_scope :price_between, lambda {|low,high|
+ ::Product.add_search_scope :price_between, lambda {|low,high|
{ :joins => :master, :conditions => ["variants.price BETWEEN ? AND ?", low, high] }
}
@@ -65,7 +65,7 @@ module Scopes::Product
#
# Product.taxons_id_eq(x)
#
- Product.named_scope :in_taxon, lambda {|taxon|
+ Product.add_search_scope :in_taxon, lambda {|taxon|
Product.in_taxons(taxon).scope :find
}
@@ -74,13 +74,13 @@ module Scopes::Product
#
# Product.taxons_id_eq([x,y])
#
- Product.named_scope :in_taxons, lambda {|*taxons|
+ Product.add_search_scope :in_taxons, lambda {|*taxons|
taxons = get_taxons(taxons)
taxons.first ? prepare_taxon_conditions(taxons) : {}
}
# for quick access to products in a group, WITHOUT using the association mechanism
- Product.named_scope :in_cached_group, lambda {|product_group|
+ Product.add_search_scope :in_cached_group, lambda {|product_group|
{ :joins => "JOIN product_groups_products ON products.id = product_groups_products.product_id",
:conditions => ["product_groups_products.product_group_id = ?", product_group]
}
@@ -88,7 +88,7 @@ module Scopes::Product
# a scope that finds all products having property specified by name, object or id
- Product.named_scope :with_property, lambda {|property|
+ Product.add_search_scope :with_property, lambda {|property|
conditions = case property
when String then ["properties.name = ?", property]
when Property then ["properties.id = ?", property.id]
@@ -102,7 +102,7 @@ module Scopes::Product
}
# a scope that finds all products having an option_type specified by name, object or id
- Product.named_scope :with_option, lambda {|option|
+ Product.add_search_scope :with_option, lambda {|option|
conditions = case option
when String then ["option_types.name = ?", option]
when OptionType then ["option_types.id = ?", option.id]
@@ -117,7 +117,7 @@ module Scopes::Product
# a simple test for product with a certain property-value pairing
# note that it can test for properties with NULL values, but not for absent values
- Product.named_scope :with_property_value, lambda { |property, value|
+ Product.add_search_scope :with_property_value, lambda { |property, value|
conditions = case property
when String then ["properties.name = ?", property]
when Property then ["properties.id = ?", property.id]
@@ -132,7 +132,7 @@ module Scopes::Product
}
# a scope that finds all products having an option value specified by name, object or id
- Product.named_scope :with_option_value, lambda {|option, value|
+ Product.add_search_scope :with_option_value, lambda {|option, value|
option_type_id = case option
when String
option_type = OptionType.find_by_name(option) || option.to_i
@@ -153,7 +153,7 @@ module Scopes::Product
}
# finds product having option value OR product_property
- Product.named_scope :with, lambda{|value|
+ Product.add_search_scope :with, lambda{|value|
{
:conditions => ["option_values.name = ? OR product_properties.value = ?", value, value],
:joins => {:variants => :option_values, :product_properties => []}
@@ -178,7 +178,7 @@ module Scopes::Product
# there is alternative faster and more elegant solution, it has small drawback though,
# it doesn stack with other scopes :/
#
- Product.named_scope :descend_by_popularity, lambda{
+ Product.add_search_scope :descend_by_popularity, lambda{
# :joins => "LEFT OUTER JOIN (SELECT line_items.variant_id as vid, COUNT(*) as cnt FROM line_items GROUP BY line_items.variant_id) AS popularity_count ON variants.id = vid",
# :order => 'COALESCE(cnt, 0) DESC'
{
@@ -201,7 +201,7 @@ SQL
}
# Produce an array of keywords for use in scopes. Always return array with at least an empty string to avoid SQL errors
- def self.prepare_words(words)
+ def Product.prepare_words(words)
a = words.split(/[,\s]/).map(&:strip)
a.any? ? a : ['']
end
@@ -212,7 +212,7 @@ SQL
end
end
- def self.get_taxons(*ids_or_records_or_names)
+ def Product.get_taxons(*ids_or_records_or_names)
ids_or_records_or_names.flatten.map { |t|
case t
when Integer then Taxon.find_by_id(t)
@@ -227,7 +227,7 @@ SQL
end
# specifically avoid having an order for taxon search (conflicts with main order)
- def self.prepare_taxon_conditions(taxons)
+ def Product.prepare_taxon_conditions(taxons)
conditions = taxons.map{|taxon|
taxon.self_and_descendants.scope(:find)[:conditions]
}.inject([[]]){|result, scope|
--
1.7.8.3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment