ransack 1.5.1 → 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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +47 -3
  3. data/CHANGELOG.md +106 -18
  4. data/CONTRIBUTING.md +56 -23
  5. data/Gemfile +16 -5
  6. data/README.md +114 -38
  7. data/Rakefile +30 -2
  8. data/lib/ransack.rb +9 -0
  9. data/lib/ransack/adapters/active_record/3.0/compat.rb +11 -8
  10. data/lib/ransack/adapters/active_record/3.0/context.rb +14 -22
  11. data/lib/ransack/adapters/active_record/3.1/context.rb +14 -22
  12. data/lib/ransack/adapters/active_record/context.rb +36 -31
  13. data/lib/ransack/adapters/active_record/ransack/constants.rb +113 -0
  14. data/lib/ransack/adapters/active_record/ransack/context.rb +64 -0
  15. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +48 -0
  16. data/lib/ransack/adapters/active_record/ransack/translate.rb +12 -0
  17. data/lib/ransack/adapters/active_record/ransack/visitor.rb +24 -0
  18. data/lib/ransack/adapters/mongoid.rb +13 -0
  19. data/lib/ransack/adapters/mongoid/3.2/.gitkeep +0 -0
  20. data/lib/ransack/adapters/mongoid/attributes/attribute.rb +37 -0
  21. data/lib/ransack/adapters/mongoid/attributes/order_predications.rb +17 -0
  22. data/lib/ransack/adapters/mongoid/attributes/predications.rb +141 -0
  23. data/lib/ransack/adapters/mongoid/base.rb +126 -0
  24. data/lib/ransack/adapters/mongoid/context.rb +208 -0
  25. data/lib/ransack/adapters/mongoid/inquiry_hash.rb +23 -0
  26. data/lib/ransack/adapters/mongoid/ransack/constants.rb +88 -0
  27. data/lib/ransack/adapters/mongoid/ransack/context.rb +60 -0
  28. data/lib/ransack/adapters/mongoid/ransack/nodes/condition.rb +27 -0
  29. data/lib/ransack/adapters/mongoid/ransack/translate.rb +13 -0
  30. data/lib/ransack/adapters/mongoid/ransack/visitor.rb +24 -0
  31. data/lib/ransack/adapters/mongoid/table.rb +35 -0
  32. data/lib/ransack/configuration.rb +22 -4
  33. data/lib/ransack/constants.rb +26 -120
  34. data/lib/ransack/context.rb +32 -60
  35. data/lib/ransack/helpers/form_builder.rb +50 -36
  36. data/lib/ransack/helpers/form_helper.rb +148 -104
  37. data/lib/ransack/naming.rb +11 -11
  38. data/lib/ransack/nodes.rb +2 -0
  39. data/lib/ransack/nodes/bindable.rb +12 -4
  40. data/lib/ransack/nodes/condition.rb +5 -22
  41. data/lib/ransack/nodes/grouping.rb +9 -10
  42. data/lib/ransack/nodes/sort.rb +3 -2
  43. data/lib/ransack/nodes/value.rb +1 -2
  44. data/lib/ransack/predicate.rb +3 -3
  45. data/lib/ransack/search.rb +46 -13
  46. data/lib/ransack/translate.rb +8 -8
  47. data/lib/ransack/version.rb +1 -1
  48. data/lib/ransack/visitor.rb +4 -16
  49. data/ransack.gemspec +1 -0
  50. data/spec/mongoid/adapters/mongoid/base_spec.rb +276 -0
  51. data/spec/mongoid/adapters/mongoid/context_spec.rb +56 -0
  52. data/spec/mongoid/configuration_spec.rb +66 -0
  53. data/spec/mongoid/dependencies_spec.rb +8 -0
  54. data/spec/mongoid/helpers/ransack_helper.rb +11 -0
  55. data/spec/mongoid/nodes/condition_spec.rb +34 -0
  56. data/spec/mongoid/nodes/grouping_spec.rb +13 -0
  57. data/spec/mongoid/predicate_spec.rb +155 -0
  58. data/spec/mongoid/search_spec.rb +446 -0
  59. data/spec/mongoid/support/mongoid.yml +6 -0
  60. data/spec/mongoid/support/schema.rb +128 -0
  61. data/spec/mongoid/translate_spec.rb +14 -0
  62. data/spec/mongoid_spec_helper.rb +59 -0
  63. data/spec/ransack/adapters/active_record/base_spec.rb +68 -35
  64. data/spec/ransack/dependencies_spec.rb +3 -1
  65. data/spec/ransack/helpers/form_builder_spec.rb +6 -6
  66. data/spec/ransack/helpers/form_helper_spec.rb +114 -47
  67. data/spec/ransack/nodes/condition_spec.rb +2 -2
  68. data/spec/ransack/search_spec.rb +2 -6
  69. data/spec/ransack/translate_spec.rb +1 -1
  70. data/spec/spec_helper.rb +2 -3
  71. data/spec/support/schema.rb +9 -0
  72. metadata +49 -4
@@ -0,0 +1,64 @@
1
+ require 'ransack/visitor'
2
+
3
+ module Ransack
4
+ class Context
5
+ attr_reader :arel_visitor
6
+
7
+ class << self
8
+
9
+ def for_class(klass, options = {})
10
+ if klass < ActiveRecord::Base
11
+ Adapters::ActiveRecord::Context.new(klass, options)
12
+ end
13
+ end
14
+
15
+ def for_object(object, options = {})
16
+ case object
17
+ when ActiveRecord::Relation
18
+ Adapters::ActiveRecord::Context.new(object.klass, options)
19
+ end
20
+ end
21
+
22
+ end # << self
23
+
24
+ def initialize(object, options = {})
25
+ @object = relation_for(object)
26
+ @klass = @object.klass
27
+ @join_dependency = join_dependency(@object)
28
+ @join_type = options[:join_type] || Polyamorous::OuterJoin
29
+ @search_key = options[:search_key] || Ransack.options[:search_key]
30
+
31
+ if ::ActiveRecord::VERSION::STRING >= Constants::RAILS_4_1
32
+ @base = @join_dependency.join_root
33
+ @engine = @base.base_klass.arel_engine
34
+ else
35
+ @base = @join_dependency.join_base
36
+ @engine = @base.arel_engine
37
+ end
38
+
39
+ @default_table = Arel::Table.new(
40
+ @base.table_name, :as => @base.aliased_table_name, :engine => @engine
41
+ )
42
+ @bind_pairs = Hash.new do |hash, key|
43
+ parent, attr_name = get_parent_and_attribute_name(key.to_s)
44
+ if parent && attr_name
45
+ hash[key] = [parent, attr_name]
46
+ end
47
+ end
48
+ end
49
+
50
+ def klassify(obj)
51
+ if Class === obj && ::ActiveRecord::Base > obj
52
+ obj
53
+ elsif obj.respond_to? :klass
54
+ obj.klass
55
+ elsif obj.respond_to? :active_record # Rails 3
56
+ obj.active_record
57
+ elsif obj.respond_to? :base_klass # Rails 4
58
+ obj.base_klass
59
+ else
60
+ raise ArgumentError, "Don't know how to klassify #{obj.inspect}"
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,48 @@
1
+ module Ransack
2
+ module Nodes
3
+ class Condition
4
+
5
+ def arel_predicate
6
+ predicates = attributes.map do |attr|
7
+ attr.attr.send(
8
+ arel_predicate_for_attribute(attr),
9
+ formatted_values_for_attribute(attr)
10
+ )
11
+ end
12
+
13
+ if predicates.size > 1
14
+ case combinator
15
+ when Constants::AND
16
+ Arel::Nodes::Grouping.new(Arel::Nodes::And.new(predicates))
17
+ when Constants::OR
18
+ predicates.inject(&:or)
19
+ end
20
+ else
21
+ return_predicate(predicates.first)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ # FIXME: Improve this edge case patch for Arel >= 6.0 (Rails >= 4.2)
28
+ # that adds several conditionals to handle changing Arel API.
29
+ # Related to Ransack issue #472 and pull requests #486-488.
30
+ #
31
+ def return_predicate(predicate)
32
+ if casted_array_with_in_predicate?(predicate)
33
+ predicate.right[0] = predicate.right[0].val
34
+ .map { |v| v.is_a?(String) ? Arel::Nodes.build_quoted(v) : v }
35
+ end
36
+ predicate
37
+ end
38
+ #
39
+ def casted_array_with_in_predicate?(predicate)
40
+ return unless defined?(Arel::Nodes::Casted)
41
+ predicate.class == Arel::Nodes::In &&
42
+ predicate.right.is_a?(Array) &&
43
+ predicate.right[0].class == Arel::Nodes::Casted
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,12 @@
1
+ module Ransack
2
+ module Translate
3
+
4
+ def self.i18n_key(klass)
5
+ if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0
6
+ klass.model_name.i18n_key.to_s.tr('.'.freeze, '/'.freeze)
7
+ else
8
+ klass.model_name.i18n_key.to_s.freeze
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,24 @@
1
+ module Ransack
2
+ class Visitor
3
+ def visit_and(object)
4
+ nodes = object.values.map { |o| accept(o) }.compact
5
+ return nil unless nodes.size > 0
6
+
7
+ if nodes.size > 1
8
+ Arel::Nodes::Grouping.new(Arel::Nodes::And.new(nodes))
9
+ else
10
+ nodes.first
11
+ end
12
+ end
13
+
14
+ def quoted?(object)
15
+ case object
16
+ when Arel::Nodes::SqlLiteral, Bignum, Fixnum
17
+ false
18
+ else
19
+ true
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,13 @@
1
+ require 'ransack/adapters/mongoid/base'
2
+ ::Mongoid::Document.send :include, Ransack::Adapters::Mongoid::Base
3
+
4
+ require 'ransack/adapters/mongoid/attributes/attribute'
5
+ require 'ransack/adapters/mongoid/table'
6
+ require 'ransack/adapters/mongoid/inquiry_hash'
7
+
8
+ case ::Mongoid::VERSION
9
+ when /^3\.2\./
10
+ require 'ransack/adapters/mongoid/3.2/context'
11
+ else
12
+ require 'ransack/adapters/mongoid/context'
13
+ end
@@ -0,0 +1,37 @@
1
+ require 'ransack/adapters/mongoid/attributes/predications'
2
+ require 'ransack/adapters/mongoid/attributes/order_predications'
3
+
4
+ module Ransack
5
+ module Adapters
6
+ module Mongoid
7
+ module Attributes
8
+ class Attribute < Struct.new :relation, :name
9
+ # include Arel::Expressions
10
+ # include Arel::Predications
11
+ # include Arel::AliasPredication
12
+ # include Arel::OrderPredications
13
+ # include Arel::Math
14
+
15
+ include ::Ransack::Adapters::Mongoid::Attributes::Predications
16
+ include ::Ransack::Adapters::Mongoid::Attributes::OrderPredications
17
+
18
+ ###
19
+ # Create a node for lowering this attribute
20
+ def lower
21
+ relation.lower self
22
+ end
23
+ end
24
+
25
+ class String < Attribute; end
26
+ class Time < Attribute; end
27
+ class Boolean < Attribute; end
28
+ class Decimal < Attribute; end
29
+ class Float < Attribute; end
30
+ class Integer < Attribute; end
31
+ class Undefined < Attribute; end
32
+ end
33
+
34
+ Attribute = Attributes::Attribute
35
+ end # Attributes
36
+ end
37
+ end
@@ -0,0 +1,17 @@
1
+ module Ransack
2
+ module Adapters
3
+ module Mongoid
4
+ module Attributes
5
+ module OrderPredications
6
+ def asc
7
+ { name => :asc }
8
+ end
9
+
10
+ def desc
11
+ { name => :desc }
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,141 @@
1
+ module Ransack
2
+ module Adapters
3
+ module Mongoid
4
+ module Attributes
5
+ module Predications
6
+ def not_eq(other)
7
+ { name => { '$ne' => other } }.to_inquiry
8
+ end
9
+
10
+ def not_eq_any(others)
11
+ grouping_any :not_eq, others
12
+ end
13
+
14
+ def not_eq_all(others)
15
+ grouping_all :not_eq, others
16
+ end
17
+
18
+ def eq(other)
19
+ { name => other }.to_inquiry
20
+ end
21
+
22
+ def eq_any(others)
23
+ grouping_any :eq, others
24
+ end
25
+
26
+ def eq_all(others)
27
+ grouping_all :eq, others
28
+ end
29
+
30
+ def in(other)
31
+ { name => { "$in" => other } }.to_inquiry
32
+ end
33
+
34
+ def in_any(others)
35
+ grouping_any :in, others
36
+ end
37
+
38
+ def in_all(others)
39
+ grouping_all :in, others
40
+ end
41
+
42
+ def not_in(other)
43
+ { "$not" => { name => { "$in" => other } } }.to_inquiry
44
+ end
45
+
46
+ def not_in_any(others)
47
+ grouping_any :not_in, others
48
+ end
49
+
50
+ def not_in_all(others)
51
+ grouping_all :not_in, others
52
+ end
53
+
54
+ def matches(other)
55
+ { name => /#{other}/i }.to_inquiry
56
+ end
57
+
58
+ def matches_any(others)
59
+ grouping_any :matches, others
60
+ end
61
+
62
+ def matches_all(others)
63
+ grouping_all :matches, others
64
+ end
65
+
66
+ def does_not_match(other)
67
+ { "$not" => { name => /#{other}/i } }.to_inquiry
68
+ end
69
+
70
+ def does_not_match_any(others)
71
+ grouping_any :does_not_match, others
72
+ end
73
+
74
+ def does_not_match_all(others)
75
+ grouping_all :does_not_match, others
76
+ end
77
+
78
+ def gteq(right)
79
+ { name => { '$gte' => right } }.to_inquiry
80
+ end
81
+
82
+ def gteq_any(others)
83
+ grouping_any :gteq, others
84
+ end
85
+
86
+ def gteq_all(others)
87
+ grouping_all :gteq, others
88
+ end
89
+
90
+ def gt(right)
91
+ { name => { '$gt' => right } }.to_inquiry
92
+ end
93
+
94
+ def gt_any(others)
95
+ grouping_any :gt, others
96
+ end
97
+
98
+ def gt_all(others)
99
+ grouping_all :gt, others
100
+ end
101
+
102
+ def lt(right)
103
+ { name => { '$lt' => right } }.to_inquiry
104
+ end
105
+
106
+ def lt_any(others)
107
+ grouping_any :lt, others
108
+ end
109
+
110
+ def lt_all(others)
111
+ grouping_all :lt, others
112
+ end
113
+
114
+ def lteq(right)
115
+ { name => { '$lte' => right } }.to_inquiry
116
+ end
117
+
118
+ def lteq_any(others)
119
+ grouping_any :lteq, others
120
+ end
121
+
122
+ def lteq_all(others)
123
+ grouping_all :lteq, others
124
+ end
125
+
126
+ private
127
+
128
+ def grouping_any(method_id, others)
129
+ nodes = others.map { |e| send(method_id, e) }
130
+ { "$or" => nodes }.to_inquiry
131
+ end
132
+
133
+ def grouping_all(method_id, others)
134
+ nodes = others.map { |e| send(method_id, e) }
135
+ { "$and" => nodes }.to_inquiry
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,126 @@
1
+ require 'delegate'
2
+
3
+ module Ransack
4
+ module Adapters
5
+ module Mongoid
6
+ module Base
7
+
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ end
12
+
13
+ class ColumnWrapper < SimpleDelegator
14
+ def type
15
+ _super = super
16
+ case _super
17
+ when BSON::ObjectId, Object
18
+ :string
19
+ else
20
+ _super.name.underscore.to_sym
21
+ end
22
+ end
23
+ end
24
+
25
+ class Connection
26
+ def initialize model
27
+ @model = model
28
+ end
29
+
30
+ def quote_column_name name
31
+ name
32
+ end
33
+ end
34
+
35
+ module ClassMethods
36
+ def _ransackers
37
+ @_ransackers ||= {}
38
+ end
39
+
40
+ def _ransackers=(value)
41
+ @_ransackers = value
42
+ end
43
+
44
+ def ransack(params = {}, options = {})
45
+ params = params.presence || {}
46
+ Search.new(self, params ? params.delete_if {
47
+ |k, v| v.blank? && v != false } : params, options)
48
+ end
49
+
50
+ alias_method :search, :ransack
51
+
52
+ def ransacker(name, opts = {}, &block)
53
+ self._ransackers = _ransackers.merge name.to_s => Ransacker
54
+ .new(self, name, opts, &block)
55
+ end
56
+
57
+ def all_ransackable_attributes
58
+ ['id'] + column_names.select { |c| c != '_id' } + _ransackers.keys
59
+ end
60
+
61
+ def ransackable_attributes(auth_object = nil)
62
+ all_ransackable_attributes
63
+ end
64
+
65
+ def ransortable_attributes(auth_object = nil)
66
+ # Here so users can overwrite the attributes
67
+ # that show up in the sort_select
68
+ ransackable_attributes(auth_object)
69
+ end
70
+
71
+ def ransackable_associations(auth_object = nil)
72
+ reflect_on_all_associations_all.map { |a| a.name.to_s }
73
+ end
74
+
75
+ def reflect_on_all_associations_all
76
+ reflect_on_all_associations(:belongs_to, :has_one, :has_many)
77
+ end
78
+
79
+ # For overriding with a whitelist of symbols
80
+ def ransackable_scopes(auth_object = nil)
81
+ []
82
+ end
83
+
84
+ # imitating active record
85
+
86
+ def joins_values *args
87
+ []
88
+ end
89
+
90
+ def first(*args)
91
+ if args.size == 0
92
+ super
93
+ else
94
+ self.criteria.limit(args.first)
95
+ end
96
+ end
97
+
98
+ # def group_by *args, &block
99
+ # criteria
100
+ # end
101
+
102
+ def columns
103
+ @columns ||= fields.map(&:second).map{ |c| ColumnWrapper.new(c) }
104
+ end
105
+
106
+ def column_names
107
+ @column_names ||= fields.map(&:first)
108
+ end
109
+
110
+ def columns_hash
111
+ columns.index_by(&:name)
112
+ end
113
+
114
+ def table
115
+ name = ::Ransack::Adapters::Mongoid::Attributes::Attribute.new(self.criteria, :name)
116
+ {
117
+ :name => name
118
+ }
119
+ end
120
+
121
+ end
122
+
123
+ end # Base
124
+ end
125
+ end
126
+ end