yoomee-searchlogic 2.4.27

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.
Files changed (44) hide show
  1. data/.gitignore +6 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +308 -0
  4. data/Rakefile +35 -0
  5. data/VERSION.yml +5 -0
  6. data/init.rb +1 -0
  7. data/lib/searchlogic.rb +56 -0
  8. data/lib/searchlogic/active_record/association_proxy.rb +19 -0
  9. data/lib/searchlogic/active_record/consistency.rb +49 -0
  10. data/lib/searchlogic/active_record/named_scope_tools.rb +101 -0
  11. data/lib/searchlogic/core_ext/object.rb +43 -0
  12. data/lib/searchlogic/core_ext/proc.rb +17 -0
  13. data/lib/searchlogic/named_scopes/alias_scope.rb +67 -0
  14. data/lib/searchlogic/named_scopes/association_conditions.rb +132 -0
  15. data/lib/searchlogic/named_scopes/association_ordering.rb +44 -0
  16. data/lib/searchlogic/named_scopes/conditions.rb +232 -0
  17. data/lib/searchlogic/named_scopes/or_conditions.rb +141 -0
  18. data/lib/searchlogic/named_scopes/ordering.rb +48 -0
  19. data/lib/searchlogic/rails_helpers.rb +79 -0
  20. data/lib/searchlogic/search.rb +26 -0
  21. data/lib/searchlogic/search/base.rb +26 -0
  22. data/lib/searchlogic/search/conditions.rb +58 -0
  23. data/lib/searchlogic/search/date_parts.rb +23 -0
  24. data/lib/searchlogic/search/implementation.rb +14 -0
  25. data/lib/searchlogic/search/method_missing.rb +123 -0
  26. data/lib/searchlogic/search/ordering.rb +10 -0
  27. data/lib/searchlogic/search/scopes.rb +19 -0
  28. data/lib/searchlogic/search/to_yaml.rb +38 -0
  29. data/lib/searchlogic/search/unknown_condition_error.rb +15 -0
  30. data/rails/init.rb +1 -0
  31. data/searchlogic.gemspec +98 -0
  32. data/spec/searchlogic/active_record/association_proxy_spec.rb +23 -0
  33. data/spec/searchlogic/active_record/consistency_spec.rb +28 -0
  34. data/spec/searchlogic/core_ext/object_spec.rb +9 -0
  35. data/spec/searchlogic/core_ext/proc_spec.rb +8 -0
  36. data/spec/searchlogic/named_scopes/alias_scope_spec.rb +23 -0
  37. data/spec/searchlogic/named_scopes/association_conditions_spec.rb +203 -0
  38. data/spec/searchlogic/named_scopes/association_ordering_spec.rb +27 -0
  39. data/spec/searchlogic/named_scopes/conditions_spec.rb +319 -0
  40. data/spec/searchlogic/named_scopes/or_conditions_spec.rb +66 -0
  41. data/spec/searchlogic/named_scopes/ordering_spec.rb +34 -0
  42. data/spec/searchlogic/search_spec.rb +497 -0
  43. data/spec/spec_helper.rb +132 -0
  44. metadata +136 -0
@@ -0,0 +1,26 @@
1
+ module Searchlogic
2
+ # A class that acts like a model, creates attr_accessors for named_scopes, and then
3
+ # chains together everything when an "action" method is called. It basically makes
4
+ # implementing search forms in your application effortless:
5
+ #
6
+ # search = User.search
7
+ # search.username_like = "bjohnson"
8
+ # search.all
9
+ #
10
+ # Is equivalent to:
11
+ #
12
+ # User.search(:username_like => "bjohnson").all
13
+ #
14
+ # Is equivalent to:
15
+ #
16
+ # User.username_like("bjohnson").all
17
+ class Search
18
+ include Base
19
+ include Conditions
20
+ include DateParts
21
+ include MethodMissing
22
+ include Scopes
23
+ include Ordering
24
+ include ToYaml
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ module Searchlogic
2
+ class Search
3
+ module Base
4
+ def self.included(klass)
5
+ klass.class_eval do
6
+ attr_accessor :klass, :current_scope
7
+ undef :id if respond_to?(:id)
8
+ end
9
+ end
10
+
11
+ # Creates a new search object for the given class. Ex:
12
+ #
13
+ # Searchlogic::Search.new(User, {}, {:username_like => "bjohnson"})
14
+ def initialize(klass, current_scope, conditions = {})
15
+ self.klass = klass
16
+ self.current_scope = current_scope
17
+ @conditions ||= {}
18
+ self.conditions = conditions if conditions.is_a?(Hash)
19
+ end
20
+
21
+ def clone
22
+ self.class.new(klass, current_scope && current_scope.clone, conditions.clone)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,58 @@
1
+ module Searchlogic
2
+ class Search
3
+ module Conditions
4
+ # Returns a hash of the current conditions set.
5
+ def conditions
6
+ mass_conditions.clone.merge(@conditions)
7
+ end
8
+
9
+ def compact_conditions
10
+ conditions.select { |k,v| !v.blank? }
11
+ end
12
+
13
+ # Accepts a hash of conditions.
14
+ def conditions=(values)
15
+ values.each do |condition, value|
16
+ mass_conditions[condition.to_sym] = value
17
+ value.delete_if { |v| ignore_value?(v) } if value.is_a?(Array)
18
+ next if ignore_value?(value)
19
+ send("#{condition}=", value)
20
+ end
21
+ end
22
+
23
+ # Delete a condition from the search. Since conditions map to named scopes,
24
+ # if a named scope accepts a parameter there is no way to actually delete
25
+ # the scope if you do not want it anymore. A nil value might be meaningful
26
+ # to that scope.
27
+ def delete(*names)
28
+ names.each do |name|
29
+ @conditions.delete(name.to_sym)
30
+ mass_conditions.delete(name)
31
+ end
32
+ self
33
+ end
34
+
35
+ private
36
+ # This is here as a hook to allow people to modify the order in which the conditions are called, for whatever reason.
37
+ def conditions_array
38
+ @conditions.to_a
39
+ end
40
+
41
+ def write_condition(name, value)
42
+ @conditions[name] = value
43
+ end
44
+
45
+ def read_condition(name)
46
+ @conditions[name]
47
+ end
48
+
49
+ def mass_conditions
50
+ @mass_conditions ||= {}
51
+ end
52
+
53
+ def ignore_value?(value)
54
+ (value.is_a?(String) && value.blank?) || (value.is_a?(Array) && value.empty?)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,23 @@
1
+ module Searchlogic
2
+ class Search
3
+ module DateParts
4
+ def conditions=(values)
5
+ values.clone.each do |condition, value|
6
+ # if a condition name ends with "(1i)", assume it's date / datetime
7
+ if condition =~ /(.*)\(1i\)$/
8
+ date_scope_name = $1
9
+ date_parts = (1..6).to_a.map do |idx|
10
+ values.delete("#{ date_scope_name }(#{ idx }i)")
11
+ end.reject{|s| s.blank? }.map{|s| s.to_i }
12
+
13
+ # did we get enough info to build a time?
14
+ if date_parts.length >= 3
15
+ values[date_scope_name] = Time.zone.local(*date_parts)
16
+ end
17
+ end
18
+ end
19
+ super
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,14 @@
1
+ module Searchlogic
2
+ class Search
3
+ # Responsible for adding a "search" method into your models.
4
+ module Implementation
5
+ # Additional method, gets aliased as "search" if that method
6
+ # is available. A lot of other libraries like to use "search"
7
+ # as well, so if you have a conflict like this, you can use
8
+ # this method directly.
9
+ def searchlogic(conditions = {})
10
+ Search.new(self, scope(:find), conditions)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,123 @@
1
+ module Searchlogic
2
+ class Search
3
+ module MethodMissing
4
+ def respond_to?(*args)
5
+ super || scope?(normalize_scope_name(args.first))
6
+ rescue Searchlogic::NamedScopes::OrConditions::UnknownConditionError
7
+ false
8
+ end
9
+
10
+ private
11
+ def method_missing(name, *args, &block)
12
+ condition_name = condition_name(name)
13
+ scope_name = scope_name(condition_name)
14
+
15
+ if setter?(name)
16
+ if scope?(scope_name)
17
+ if args.size == 1
18
+ write_condition(
19
+ condition_name,
20
+ type_cast(
21
+ args.first,
22
+ cast_type(scope_name),
23
+ scope_options(scope_name).respond_to?(:searchlogic_options) ? scope_options(scope_name).searchlogic_options : {}
24
+ )
25
+ )
26
+ else
27
+ write_condition(condition_name, args)
28
+ end
29
+ else
30
+ raise UnknownConditionError.new(condition_name)
31
+ end
32
+ elsif scope?(scope_name) && args.size <= 1
33
+ if args.size == 0
34
+ read_condition(condition_name)
35
+ else
36
+ send("#{condition_name}=", *args)
37
+ self
38
+ end
39
+ else
40
+ scope = conditions_array.inject(klass.scoped(current_scope) || {}) do |scope, condition|
41
+ scope_name, value = condition
42
+ scope_name = normalize_scope_name(scope_name)
43
+ klass.send(scope_name, value) if !klass.respond_to?(scope_name)
44
+ arity = klass.named_scope_arity(scope_name)
45
+
46
+ if !arity || arity == 0
47
+ if value == true
48
+ scope.send(scope_name)
49
+ else
50
+ scope
51
+ end
52
+ elsif arity == -1
53
+ scope.send(scope_name, *(value.is_a?(Array) ? value : [value]))
54
+ else
55
+ scope.send(scope_name, value)
56
+ end
57
+ end
58
+ scope.send(name, *args, &block)
59
+ end
60
+ end
61
+
62
+ def normalize_scope_name(scope_name)
63
+ case
64
+ when klass.scopes.key?(scope_name.to_sym) then scope_name.to_sym
65
+ when klass.column_names.include?(scope_name.to_s) then "#{scope_name}_equals".to_sym
66
+ else scope_name.to_sym
67
+ end
68
+ end
69
+
70
+ def setter?(name)
71
+ !(name.to_s =~ /=$/).nil?
72
+ end
73
+
74
+ def condition_name(name)
75
+ condition = name.to_s.match(/(\w+)=?$/)
76
+ condition ? condition[1].to_sym : nil
77
+ end
78
+
79
+ def cast_type(name)
80
+ named_scope_options = scope_options(name)
81
+ arity = klass.named_scope_arity(name)
82
+ if !arity || arity == 0
83
+ :boolean
84
+ else
85
+ named_scope_options.respond_to?(:searchlogic_options) ? named_scope_options.searchlogic_options[:type] : :string
86
+ end
87
+ end
88
+
89
+ def type_cast(value, type, options = {})
90
+ case value
91
+ when Array
92
+ value.collect { |v| type_cast(v, type) }
93
+ when Range
94
+ Range.new(type_cast(value.first, type), type_cast(value.last, type))
95
+ else
96
+ # Let's leverage ActiveRecord's type casting, so that casting is consistent
97
+ # with the other models.
98
+ column_for_type_cast = ::ActiveRecord::ConnectionAdapters::Column.new("", nil)
99
+ column_for_type_cast.instance_variable_set(:@type, type)
100
+ casted_value = column_for_type_cast.type_cast(value)
101
+
102
+ if Time.zone && casted_value.is_a?(Time)
103
+ if value.is_a?(String)
104
+ if options[:skip_conversion]
105
+ casted_value.utc
106
+ else
107
+ (casted_value + (Time.zone.utc_offset * -1)).in_time_zone
108
+ end
109
+ else
110
+ if options[:skip_conversion]
111
+ casted_value.utc
112
+ else
113
+ casted_value.in_time_zone
114
+ end
115
+ end
116
+ else
117
+ casted_value
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,10 @@
1
+ module Searchlogic
2
+ class Search
3
+ module Ordering
4
+ # Returns the column we are currently ordering by
5
+ def ordering_by
6
+ order && order.to_s.gsub(/^(ascend|descend)_by_/, '')
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,19 @@
1
+ module Searchlogic
2
+ class Search
3
+ module Scopes
4
+ private
5
+ def scope_name(condition_name)
6
+ condition_name && normalize_scope_name(condition_name)
7
+ end
8
+
9
+ def scope?(scope_name)
10
+ klass.scopes.key?(scope_name) || klass.condition?(scope_name)
11
+ end
12
+
13
+ def scope_options(name)
14
+ klass.send(name, nil) if !klass.respond_to?(name) # We need to set up the named scope if it doesn't exist, so we can get a value for named_scope_options
15
+ klass.named_scope_options(name)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,38 @@
1
+ module Searchlogic
2
+ class Search
3
+ module ToYaml
4
+ def self.included(klass)
5
+ klass.class_eval do
6
+ yaml_as "tag:ruby.yaml.org,2002:class"
7
+ extend ClassMethods
8
+ include InstanceMethods
9
+ end
10
+ end
11
+
12
+ module ClassMethods
13
+ def yaml_new(klass, tag, val)
14
+ raise "ass"
15
+ end
16
+ end
17
+
18
+ module InstanceMethods
19
+ def to_yaml( opts = {} )
20
+ YAML::quick_emit( self, opts ) do |out|
21
+ out.map("tag:ruby.yaml.org,2002:object:Searchlogic::Search") do |map|
22
+ map.add('class_name', klass.name)
23
+ map.add('current_scope', current_scope)
24
+ map.add('conditions', conditions)
25
+ end
26
+ end
27
+ end
28
+
29
+ def yaml_initialize(taguri, attributes = {})
30
+ self.klass = attributes["class_name"].constantize
31
+ self.current_scope = attributes["current_scope"]
32
+ @conditions ||= {}
33
+ self.conditions = attributes["conditions"]
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,15 @@
1
+ module Searchlogic
2
+ class Search
3
+ # Is an invalid condition is used this error will be raised. Ex:
4
+ #
5
+ # User.search(:unkown => true)
6
+ #
7
+ # Where unknown is not a valid named scope for the User model.
8
+ class UnknownConditionError < StandardError
9
+ def initialize(condition)
10
+ msg = "The #{condition} is not a valid condition. You may only use conditions that map to a named scope"
11
+ super(msg)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1 @@
1
+ require "searchlogic"
@@ -0,0 +1,98 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{searchlogic}
8
+ s.version = "2.4.27"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Yoomee Developers, Ben Johnson of Binary Logic"]
12
+ s.date = %q{2010-09-22}
13
+ s.description = %q{Searchlogic makes using ActiveRecord named scopes easier and less repetitive.}
14
+ s.email = %q{developers@yoomee.com, bjohnson@binarylogic.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".gitignore",
21
+ "LICENSE",
22
+ "README.rdoc",
23
+ "Rakefile",
24
+ "VERSION.yml",
25
+ "init.rb",
26
+ "lib/searchlogic.rb",
27
+ "lib/searchlogic/active_record/association_proxy.rb",
28
+ "lib/searchlogic/active_record/consistency.rb",
29
+ "lib/searchlogic/active_record/named_scope_tools.rb",
30
+ "lib/searchlogic/core_ext/object.rb",
31
+ "lib/searchlogic/core_ext/proc.rb",
32
+ "lib/searchlogic/named_scopes/alias_scope.rb",
33
+ "lib/searchlogic/named_scopes/association_conditions.rb",
34
+ "lib/searchlogic/named_scopes/association_ordering.rb",
35
+ "lib/searchlogic/named_scopes/conditions.rb",
36
+ "lib/searchlogic/named_scopes/or_conditions.rb",
37
+ "lib/searchlogic/named_scopes/ordering.rb",
38
+ "lib/searchlogic/rails_helpers.rb",
39
+ "lib/searchlogic/search.rb",
40
+ "lib/searchlogic/search/base.rb",
41
+ "lib/searchlogic/search/conditions.rb",
42
+ "lib/searchlogic/search/date_parts.rb",
43
+ "lib/searchlogic/search/implementation.rb",
44
+ "lib/searchlogic/search/method_missing.rb",
45
+ "lib/searchlogic/search/ordering.rb",
46
+ "lib/searchlogic/search/scopes.rb",
47
+ "lib/searchlogic/search/to_yaml.rb",
48
+ "lib/searchlogic/search/unknown_condition_error.rb",
49
+ "rails/init.rb",
50
+ "searchlogic.gemspec",
51
+ "spec/searchlogic/active_record/association_proxy_spec.rb",
52
+ "spec/searchlogic/active_record/consistency_spec.rb",
53
+ "spec/searchlogic/core_ext/object_spec.rb",
54
+ "spec/searchlogic/core_ext/proc_spec.rb",
55
+ "spec/searchlogic/named_scopes/alias_scope_spec.rb",
56
+ "spec/searchlogic/named_scopes/association_conditions_spec.rb",
57
+ "spec/searchlogic/named_scopes/association_ordering_spec.rb",
58
+ "spec/searchlogic/named_scopes/conditions_spec.rb",
59
+ "spec/searchlogic/named_scopes/or_conditions_spec.rb",
60
+ "spec/searchlogic/named_scopes/ordering_spec.rb",
61
+ "spec/searchlogic/search_spec.rb",
62
+ "spec/spec_helper.rb"
63
+ ]
64
+ s.homepage = %q{http://github.com/binarylogic/searchlogic}
65
+ s.rdoc_options = ["--charset=UTF-8"]
66
+ s.require_paths = ["lib"]
67
+ s.rubyforge_project = %q{searchlogic}
68
+ s.rubygems_version = %q{1.3.7}
69
+ s.summary = %q{Searchlogic makes using ActiveRecord named scopes easier and less repetitive.}
70
+ s.test_files = [
71
+ "spec/searchlogic/active_record/association_proxy_spec.rb",
72
+ "spec/searchlogic/active_record/consistency_spec.rb",
73
+ "spec/searchlogic/core_ext/object_spec.rb",
74
+ "spec/searchlogic/core_ext/proc_spec.rb",
75
+ "spec/searchlogic/named_scopes/alias_scope_spec.rb",
76
+ "spec/searchlogic/named_scopes/association_conditions_spec.rb",
77
+ "spec/searchlogic/named_scopes/association_ordering_spec.rb",
78
+ "spec/searchlogic/named_scopes/conditions_spec.rb",
79
+ "spec/searchlogic/named_scopes/or_conditions_spec.rb",
80
+ "spec/searchlogic/named_scopes/ordering_spec.rb",
81
+ "spec/searchlogic/search_spec.rb",
82
+ "spec/spec_helper.rb"
83
+ ]
84
+
85
+ if s.respond_to? :specification_version then
86
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
87
+ s.specification_version = 3
88
+
89
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
90
+ s.add_runtime_dependency(%q<activerecord>, [">= 2.0.0"])
91
+ else
92
+ s.add_dependency(%q<activerecord>, [">= 2.0.0"])
93
+ end
94
+ else
95
+ s.add_dependency(%q<activerecord>, [">= 2.0.0"])
96
+ end
97
+ end
98
+