searchlogic 1.5.10 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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