searchlogic 1.5.10 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,29 @@
1
+ # Rails <2.x doesn't define #except
2
+ class Hash #:nodoc:
3
+ # Returns a new hash without the given keys.
4
+ def except(*keys)
5
+ clone.except!(*keys)
6
+ end unless method_defined?(:except)
7
+
8
+ # Replaces the hash without the given keys.
9
+ def except!(*keys)
10
+ keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
11
+ keys.each { |key| delete(key) }
12
+ self
13
+ end unless method_defined?(:except!)
14
+ end
15
+
16
+ # NamedScope is new to Rails 2.1
17
+ unless defined? ActiveRecord::NamedScope
18
+ require 'awesome_nested_set/named_scope'
19
+ ActiveRecord::Base.class_eval do
20
+ include CollectiveIdea::NamedScope
21
+ end
22
+ end
23
+
24
+ # Rails 1.2.x doesn't define #quoted_table_name
25
+ class ActiveRecord::Base #:nodoc:
26
+ def self.quoted_table_name
27
+ self.connection.quote_column_name(self.table_name)
28
+ end unless methods.include?('quoted_table_name')
29
+ end
@@ -0,0 +1,40 @@
1
+ module CollectiveIdea #:nodoc:
2
+ module Acts #:nodoc:
3
+ module NestedSet #:nodoc:
4
+ # This module provides some helpers for the model classes using acts_as_nested_set.
5
+ # It is included by default in all views.
6
+ #
7
+ module Helper
8
+ # Returns options for select.
9
+ # You can exclude some items from the tree.
10
+ # You can pass a block receiving an item and returning the string displayed in the select.
11
+ #
12
+ # == Params
13
+ # * +class_or_item+ - Class name or top level times
14
+ # * +mover+ - The item that is being move, used to exlude impossible moves
15
+ # * +&block+ - a block that will be used to display: { |item| ... item.name }
16
+ #
17
+ # == Usage
18
+ #
19
+ # <%= f.select :parent_id, nested_set_options(Category, @category) {|i|
20
+ # "#{'–' * i.level} #{i.name}"
21
+ # }) %>
22
+ #
23
+ def nested_set_options(class_or_item, mover = nil)
24
+ class_or_item = class_or_item.roots if class_or_item.is_a?(Class)
25
+ items = Array(class_or_item)
26
+ result = []
27
+ items.each do |root|
28
+ result += root.self_and_descendants.map do |i|
29
+ if mover.nil? || mover.new_record? || mover.move_possible?(i)
30
+ [yield(i), i.id]
31
+ end
32
+ end.compact
33
+ end
34
+ result
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,140 @@
1
+ # Taken from Rails 2.1
2
+ module CollectiveIdea #:nodoc:
3
+ module NamedScope #:nodoc:
4
+ # All subclasses of ActiveRecord::Base have two named_scopes:
5
+ # * <tt>all</tt>, which is similar to a <tt>find(:all)</tt> query, and
6
+ # * <tt>scoped</tt>, which allows for the creation of anonymous scopes, on the fly:
7
+ #
8
+ # Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions)
9
+ #
10
+ # These anonymous scopes tend to be useful when procedurally generating complex queries, where passing
11
+ # intermediate values (scopes) around as first-class objects is convenient.
12
+ def self.included(base)
13
+ base.class_eval do
14
+ extend ClassMethods
15
+ named_scope :scoped, lambda { |scope| scope }
16
+ end
17
+ end
18
+
19
+ module ClassMethods
20
+ def scopes
21
+ read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
22
+ end
23
+
24
+ # Adds a class method for retrieving and querying objects. A scope represents a narrowing of a database query,
25
+ # such as <tt>:conditions => {:color => :red}, :select => 'shirts.*', :include => :washing_instructions</tt>.
26
+ #
27
+ # class Shirt < ActiveRecord::Base
28
+ # named_scope :red, :conditions => {:color => 'red'}
29
+ # named_scope :dry_clean_only, :joins => :washing_instructions, :conditions => ['washing_instructions.dry_clean_only = ?', true]
30
+ # end
31
+ #
32
+ # The above calls to <tt>named_scope</tt> define class methods <tt>Shirt.red</tt> and <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>,
33
+ # in effect, represents the query <tt>Shirt.find(:all, :conditions => {:color => 'red'})</tt>.
34
+ #
35
+ # Unlike Shirt.find(...), however, the object returned by <tt>Shirt.red</tt> is not an Array; it resembles the association object
36
+ # constructed by a <tt>has_many</tt> declaration. For instance, you can invoke <tt>Shirt.red.find(:first)</tt>, <tt>Shirt.red.count</tt>,
37
+ # <tt>Shirt.red.find(:all, :conditions => {:size => 'small'})</tt>. Also, just
38
+ # as with the association objects, name scopes acts like an Array, implementing Enumerable; <tt>Shirt.red.each(&block)</tt>,
39
+ # <tt>Shirt.red.first</tt>, and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if Shirt.red really were an Array.
40
+ #
41
+ # These named scopes are composable. For instance, <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are both red and dry clean only.
42
+ # Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt> returns the number of garments
43
+ # for which these criteria obtain. Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
44
+ #
45
+ # All scopes are available as class methods on the ActiveRecord descendent upon which the scopes were defined. But they are also available to
46
+ # <tt>has_many</tt> associations. If,
47
+ #
48
+ # class Person < ActiveRecord::Base
49
+ # has_many :shirts
50
+ # end
51
+ #
52
+ # then <tt>elton.shirts.red.dry_clean_only</tt> will return all of Elton's red, dry clean
53
+ # only shirts.
54
+ #
55
+ # Named scopes can also be procedural.
56
+ #
57
+ # class Shirt < ActiveRecord::Base
58
+ # named_scope :colored, lambda { |color|
59
+ # { :conditions => { :color => color } }
60
+ # }
61
+ # end
62
+ #
63
+ # In this example, <tt>Shirt.colored('puce')</tt> finds all puce shirts.
64
+ #
65
+ # Named scopes can also have extensions, just as with <tt>has_many</tt> declarations:
66
+ #
67
+ # class Shirt < ActiveRecord::Base
68
+ # named_scope :red, :conditions => {:color => 'red'} do
69
+ # def dom_id
70
+ # 'red_shirts'
71
+ # end
72
+ # end
73
+ # end
74
+ #
75
+ #
76
+ # For testing complex named scopes, you can examine the scoping options using the
77
+ # <tt>proxy_options</tt> method on the proxy itself.
78
+ #
79
+ # class Shirt < ActiveRecord::Base
80
+ # named_scope :colored, lambda { |color|
81
+ # { :conditions => { :color => color } }
82
+ # }
83
+ # end
84
+ #
85
+ # expected_options = { :conditions => { :colored => 'red' } }
86
+ # assert_equal expected_options, Shirt.colored('red').proxy_options
87
+ def named_scope(name, options = {}, &block)
88
+ scopes[name] = lambda do |parent_scope, *args|
89
+ Scope.new(parent_scope, case options
90
+ when Hash
91
+ options
92
+ when Proc
93
+ options.call(*args)
94
+ end, &block)
95
+ end
96
+ (class << self; self end).instance_eval do
97
+ define_method name do |*args|
98
+ scopes[name].call(self, *args)
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ class Scope
105
+ attr_reader :proxy_scope, :proxy_options
106
+ [].methods.each { |m| delegate m, :to => :proxy_found unless m =~ /(^__|^nil\?|^send|class|extend|find|count|sum|average|maximum|minimum|paginate)/ }
107
+ delegate :scopes, :with_scope, :to => :proxy_scope
108
+
109
+ def initialize(proxy_scope, options, &block)
110
+ [options[:extend]].flatten.each { |extension| extend extension } if options[:extend]
111
+ extend Module.new(&block) if block_given?
112
+ @proxy_scope, @proxy_options = proxy_scope, options.except(:extend)
113
+ end
114
+
115
+ def reload
116
+ load_found; self
117
+ end
118
+
119
+ protected
120
+ def proxy_found
121
+ @found || load_found
122
+ end
123
+
124
+ private
125
+ def method_missing(method, *args, &block)
126
+ if scopes.include?(method)
127
+ scopes[method].call(self, *args)
128
+ else
129
+ with_scope :find => proxy_options do
130
+ proxy_scope.send(method, *args, &block)
131
+ end
132
+ end
133
+ end
134
+
135
+ def load_found
136
+ @found = find(:all)
137
+ end
138
+ end
139
+ end
140
+ end
@@ -142,7 +142,7 @@ module SearchTests
142
142
  search.conditions.users.id_greater_than = 2
143
143
  search.page = 3
144
144
  search.readonly = true
145
- assert_equal({:joins => :users, :offset => 4, :readonly => true, :conditions => ["\"accounts\".\"name\" LIKE ? AND \"users\".\"id\" > ?", "%Binary%", 2], :limit => 2 }, search.sanitize)
145
+ assert_equal({:select => "DISTINCT \"accounts\".*", :joins => :users, :offset => 4, :readonly => true, :conditions => ["\"accounts\".\"name\" LIKE ? AND \"users\".\"id\" > ?", "%Binary%", 2], :limit => 2 }, search.sanitize)
146
146
  end
147
147
 
148
148
  def test_scope
data/test/test_helper.rb CHANGED
@@ -3,7 +3,7 @@ require "rubygems"
3
3
  require "ruby-debug"
4
4
  require "active_record"
5
5
  require "active_record/fixtures"
6
- require File.dirname(__FILE__) + '/libs/acts_as_tree'
6
+ require File.dirname(__FILE__) + '/libs/awesome_nested_set'
7
7
  require File.dirname(__FILE__) + '/libs/rexml_fix'
8
8
  require File.dirname(__FILE__) + '/../lib/searchlogic' unless defined?(Searchlogic)
9
9
 
@@ -34,6 +34,8 @@ ActiveRecord::Schema.define(:version => 1) do
34
34
  t.datetime :updated_at
35
35
  t.integer :account_id
36
36
  t.integer :parent_id
37
+ t.integer :lft
38
+ t.integer :rgt
37
39
  t.string :first_name
38
40
  t.string :last_name
39
41
  t.boolean :active
@@ -78,7 +80,7 @@ class UserGroup < ActiveRecord::Base
78
80
  end
79
81
 
80
82
  class User < ActiveRecord::Base
81
- acts_as_tree
83
+ acts_as_nested_set
82
84
  belongs_to :account
83
85
  has_many :orders, :dependent => :destroy
84
86
  has_many :cats, :dependent => :destroy
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: searchlogic
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.10
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Johnson of Binary Logic
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-12-07 00:00:00 -05:00
12
+ date: 2008-12-08 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -69,6 +69,7 @@ extra_rdoc_files:
69
69
  - lib/searchlogic/condition/less_than.rb
70
70
  - lib/searchlogic/condition/less_than_or_equal_to.rb
71
71
  - lib/searchlogic/condition/like.rb
72
+ - lib/searchlogic/condition/nested_set.rb
72
73
  - lib/searchlogic/condition/nil.rb
73
74
  - lib/searchlogic/condition/not_begin_with.rb
74
75
  - lib/searchlogic/condition/not_blank.rb
@@ -78,7 +79,6 @@ extra_rdoc_files:
78
79
  - lib/searchlogic/condition/not_like.rb
79
80
  - lib/searchlogic/condition/not_nil.rb
80
81
  - lib/searchlogic/condition/sibling_of.rb
81
- - lib/searchlogic/condition/tree.rb
82
82
  - lib/searchlogic/conditions/any_or_all.rb
83
83
  - lib/searchlogic/conditions/base.rb
84
84
  - lib/searchlogic/conditions/groups.rb
@@ -175,6 +175,7 @@ files:
175
175
  - lib/searchlogic/condition/less_than.rb
176
176
  - lib/searchlogic/condition/less_than_or_equal_to.rb
177
177
  - lib/searchlogic/condition/like.rb
178
+ - lib/searchlogic/condition/nested_set.rb
178
179
  - lib/searchlogic/condition/nil.rb
179
180
  - lib/searchlogic/condition/not_begin_with.rb
180
181
  - lib/searchlogic/condition/not_blank.rb
@@ -184,7 +185,6 @@ files:
184
185
  - lib/searchlogic/condition/not_like.rb
185
186
  - lib/searchlogic/condition/not_nil.rb
186
187
  - lib/searchlogic/condition/sibling_of.rb
187
- - lib/searchlogic/condition/tree.rb
188
188
  - lib/searchlogic/conditions/any_or_all.rb
189
189
  - lib/searchlogic/conditions/base.rb
190
190
  - lib/searchlogic/conditions/groups.rb
@@ -298,7 +298,10 @@ files:
298
298
  - test/fixtures/orders.yml
299
299
  - test/fixtures/user_groups.yml
300
300
  - test/fixtures/users.yml
301
- - test/libs/acts_as_tree.rb
301
+ - test/libs/awesome_nested_set/compatability.rb
302
+ - test/libs/awesome_nested_set/helper.rb
303
+ - test/libs/awesome_nested_set/named_scope.rb
304
+ - test/libs/awesome_nested_set.rb
302
305
  - test/libs/rexml_fix.rb
303
306
  - test/modifier_tests/day_of_month_test.rb
304
307
  - test/search_tests/base_test.rb
@@ -1,98 +0,0 @@
1
- module ActiveRecord
2
- module Acts
3
- module Tree
4
- def self.included(base)
5
- base.extend(ClassMethods)
6
- end
7
-
8
- # Specify this +acts_as+ extension if you want to model a tree structure by providing a parent association and a children
9
- # association. This requires that you have a foreign key column, which by default is called +parent_id+.
10
- #
11
- # class Category < ActiveRecord::Base
12
- # acts_as_tree :order => "name"
13
- # end
14
- #
15
- # Example:
16
- # root
17
- # \_ child1
18
- # \_ subchild1
19
- # \_ subchild2
20
- #
21
- # root = Category.create("name" => "root")
22
- # child1 = root.children.create("name" => "child1")
23
- # subchild1 = child1.children.create("name" => "subchild1")
24
- #
25
- # root.parent # => nil
26
- # child1.parent # => root
27
- # root.children # => [child1]
28
- # root.children.first.children.first # => subchild1
29
- #
30
- # In addition to the parent and children associations, the following instance methods are added to the class
31
- # after calling <tt>acts_as_tree</tt>:
32
- # * <tt>siblings</tt> - Returns all the children of the parent, excluding the current node (<tt>[subchild2]</tt> when called on <tt>subchild1</tt>)
33
- # * <tt>self_and_siblings</tt> - Returns all the children of the parent, including the current node (<tt>[subchild1, subchild2]</tt> when called on <tt>subchild1</tt>)
34
- # * <tt>ancestors</tt> - Returns all the ancestors of the current node (<tt>[child1, root]</tt> when called on <tt>subchild2</tt>)
35
- # * <tt>root</tt> - Returns the root of the current node (<tt>root</tt> when called on <tt>subchild2</tt>)
36
- module ClassMethods
37
- # Configuration options are:
38
- #
39
- # * <tt>foreign_key</tt> - specifies the column name to use for tracking of the tree (default: +parent_id+)
40
- # * <tt>order</tt> - makes it possible to sort the children according to this SQL snippet.
41
- # * <tt>counter_cache</tt> - keeps a count in a +children_count+ column if set to +true+ (default: +false+).
42
- def acts_as_tree(options = {})
43
- configuration = { :foreign_key => "parent_id", :order => nil, :counter_cache => nil }
44
- configuration.update(options) if options.is_a?(Hash)
45
-
46
- belongs_to :parent, :class_name => name, :foreign_key => configuration[:foreign_key], :counter_cache => configuration[:counter_cache]
47
- has_many :children, :class_name => name, :foreign_key => configuration[:foreign_key], :order => configuration[:order], :dependent => :destroy
48
-
49
- class_eval <<-EOV
50
- include ActiveRecord::Acts::Tree::InstanceMethods
51
-
52
- def self.roots
53
- find(:all, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
54
- end
55
-
56
- def self.root
57
- find(:first, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
58
- end
59
- EOV
60
- end
61
- end
62
-
63
- module InstanceMethods
64
- # Returns list of ancestors, starting from parent until root.
65
- #
66
- # subchild1.ancestors # => [child1, root]
67
- def ancestors
68
- node, nodes = self, []
69
- nodes << node = node.parent while node.parent
70
- nodes
71
- end
72
-
73
- # Returns the root node of the tree.
74
- def root
75
- node = self
76
- node = node.parent while node.parent
77
- node
78
- end
79
-
80
- # Returns all siblings of the current node.
81
- #
82
- # subchild1.siblings # => [subchild2]
83
- def siblings
84
- self_and_siblings - [self]
85
- end
86
-
87
- # Returns all siblings and a reference to the current node.
88
- #
89
- # subchild1.self_and_siblings # => [subchild1, subchild2]
90
- def self_and_siblings
91
- parent ? parent.children : self.class.roots
92
- end
93
- end
94
- end
95
- end
96
- end
97
-
98
- ActiveRecord::Base.send :include, ActiveRecord::Acts::Tree