ransack 0.1.0 → 0.2.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.
- data/lib/ransack.rb +1 -1
- data/lib/ransack/adapters/active_record.rb +18 -2
- data/lib/ransack/adapters/active_record/3.0/base.rb +34 -0
- data/lib/ransack/adapters/active_record/3.0/compat.rb +23 -0
- data/lib/ransack/adapters/active_record/3.0/context.rb +168 -0
- data/lib/ransack/adapters/active_record/3.0/join_association.rb +44 -0
- data/lib/ransack/adapters/active_record/3.0/join_dependency.rb +63 -0
- data/lib/ransack/adapters/active_record/base.rb +19 -2
- data/lib/ransack/adapters/active_record/context.rb +45 -33
- data/lib/ransack/adapters/active_record/join_association.rb +44 -0
- data/lib/ransack/adapters/active_record/join_dependency.rb +63 -0
- data/lib/ransack/context.rb +25 -65
- data/lib/ransack/helpers/form_builder.rb +10 -4
- data/lib/ransack/helpers/form_helper.rb +1 -0
- data/lib/ransack/locale/en.yml +1 -0
- data/lib/ransack/nodes.rb +1 -0
- data/lib/ransack/nodes/attribute.rb +21 -4
- data/lib/ransack/nodes/bindable.rb +29 -0
- data/lib/ransack/nodes/condition.rb +30 -28
- data/lib/ransack/nodes/sort.rb +5 -3
- data/lib/ransack/nodes/value.rb +84 -100
- data/lib/ransack/predicate.rb +1 -11
- data/lib/ransack/ransacker.rb +26 -0
- data/lib/ransack/search.rb +3 -2
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack/visitor.rb +64 -0
- data/ransack.gemspec +3 -3
- data/spec/console.rb +1 -2
- data/spec/ransack/adapters/active_record/base_spec.rb +18 -0
- data/spec/ransack/adapters/active_record/context_spec.rb +2 -2
- data/spec/ransack/helpers/form_builder_spec.rb +4 -0
- data/spec/ransack/search_spec.rb +25 -2
- data/spec/spec_helper.rb +2 -3
- data/spec/support/schema.rb +8 -0
- metadata +16 -8
data/lib/ransack/nodes/sort.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
module Ransack
|
2
2
|
module Nodes
|
3
3
|
class Sort < Node
|
4
|
-
|
4
|
+
include Bindable
|
5
|
+
|
6
|
+
attr_reader :name, :dir
|
5
7
|
i18n_word :asc, :desc
|
6
8
|
|
7
9
|
class << self
|
@@ -22,12 +24,12 @@ module Ransack
|
|
22
24
|
end
|
23
25
|
|
24
26
|
def valid?
|
25
|
-
|
27
|
+
bound? && attr
|
26
28
|
end
|
27
29
|
|
28
30
|
def name=(name)
|
29
31
|
@name = name
|
30
|
-
|
32
|
+
context.bind(self, name) unless name.blank?
|
31
33
|
end
|
32
34
|
|
33
35
|
def dir=(dir)
|
data/lib/ransack/nodes/value.rb
CHANGED
@@ -1,22 +1,12 @@
|
|
1
1
|
module Ransack
|
2
2
|
module Nodes
|
3
3
|
class Value < Node
|
4
|
-
|
5
|
-
delegate :blank?, :to => :
|
4
|
+
attr_accessor :value
|
5
|
+
delegate :blank?, :present?, :to => :value
|
6
6
|
|
7
|
-
def initialize(context, value = nil
|
7
|
+
def initialize(context, value = nil)
|
8
8
|
super(context)
|
9
|
-
@
|
10
|
-
self.type = type if type
|
11
|
-
end
|
12
|
-
|
13
|
-
def value=(val)
|
14
|
-
@value_before_cast = value
|
15
|
-
@value = nil
|
16
|
-
end
|
17
|
-
|
18
|
-
def value
|
19
|
-
@value ||= cast_to_type(@value_before_cast, @type)
|
9
|
+
@value = value
|
20
10
|
end
|
21
11
|
|
22
12
|
def persisted?
|
@@ -25,96 +15,90 @@ module Ransack
|
|
25
15
|
|
26
16
|
def eql?(other)
|
27
17
|
self.class == other.class &&
|
28
|
-
self.
|
18
|
+
self.value == other.value
|
29
19
|
end
|
30
20
|
alias :== :eql?
|
31
21
|
|
32
22
|
def hash
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
def
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def array_of_arrays?(val)
|
116
|
-
Array === val && Array === val.first
|
117
|
-
end
|
23
|
+
value.hash
|
24
|
+
end
|
25
|
+
|
26
|
+
def cast_to_type(type)
|
27
|
+
case type
|
28
|
+
when :date
|
29
|
+
cast_to_date(value)
|
30
|
+
when :datetime, :timestamp, :time
|
31
|
+
cast_to_time(value)
|
32
|
+
when :boolean
|
33
|
+
cast_to_boolean(value)
|
34
|
+
when :integer
|
35
|
+
cast_to_integer(value)
|
36
|
+
when :float
|
37
|
+
cast_to_float(value)
|
38
|
+
when :decimal
|
39
|
+
cast_to_decimal(value)
|
40
|
+
else
|
41
|
+
cast_to_string(value)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def cast_to_date(val)
|
46
|
+
if val.respond_to?(:to_date)
|
47
|
+
val.to_date rescue nil
|
48
|
+
else
|
49
|
+
y, m, d = *[val].flatten
|
50
|
+
m ||= 1
|
51
|
+
d ||= 1
|
52
|
+
Date.new(y,m,d) rescue nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def cast_to_time(val)
|
57
|
+
if val.is_a?(Array)
|
58
|
+
Time.zone.local(*val) rescue nil
|
59
|
+
else
|
60
|
+
unless val.acts_like?(:time)
|
61
|
+
val = val.is_a?(String) ? Time.zone.parse(val) : val.to_time rescue val
|
62
|
+
end
|
63
|
+
val.in_time_zone rescue nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def cast_to_boolean(val)
|
68
|
+
if val.is_a?(String) && val.blank?
|
69
|
+
nil
|
70
|
+
else
|
71
|
+
Constants::TRUE_VALUES.include?(val)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def cast_to_string(val)
|
76
|
+
val.respond_to?(:to_s) ? val.to_s : String.new(val)
|
77
|
+
end
|
78
|
+
|
79
|
+
def cast_to_integer(val)
|
80
|
+
val.blank? ? nil : val.to_i
|
81
|
+
end
|
82
|
+
|
83
|
+
def cast_to_float(val)
|
84
|
+
val.blank? ? nil : val.to_f
|
85
|
+
end
|
86
|
+
|
87
|
+
def cast_to_decimal(val)
|
88
|
+
if val.blank?
|
89
|
+
nil
|
90
|
+
elsif val.class == BigDecimal
|
91
|
+
val
|
92
|
+
elsif val.respond_to?(:to_d)
|
93
|
+
val.to_d
|
94
|
+
else
|
95
|
+
val.to_s.to_d
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def array_of_arrays?(val)
|
100
|
+
Array === val && Array === val.first
|
101
|
+
end
|
118
102
|
end
|
119
103
|
end
|
120
104
|
end
|
data/lib/ransack/predicate.rb
CHANGED
@@ -25,16 +25,6 @@ module Ransack
|
|
25
25
|
@compound = opts[:compound]
|
26
26
|
end
|
27
27
|
|
28
|
-
def format(vals)
|
29
|
-
if formatter
|
30
|
-
vals.select {|v| validator ? validator.call(v.value_before_cast) : !v.blank?}.
|
31
|
-
map {|v| formatter.call(v.value)}
|
32
|
-
else
|
33
|
-
vals.select {|v| validator ? validator.call(v.value_before_cast) : !v.blank?}.
|
34
|
-
map {|v| v.value}
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
28
|
def eql?(other)
|
39
29
|
self.class == other.class &&
|
40
30
|
self.name == other.name
|
@@ -47,7 +37,7 @@ module Ransack
|
|
47
37
|
|
48
38
|
def validate(vals)
|
49
39
|
if validator
|
50
|
-
vals.select {|v| validator.call(v.
|
40
|
+
vals.select {|v| validator.call(v.value)}.any?
|
51
41
|
else
|
52
42
|
vals.select {|v| !v.blank?}.any?
|
53
43
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Ransack
|
2
|
+
class Ransacker
|
3
|
+
|
4
|
+
attr_reader :name, :type, :formatter, :args
|
5
|
+
|
6
|
+
delegate :call, :to => :@callable
|
7
|
+
|
8
|
+
def initialize(klass, name, opts = {}, &block)
|
9
|
+
@klass, @name = klass, name
|
10
|
+
|
11
|
+
@type = opts[:type] || :string
|
12
|
+
@args = opts[:args] || [:parent]
|
13
|
+
@formatter = opts[:formatter]
|
14
|
+
@callable = opts[:callable] || block ||
|
15
|
+
(@klass.method(name) if @klass.respond_to?(name)) ||
|
16
|
+
proc {|parent| parent.table[name]}
|
17
|
+
|
18
|
+
@klass._ransackers[name.to_s] = self
|
19
|
+
end
|
20
|
+
|
21
|
+
def attr_from(bindable)
|
22
|
+
call(*args.map {|arg| bindable.send(arg)})
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/ransack/search.rb
CHANGED
@@ -13,15 +13,16 @@ module Ransack
|
|
13
13
|
:build_and, :build_or, :build_condition,
|
14
14
|
:translate, :to => :base
|
15
15
|
|
16
|
-
def initialize(object, params = {})
|
16
|
+
def initialize(object, params = {}, options = {})
|
17
17
|
params ||= {}
|
18
18
|
@context = Context.for(object)
|
19
|
+
@context.auth_object = options[:auth_object]
|
19
20
|
@base = Nodes::And.new(@context)
|
20
21
|
build(params.with_indifferent_access)
|
21
22
|
end
|
22
23
|
|
23
24
|
def result(opts = {})
|
24
|
-
@
|
25
|
+
@context.evaluate(self, opts)
|
25
26
|
end
|
26
27
|
|
27
28
|
def build(params)
|
data/lib/ransack/version.rb
CHANGED
@@ -0,0 +1,64 @@
|
|
1
|
+
module Ransack
|
2
|
+
class Visitor
|
3
|
+
|
4
|
+
def accept(object)
|
5
|
+
visit(object)
|
6
|
+
end
|
7
|
+
|
8
|
+
def can_accept?(object)
|
9
|
+
respond_to? DISPATCH[object.class]
|
10
|
+
end
|
11
|
+
|
12
|
+
def visit_Array(object)
|
13
|
+
object.map {|o| accept(o)}.compact
|
14
|
+
end
|
15
|
+
|
16
|
+
def visit_Ransack_Nodes_Condition(object)
|
17
|
+
object.arel_predicate if object.valid?
|
18
|
+
end
|
19
|
+
|
20
|
+
def visit_Ransack_Nodes_And(object)
|
21
|
+
nodes = object.values.map {|o| accept(o)}.compact
|
22
|
+
return nil unless nodes.size > 0
|
23
|
+
|
24
|
+
if nodes.size > 1
|
25
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::And.new(nodes))
|
26
|
+
else
|
27
|
+
nodes.first
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def visit_Ransack_Nodes_Sort(object)
|
32
|
+
object.attr.send(object.dir) if object.valid?
|
33
|
+
end
|
34
|
+
|
35
|
+
def visit_Ransack_Nodes_Or(object)
|
36
|
+
nodes = object.values.map {|o| accept(o)}.compact
|
37
|
+
return nil unless nodes.size > 0
|
38
|
+
|
39
|
+
if nodes.size > 1
|
40
|
+
nodes.inject(&:or)
|
41
|
+
else
|
42
|
+
nodes.first
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def quoted?(object)
|
47
|
+
case object
|
48
|
+
when Arel::Nodes::SqlLiteral, Bignum, Fixnum
|
49
|
+
false
|
50
|
+
else
|
51
|
+
true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def visit(object)
|
56
|
+
send(DISPATCH[object.class], object)
|
57
|
+
end
|
58
|
+
|
59
|
+
DISPATCH = Hash.new do |hash, klass|
|
60
|
+
hash[klass] = "visit_#{klass.name.gsub('::', '_')}"
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
data/ransack.gemspec
CHANGED
@@ -14,9 +14,9 @@ Gem::Specification.new do |s|
|
|
14
14
|
|
15
15
|
s.rubyforge_project = "ransack"
|
16
16
|
|
17
|
-
s.add_dependency 'activerecord', '~> 3.
|
18
|
-
s.add_dependency 'activesupport', '~> 3.
|
19
|
-
s.add_dependency 'actionpack', '~> 3.
|
17
|
+
s.add_dependency 'activerecord', '~> 3.0'
|
18
|
+
s.add_dependency 'activesupport', '~> 3.0'
|
19
|
+
s.add_dependency 'actionpack', '~> 3.0'
|
20
20
|
s.add_development_dependency 'rspec', '~> 2.5.0'
|
21
21
|
s.add_development_dependency 'machinist', '~> 1.0.6'
|
22
22
|
s.add_development_dependency 'faker', '~> 0.9.5'
|
data/spec/console.rb
CHANGED
@@ -2,6 +2,7 @@ Bundler.setup
|
|
2
2
|
require 'machinist/active_record'
|
3
3
|
require 'sham'
|
4
4
|
require 'faker'
|
5
|
+
require 'ransack'
|
5
6
|
|
6
7
|
Dir[File.expand_path('../../spec/{helpers,support,blueprints}/*.rb', __FILE__)].each do |f|
|
7
8
|
require f
|
@@ -18,5 +19,3 @@ end
|
|
18
19
|
|
19
20
|
Schema.create
|
20
21
|
|
21
|
-
require 'ransack'
|
22
|
-
|
@@ -24,6 +24,24 @@ module Ransack
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
describe '#ransacker' do
|
28
|
+
it 'creates ransack attributes' do
|
29
|
+
s = Person.search(:reversed_name_eq => 'htimS cirA')
|
30
|
+
s.result.should have(1).person
|
31
|
+
s.result.first.should eq Person.find_by_name('Aric Smith')
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'allows access of attributes through associations' do
|
35
|
+
s = Person.search(:children_reversed_name_eq => 'htimS cirA')
|
36
|
+
s.result.to_sql.should match /"children_people"."name" = 'Aric Smith'/
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'allows an "attribute" to be an InfixOperation' do
|
40
|
+
s = Person.search(:doubled_name_eq => 'Aric SmithAric Smith')
|
41
|
+
s.result.first.should eq Person.find_by_name('Aric Smith')
|
42
|
+
end if defined?(Arel::Nodes::InfixOperation)
|
43
|
+
end
|
44
|
+
|
27
45
|
end
|
28
46
|
end
|
29
47
|
end
|
@@ -11,14 +11,14 @@ module Ransack
|
|
11
11
|
it 'contextualizes strings to attributes' do
|
12
12
|
attribute = @c.contextualize 'children_children_parent_name'
|
13
13
|
attribute.should be_a Arel::Attributes::Attribute
|
14
|
-
attribute.name.should eq 'name'
|
14
|
+
attribute.name.to_s.should eq 'name'
|
15
15
|
attribute.relation.table_alias.should eq 'parents_people'
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'builds new associations if not yet built' do
|
19
19
|
attribute = @c.contextualize 'children_articles_title'
|
20
20
|
attribute.should be_a Arel::Attributes::Attribute
|
21
|
-
attribute.name.should eq 'title'
|
21
|
+
attribute.name.to_s.should eq 'title'
|
22
22
|
attribute.relation.name.should eq 'articles'
|
23
23
|
attribute.relation.table_alias.should be_nil
|
24
24
|
end
|