ransack 1.5.1 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
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