Permalink
Browse files

Added tests, new features, and improved readme

  • Loading branch information...
1 parent dc46cf2 commit cf237603c7819ac6bc29da52c20c4b0f134c5033 @binarylogic committed Sep 2, 2008
View
@@ -1 +1,2 @@
.DS_Store
+*.log
View
@@ -0,0 +1 @@
+v0.9.0. First release
View
@@ -0,0 +1,28 @@
+CHANGELOG
+init.rb
+lib/searchgasm/active_record/associations.rb
+lib/searchgasm/active_record/base.rb
+lib/searchgasm/active_record/protection.rb
+lib/searchgasm/helpers.rb
+lib/searchgasm/search/base.rb
+lib/searchgasm/search/condition.rb
+lib/searchgasm/search/conditions.rb
+lib/searchgasm/search/utilities.rb
+lib/searchgasm/version.rb
+lib/searchgasm.rb
+Manifest
+MIT-LICENSE
+Rakefile
+README.mdown
+test/fixtures/accounts.yml
+test/fixtures/orders.yml
+test/fixtures/users.yml
+test/libs/acts_as_tree.rb
+test/libs/rexml_fix.rb
+test/test_active_record_associations.rb
+test/test_active_record_base.rb
+test/test_active_record_protection.rb
+test/test_helper.rb
+test/test_searchgasm_base.rb
+test/test_searchgasm_condition.rb
+test/test_searchgasm_conditions.rb
View
@@ -2,22 +2,35 @@
Searchgasm is orgasmic. Maybe not orgasmic, but you will get aroused. So go grab a towel and let's dive in.
-Searchgasm originated to satisfy a VERY simple need: so that I could use my form builder when making search forms. Sounds simple right? The goal was to use an object, that represents a search, just like an ActiveRecord object in form\_for and fields\_for. The design behind this plugin is pretty simple: the search object "santiizes" down into the options passed into ActiveRecord::Base.find(). It basically serves as a transparent filter between you and ActiveRecord::Base.find(). This filter provides "enhancements" that get translated into options that ActiveRecord::Base.find() can understand. Here's where you get aroused....
+Searchgasm originated to satisfy a VERY simple need: so that I could use my form builder when making search forms. Sounds simple right? The goal was to use an object, that represents a search, just like an ActiveRecord object in form\_for and fields\_for.
-## Install and use
+I'm a big fan of understanding what I'm using, so here's a quick explanation: The design behind this plugin is pretty simple. The search object "santiizes" down into the options passed into ActiveRecord::Base.find(). It basically serves as a transparent filter between you and ActiveRecord::Base.find(). This filter provides "enhancements" that get translated into options that ActiveRecord::Base.find() can understand. This doesn't step on the toes or dig into he internals of ActiveRecord. It uses what ActiveRecord provides publicly. Letting ActiveRecord do all of the hard work and keeping this plugin solid and less brittle.
+
+Here's where you get aroused...
-Since rails 2.1, gem support has been added, and it's great. Checkout how it works [http://railscasts.com/episodes/110-gem-dependencies](http://railscasts.com/episodes/110-gem-dependencies)
+## Install and use
sudo gem install searchgasm
+For rails > 2.1
+
# environment.rb
config.gem "searchgasm"
-## Inclusive Example
+For rails < 2.1
+
+ # environment.rb
+ require "searchgasm"
+
+Now go into your console and try out any of these example with your own models.
+
+**For all of the examples below, let's assume the following relationships: User => Orders => Line Items**
+
+## Super Simple Example
-The purpose of this example is to show you everything searchgasm has to offer.
+ User.all(:conditions => {:first_name_contains => "Ben", :email_ends_with => "binarylogic.com"}, :page => 3, :per_page => 20)
-For all of the examples let's assume the following relationships: User => Orders => Line Items
+## Detailed Example w/ object based searching
# new_search returns an object, you can call "find", "all", "first" also, see "a multitude of ways to search" below
search = User.new_search(
@@ -44,13 +57,23 @@ For all of the examples let's assume the following relationships: User => Orders
search.all
search.search # alias for all
search.first
+
+## Calculations
+
+Using the object from above:
+
search.average('id')
search.count
search.maximum('id')
search.minimum('id')
search.sum('id')
search.calculate(:sum, 'id') # any of the above calculations
+Or do it from your model:
+
+ User.count(:conditions => {:first_name_contains => "Ben"})
+ # ... all other calcualtions, etc.
+
## A multitude of ways to search, take your pick
Any of the options used in the above example can be used in these, but for the sake of brevity I am only using a few:
@@ -75,7 +98,7 @@ If you want to be hardcore:
search.per_page = 20
search.all
-## Search with conditions only
+## Search with conditions only (great for using in form\_for or fields\_for)
conditions = User.new_conditions(:age_gt => 18)
conditions.first_name_contains = "Ben"
@@ -101,6 +124,28 @@ Again, if you want to be hardcore:
search = @current_user.orders.build_search('total', :conditions => {:total_lte => 500})
+## Searching trees
+
+For tree data structures you get a few nifty methods. Let's assume Users is a tree data structure.
+
+ # Child of
+ User.all(:conditions => {:child_of => User.roots.first})
+ User.all(:conditions => {:child_of => User.roots.first.id})
+
+ # Sibling of
+ User.all(:conditions => {:sibling_of => User.roots.first})
+ User.all(:conditions => {:sibling_of => User.roots.first.id})
+
+ # Descendent of (includes all recursive children: children, grand children, great grand children, etc)
+ User.all(:conditions => {:descendent_of => User.roots.first})
+ User.all(:conditions => {:descendent_of => User.roots.first.id})
+
+ # Inclusive descendent_of. Same as above but includes the root
+ User.all(:conditions => {:inclusive_descendent_of => User.roots.first})
+ User.all(:conditions => {:inclusive_descendent_of => User.roots.first.id})
+
+
+
## Available anywhere (relationships & named scopes)
Not only can you use searchgasm when searching, but you can use it when setting up relationships or named scopes:
@@ -145,6 +190,9 @@ Depending on the type, each column comes preloaded with a bunch of nifty conditi
:integer, :float, :decimal,:datetime, :timestamp, :time, :date
=> :greater_than, :greater_than_or_equal_to, :less_than, :less_than_or_equal_to
+ tree data structures (see above "searching trees")
+ => :child_of, :sibling_of, :descendent_of, :inclusive_descendent_of
+
Some of these conditions come with aliases, so you have your choice how to call the conditions. For example you can use "greater\_than" or "gt":
:equals; => :is
View
@@ -0,0 +1,17 @@
+require 'rubygems'
+require 'echoe'
+
+require File.dirname(__FILE__) << "/lib/searchgasm/version"
+
+Echoe.new 'fiveruns_manage' do |p|
+ p.version = BinaryLogic::Searchgasm::Version::STRING
+ p.author = "Ben Johnson of Binary Logic"
+ p.email = 'bjohnson@binarylogic.com'
+ p.project = 'searchgasm'
+ p.summary = "Orgasmic ActiveRecord searching"
+ p.description = "Makes ActiveRecord searching easier, robust, and powerful. Automatic conditions, pagination support, object based searching, and more."
+ p.url = "http://github.com/binarylogic/searchgasm"
+ p.dependencies = %w(activerecord)
+ p.include_rakefile = true
+end
+
View
22 init.rb
@@ -1,21 +1 @@
-require "active_record"
-
-module ::ActiveRecord
- class Base
- class << self
- def valid_find_options
- VALID_FIND_OPTIONS
- end
- end
- end
-end
-
-require "searchgasm/search/utilities"
-require "searchgasm/search/condition"
-require "searchgasm/search/conditions"
-require "searchgasm/search/base"
-require "searchgasm/active_record/protection"
-require "searchgasm/active_record/base"
-require "searchgasm/active_record/associations"
-
-Searchgasm = BinaryLogic::Searchgasm
+require File.dirname(__FILE__) << "/lib/searchgasm"
View
@@ -0,0 +1,9 @@
+require "active_record"
+require "searchgasm/active_record/protection"
+require "searchgasm/active_record/base"
+require "searchgasm/active_record/associations"
+require "searchgasm/version"
+require "searchgasm/search/utilities"
+require "searchgasm/search/condition"
+require "searchgasm/search/conditions"
+require "searchgasm/search/base"
@@ -4,22 +4,23 @@ module ActiveRecord
module Associations
module AssociationCollection
def self.included(klass)
- klass.include Protection
+ klass.class_eval do
+ include Protection
+ end
end
def find_with_searchgasm(*args)
options = args.extract_options!
args << sanitize_options_with_searchgasm(options)
find_without_searchgasm(*args)
end
+
+ def build_conditions(options = {}, &block)
+ @reflection.klass.build_conditions(options.merge(:scope => scope(:find)[:conditions]), &block)
+ end
def build_search(options = {}, &block)
- @reflection.klass.build_search(scope(:find), &block)
- end
-
- def search(options = {}, &block)
- searcher = build_search(options, &block)
- searcher.all
+ @reflection.klass.build_search(options.merge(:scope => scope(:find)[:conditions]), &block)
end
end
@@ -52,4 +53,4 @@ class HasManyAssociation
alias_method_chain :count, :searchgasm
end
end
-end
+end
@@ -3,8 +3,11 @@ module Searchgasm
module ActiveRecord
module Base
def self.included(klass)
- klass.alias_method :new_search, :build_search
- klass.include Protection
+ klass.class_eval do
+ alias_method :new_conditions, :build_conditions
+ alias_method :new_search, :build_search
+ include Protection
+ end
end
def calculate_with_searchgasm(*args)
@@ -14,11 +17,11 @@ def calculate_with_searchgasm(*args)
calculate_without_searchgasm(*args)
end
- def find_every_with_searchgasm(*args)
+ def find_with_searchgasm(*args)
options = args.extract_options!
options = sanitize_options_with_searchgasm(options)
args << options
- find_every_without_searchgasm(options)
+ find_without_searchgasm(*args)
end
def scope_with_searchgasm(method, key = nil)
@@ -27,6 +30,10 @@ def scope_with_searchgasm(method, key = nil)
scope
end
+ def build_conditions(options = {})
+ BinaryLogic::Searchgasm::Search::Conditions(self, options)
+ end
+
def build_search(options = {})
searcher = searchgasm_searcher(options)
yield searcher if block_given?
@@ -37,7 +44,7 @@ def build_search(options = {})
def sanitize_options_with_searchgasm(options)
searchgasm_searcher(options).sanitize
end
-
+
def searchgasm_searcher(options)
BinaryLogic::Searchgasm::Search::Base.new(self, options)
end
@@ -51,9 +58,13 @@ def searchgasm_searcher(options)
module ::ActiveRecord
class Base
class << self
- alias_method_chain :calculate, :searchgasm
- alias_method_chain :find_every, :searchgasm
- alias_method_chain :scope, :searchgasm
+ alias_method_chain :calculate, :searchgasm
+ alias_method_chain :find, :searchgasm
+ alias_method_chain :scope, :searchgasm
+
+ def valid_find_options
+ VALID_FIND_OPTIONS
+ end
end
end
end
@@ -3,10 +3,12 @@ module Searchgasm
module ActiveRecord
module Protection
def self.included(klass)
- klass.alias_method :new_search, :build_search
- klass.alias_method :findwp, :find_with_protection
- klass.alias_method :allwp, :all_with_protection
- klass.alias_method :firstwp, :first_with_protection
+ klass.class_eval do
+ alias_method :new_search, :build_search
+ alias_method :findwp, :find_with_protection
+ alias_method :allwp, :all_with_protection
+ alias_method :firstwp, :first_with_protection
+ end
end
def find_with_protection(*args)
@@ -25,7 +25,6 @@ def #{option}=(value); self.options[:#{option}] = value; end
end
alias_method :per_page, :limit
- alias_method :per_page=, :limit=
def all
klass.all(sanitize)
@@ -56,7 +55,22 @@ def find(target)
def first
klass.first(sanitize)
end
-
+
+ def include(sanitize = false)
+ includes = [self.options[:include], conditions.includes].flatten.compact
+ includes.blank? ? nil : (includes.size == 1 ? includes.first : includes)
+ end
+
+ def limit=(value)
+ return options[:limit] = nil if value.nil? || value == 0
+
+ old_limit = options[:limit]
+ options[:limit] = value
+ self.page = @page if !@page.blank? # retry page now that limit is set
+ value
+ end
+ alias_method :per_page=, :limit=
+
def options
@options ||= {}
end
@@ -89,7 +103,20 @@ def page
end
def page=(value)
- self.offset = value * limit
+ return self.offset = nil if value.nil?
+
+ if limit.blank?
+ @page = value
+ else
+ @page = nil
+ self.offset = value * limit
+ end
+ value
+ end
+
+ def reset!
+ conditions.reset!
+ self.options = {}
end
def sanitize
@@ -101,6 +128,14 @@ def sanitize
end
find_options
end
+
+ def scope
+ conditions.scope
+ end
+
+ def scope=(value)
+ conditions.scope = value
+ end
end
end
end
Oops, something went wrong.

0 comments on commit cf23760

Please sign in to comment.