ransack_ffcrm 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.travis.yml +9 -0
- data/Gemfile +40 -0
- data/LICENSE +20 -0
- data/README.md +137 -0
- data/Rakefile +19 -0
- data/lib/ransack/adapters/active_record/3.0/compat.rb +166 -0
- data/lib/ransack/adapters/active_record/3.0/context.rb +161 -0
- data/lib/ransack/adapters/active_record/3.1/context.rb +166 -0
- data/lib/ransack/adapters/active_record/base.rb +33 -0
- data/lib/ransack/adapters/active_record/context.rb +41 -0
- data/lib/ransack/adapters/active_record.rb +12 -0
- data/lib/ransack/configuration.rb +35 -0
- data/lib/ransack/constants.rb +23 -0
- data/lib/ransack/context.rb +124 -0
- data/lib/ransack/helpers/form_builder.rb +203 -0
- data/lib/ransack/helpers/form_helper.rb +75 -0
- data/lib/ransack/helpers.rb +2 -0
- data/lib/ransack/locale/en.yml +70 -0
- data/lib/ransack/naming.rb +53 -0
- data/lib/ransack/nodes/attribute.rb +49 -0
- data/lib/ransack/nodes/bindable.rb +30 -0
- data/lib/ransack/nodes/condition.rb +212 -0
- data/lib/ransack/nodes/grouping.rb +183 -0
- data/lib/ransack/nodes/node.rb +34 -0
- data/lib/ransack/nodes/sort.rb +41 -0
- data/lib/ransack/nodes/value.rb +108 -0
- data/lib/ransack/nodes.rb +7 -0
- data/lib/ransack/predicate.rb +70 -0
- data/lib/ransack/ransacker.rb +24 -0
- data/lib/ransack/search.rb +123 -0
- data/lib/ransack/translate.rb +92 -0
- data/lib/ransack/version.rb +3 -0
- data/lib/ransack/visitor.rb +68 -0
- data/lib/ransack.rb +27 -0
- data/ransack_ffcrm.gemspec +30 -0
- data/spec/blueprints/articles.rb +5 -0
- data/spec/blueprints/comments.rb +5 -0
- data/spec/blueprints/notes.rb +3 -0
- data/spec/blueprints/people.rb +4 -0
- data/spec/blueprints/tags.rb +3 -0
- data/spec/console.rb +21 -0
- data/spec/helpers/ransack_helper.rb +2 -0
- data/spec/ransack/adapters/active_record/base_spec.rb +67 -0
- data/spec/ransack/adapters/active_record/context_spec.rb +45 -0
- data/spec/ransack/configuration_spec.rb +31 -0
- data/spec/ransack/helpers/form_builder_spec.rb +137 -0
- data/spec/ransack/helpers/form_helper_spec.rb +38 -0
- data/spec/ransack/nodes/condition_spec.rb +15 -0
- data/spec/ransack/nodes/grouping_spec.rb +13 -0
- data/spec/ransack/predicate_spec.rb +55 -0
- data/spec/ransack/search_spec.rb +225 -0
- data/spec/spec_helper.rb +47 -0
- data/spec/support/en.yml +5 -0
- data/spec/support/schema.rb +111 -0
- metadata +229 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
module Ransack
|
2
|
+
module Nodes
|
3
|
+
class Sort < Node
|
4
|
+
include Bindable
|
5
|
+
|
6
|
+
attr_reader :name, :dir
|
7
|
+
i18n_word :asc, :desc
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def extract(context, str)
|
11
|
+
attr, direction = str.split(/\s+/,2)
|
12
|
+
self.new(context).build(:name => attr, :dir => direction)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def build(params)
|
17
|
+
params.with_indifferent_access.each do |key, value|
|
18
|
+
if key.match(/^(name|dir)$/)
|
19
|
+
self.send("#{key}=", value)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def valid?
|
27
|
+
bound? && attr
|
28
|
+
end
|
29
|
+
|
30
|
+
def name=(name)
|
31
|
+
@name = name
|
32
|
+
context.bind(self, name) unless name.blank?
|
33
|
+
end
|
34
|
+
|
35
|
+
def dir=(dir)
|
36
|
+
@dir = %w(asc desc).include?(dir) ? dir : 'asc'
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Ransack
|
2
|
+
module Nodes
|
3
|
+
class Value < Node
|
4
|
+
attr_accessor :value
|
5
|
+
delegate :present?, :blank?, :to => :value
|
6
|
+
|
7
|
+
def initialize(context, value = nil)
|
8
|
+
super(context)
|
9
|
+
@value = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def persisted?
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
def eql?(other)
|
17
|
+
self.class == other.class &&
|
18
|
+
self.value == other.value
|
19
|
+
end
|
20
|
+
alias :== :eql?
|
21
|
+
|
22
|
+
def hash
|
23
|
+
value.hash
|
24
|
+
end
|
25
|
+
|
26
|
+
def cast(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.is_a?(String)
|
47
|
+
Chronic.parse(val).in_time_zone.to_date rescue nil
|
48
|
+
elsif val.respond_to?(:to_date)
|
49
|
+
val.to_date rescue nil
|
50
|
+
else
|
51
|
+
y, m, d = *[val].flatten
|
52
|
+
m ||= 1
|
53
|
+
d ||= 1
|
54
|
+
Date.new(y,m,d) rescue nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def cast_to_time(val)
|
59
|
+
if val.is_a?(Array)
|
60
|
+
Time.zone.local(*val) rescue nil
|
61
|
+
else
|
62
|
+
unless val.acts_like?(:time)
|
63
|
+
val = val.is_a?(String) ? Chronic.parse(val) : val.to_time rescue nil
|
64
|
+
end
|
65
|
+
val.in_time_zone rescue nil
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def cast_to_boolean(val)
|
70
|
+
if Constants::TRUE_VALUES.include?(val)
|
71
|
+
true
|
72
|
+
elsif Constants::FALSE_VALUES.include?(val)
|
73
|
+
false
|
74
|
+
else
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def cast_to_string(val)
|
80
|
+
val.respond_to?(:to_s) ? val.to_s : String.new(val)
|
81
|
+
end
|
82
|
+
|
83
|
+
def cast_to_integer(val)
|
84
|
+
val.blank? ? nil : val.to_i
|
85
|
+
end
|
86
|
+
|
87
|
+
def cast_to_float(val)
|
88
|
+
val.blank? ? nil : val.to_f
|
89
|
+
end
|
90
|
+
|
91
|
+
def cast_to_decimal(val)
|
92
|
+
if val.blank?
|
93
|
+
nil
|
94
|
+
elsif val.class == BigDecimal
|
95
|
+
val
|
96
|
+
elsif val.respond_to?(:to_d)
|
97
|
+
val.to_d
|
98
|
+
else
|
99
|
+
val.to_s.to_d
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def array_of_arrays?(val)
|
104
|
+
Array === val && Array === val.first
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Ransack
|
2
|
+
class Predicate
|
3
|
+
attr_reader :name, :arel_predicate, :type, :formatter, :validator, :compound, :wants_array
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def names
|
8
|
+
Ransack.predicates.keys
|
9
|
+
end
|
10
|
+
|
11
|
+
def names_by_decreasing_length
|
12
|
+
names.sort {|a,b| b.length <=> a.length}
|
13
|
+
end
|
14
|
+
|
15
|
+
def named(name)
|
16
|
+
Ransack.predicates[name.to_s]
|
17
|
+
end
|
18
|
+
|
19
|
+
def detect_and_strip_from_string!(str)
|
20
|
+
names_by_decreasing_length.detect {|p| str.sub!(/_#{p}$/, '')}
|
21
|
+
end
|
22
|
+
|
23
|
+
def detect_from_string(str)
|
24
|
+
names_by_decreasing_length.detect {|p| str.match(/_#{p}$/)}
|
25
|
+
end
|
26
|
+
|
27
|
+
def name_from_attribute_name(attribute_name)
|
28
|
+
names_by_decreasing_length.detect {|p| attribute_name.to_s.match(/_#{p}$/)}
|
29
|
+
end
|
30
|
+
|
31
|
+
def for_attribute_name(attribute_name)
|
32
|
+
self.named(detect_from_string(attribute_name.to_s))
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(opts = {})
|
38
|
+
@name = opts[:name]
|
39
|
+
@arel_predicate = opts[:arel_predicate]
|
40
|
+
@type = opts[:type]
|
41
|
+
@formatter = opts[:formatter]
|
42
|
+
@validator = opts[:validator] || lambda { |v| v.respond_to?(:empty?) ? !v.empty? : !v.nil? }
|
43
|
+
@compound = opts[:compound]
|
44
|
+
@wants_array = @compound || ['in', 'not_in'].include?(@arel_predicate)
|
45
|
+
end
|
46
|
+
|
47
|
+
def eql?(other)
|
48
|
+
self.class == other.class &&
|
49
|
+
self.name == other.name
|
50
|
+
end
|
51
|
+
alias :== :eql?
|
52
|
+
|
53
|
+
def hash
|
54
|
+
name.hash
|
55
|
+
end
|
56
|
+
|
57
|
+
def format(val)
|
58
|
+
if formatter
|
59
|
+
formatter.call(val)
|
60
|
+
else
|
61
|
+
val
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def validate(vals, type = @type)
|
66
|
+
vals.select {|v| validator.call(type ? v.cast(type) : v.value)}.any?
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,24 @@
|
|
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
|
+
end
|
18
|
+
|
19
|
+
def attr_from(bindable)
|
20
|
+
call(*args.map {|arg| bindable.send(arg)})
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'ransack/nodes'
|
2
|
+
require 'ransack/context'
|
3
|
+
require 'ransack/naming'
|
4
|
+
|
5
|
+
module Ransack
|
6
|
+
class Search
|
7
|
+
include Naming
|
8
|
+
|
9
|
+
attr_reader :base, :context
|
10
|
+
|
11
|
+
delegate :object, :klass, :to => :context
|
12
|
+
delegate :new_grouping, :new_condition,
|
13
|
+
:build_grouping, :build_condition,
|
14
|
+
:translate, :to => :base
|
15
|
+
|
16
|
+
def initialize(object, params = {}, options = {})
|
17
|
+
params ||= {}
|
18
|
+
@context = Context.for(object, options)
|
19
|
+
@context.auth_object = options[:auth_object]
|
20
|
+
@base = Nodes::Grouping.new(@context, 'and')
|
21
|
+
build(params.with_indifferent_access)
|
22
|
+
end
|
23
|
+
|
24
|
+
def result(opts = {})
|
25
|
+
@context.evaluate(self, opts)
|
26
|
+
end
|
27
|
+
|
28
|
+
def inspect
|
29
|
+
# Replace the inspect method on object's singleton class to call to_s() instead of to_a().
|
30
|
+
object.class_eval { alias :inspect :to_s }
|
31
|
+
super
|
32
|
+
end
|
33
|
+
|
34
|
+
def build(params)
|
35
|
+
collapse_multiparameter_attributes!(params).each do |key, value|
|
36
|
+
case key
|
37
|
+
when 's', 'sorts'
|
38
|
+
send("#{key}=", value)
|
39
|
+
else
|
40
|
+
base.send("#{key}=", value) if base.attribute_method?(key)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
def sorts=(args)
|
47
|
+
case args
|
48
|
+
when Array
|
49
|
+
args.each do |sort|
|
50
|
+
sort = Nodes::Sort.extract(@context, sort)
|
51
|
+
self.sorts << sort
|
52
|
+
end
|
53
|
+
when Hash
|
54
|
+
args.each do |index, attrs|
|
55
|
+
sort = Nodes::Sort.new(@context).build(attrs)
|
56
|
+
self.sorts << sort
|
57
|
+
end
|
58
|
+
when String
|
59
|
+
self.sorts = [args]
|
60
|
+
else
|
61
|
+
raise ArgumentError, "Invalid argument (#{args.class}) supplied to sorts="
|
62
|
+
end
|
63
|
+
end
|
64
|
+
alias :s= :sorts=
|
65
|
+
|
66
|
+
def sorts
|
67
|
+
@sorts ||= []
|
68
|
+
end
|
69
|
+
alias :s :sorts
|
70
|
+
|
71
|
+
def build_sort(opts = {})
|
72
|
+
new_sort(opts).tap do |sort|
|
73
|
+
self.sorts << sort
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def new_sort(opts = {})
|
78
|
+
Nodes::Sort.new(@context).build(opts)
|
79
|
+
end
|
80
|
+
|
81
|
+
def respond_to?(method_id)
|
82
|
+
super or begin
|
83
|
+
method_name = method_id.to_s
|
84
|
+
writer = method_name.sub!(/\=$/, '')
|
85
|
+
base.attribute_method?(method_name) ? true : false
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def method_missing(method_id, *args)
|
90
|
+
method_name = method_id.to_s
|
91
|
+
writer = method_name.sub!(/\=$/, '')
|
92
|
+
if base.attribute_method?(method_name)
|
93
|
+
base.send(method_id, *args)
|
94
|
+
else
|
95
|
+
super
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def collapse_multiparameter_attributes!(attrs)
|
102
|
+
attrs.keys.each do |k|
|
103
|
+
if k.include?("(")
|
104
|
+
real_attribute, position = k.split(/\(|\)/)
|
105
|
+
cast = %w(a s i).include?(position.last) ? position.last : nil
|
106
|
+
position = position.to_i - 1
|
107
|
+
value = attrs.delete(k)
|
108
|
+
attrs[real_attribute] ||= []
|
109
|
+
attrs[real_attribute][position] = if cast
|
110
|
+
(value.blank? && cast == 'i') ? nil : value.send("to_#{cast}")
|
111
|
+
else
|
112
|
+
value
|
113
|
+
end
|
114
|
+
elsif Hash === attrs[k]
|
115
|
+
collapse_multiparameter_attributes!(attrs[k])
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
attrs
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
I18n.load_path += Dir[File.join(File.dirname(__FILE__), 'locale', '*.yml')]
|
2
|
+
|
3
|
+
module Ransack
|
4
|
+
module Translate
|
5
|
+
def self.word(key, options = {})
|
6
|
+
I18n.translate(:"ransack.#{key}", :default => key.to_s)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.predicate(key, options = {})
|
10
|
+
I18n.translate(:"ransack.predicates.#{key}", :default => key.to_s)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.attribute(key, options = {})
|
14
|
+
unless context = options.delete(:context)
|
15
|
+
raise ArgumentError, "A context is required to translate attributes"
|
16
|
+
end
|
17
|
+
|
18
|
+
original_name = key.to_s
|
19
|
+
base_class = context.klass
|
20
|
+
base_ancestors = base_class.ancestors.select { |x| x.respond_to?(:model_name) }
|
21
|
+
predicate = Predicate.detect_from_string(original_name)
|
22
|
+
attributes_str = original_name.sub(/_#{predicate}$/, '')
|
23
|
+
attribute_names = attributes_str.split(/_and_|_or_/)
|
24
|
+
combinator = attributes_str.match(/_and_/) ? :and : :or
|
25
|
+
defaults = base_ancestors.map do |klass|
|
26
|
+
:"ransack.attributes.#{klass.model_name.underscore}.#{original_name}"
|
27
|
+
end
|
28
|
+
|
29
|
+
translated_names = attribute_names.map do |attr|
|
30
|
+
attribute_name(context, attr, options[:include_associations])
|
31
|
+
end
|
32
|
+
|
33
|
+
interpolations = {}
|
34
|
+
interpolations[:attributes] = translated_names.join(" #{Translate.word(combinator)} ")
|
35
|
+
|
36
|
+
if predicate
|
37
|
+
defaults << "%{attributes} %{predicate}"
|
38
|
+
interpolations[:predicate] = Translate.predicate(predicate)
|
39
|
+
else
|
40
|
+
defaults << "%{attributes}"
|
41
|
+
end
|
42
|
+
|
43
|
+
defaults << options.delete(:default) if options[:default]
|
44
|
+
options.reverse_merge! :count => 1, :default => defaults
|
45
|
+
I18n.translate(defaults.shift, options.merge(interpolations))
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.association(key, options = {})
|
49
|
+
unless context = options.delete(:context)
|
50
|
+
raise ArgumentError, "A context is required to translate associations"
|
51
|
+
end
|
52
|
+
|
53
|
+
defaults = key.blank? ? [:"#{context.klass.i18n_scope}.models.#{context.klass.model_name.underscore}"] : [:"ransack.associations.#{context.klass.model_name.underscore}.#{key}"]
|
54
|
+
defaults << context.traverse(key).model_name.human
|
55
|
+
options = {:count => 1, :default => defaults}
|
56
|
+
I18n.translate(defaults.shift, options)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def self.attribute_name(context, name, include_associations = nil)
|
62
|
+
assoc_path = context.association_path(name)
|
63
|
+
associated_class = context.traverse(assoc_path) if assoc_path.present?
|
64
|
+
attr_name = name.sub(/^#{assoc_path}_/, '')
|
65
|
+
interpolations = {}
|
66
|
+
interpolations[:attr_fallback_name] = I18n.translate(
|
67
|
+
(associated_class ?
|
68
|
+
:"ransack.attributes.#{associated_class.model_name.underscore}.#{attr_name}" :
|
69
|
+
:"ransack.attributes.#{context.klass.model_name.underscore}.#{attr_name}"
|
70
|
+
),
|
71
|
+
:default => [
|
72
|
+
(associated_class ?
|
73
|
+
:"#{associated_class.i18n_scope}.attributes.#{associated_class.model_name.underscore}.#{attr_name}" :
|
74
|
+
:"#{context.klass.i18n_scope}.attributes.#{context.klass.model_name.underscore}.#{attr_name}"
|
75
|
+
),
|
76
|
+
attr_name.humanize
|
77
|
+
]
|
78
|
+
)
|
79
|
+
defaults = [
|
80
|
+
:"ransack.attributes.#{context.klass.model_name.underscore}.#{name}"
|
81
|
+
]
|
82
|
+
if include_associations && associated_class
|
83
|
+
defaults << '%{association_name} %{attr_fallback_name}'
|
84
|
+
interpolations[:association_name] = association(assoc_path, :context => context)
|
85
|
+
else
|
86
|
+
defaults << '%{attr_fallback_name}'
|
87
|
+
end
|
88
|
+
options = {:count => 1, :default => defaults}
|
89
|
+
I18n.translate(defaults.shift, options.merge(interpolations))
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,68 @@
|
|
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_Grouping(object)
|
21
|
+
object.combinator == 'or' ? visit_or(object) : visit_and(object)
|
22
|
+
end
|
23
|
+
|
24
|
+
def visit_and(object)
|
25
|
+
nodes = object.values.map {|o| accept(o)}.compact
|
26
|
+
return nil unless nodes.size > 0
|
27
|
+
|
28
|
+
if nodes.size > 1
|
29
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::And.new(nodes))
|
30
|
+
else
|
31
|
+
nodes.first
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def visit_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 visit_Ransack_Nodes_Sort(object)
|
47
|
+
object.attr.send(object.dir) if object.valid?
|
48
|
+
end
|
49
|
+
|
50
|
+
def quoted?(object)
|
51
|
+
case object
|
52
|
+
when Arel::Nodes::SqlLiteral, Bignum, Fixnum
|
53
|
+
false
|
54
|
+
else
|
55
|
+
true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def visit(object)
|
60
|
+
send(DISPATCH[object.class], object)
|
61
|
+
end
|
62
|
+
|
63
|
+
DISPATCH = Hash.new do |hash, klass|
|
64
|
+
hash[klass] = "visit_#{klass.name.gsub('::', '_')}"
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
data/lib/ransack.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'ransack/configuration'
|
2
|
+
|
3
|
+
module Ransack
|
4
|
+
extend Configuration
|
5
|
+
|
6
|
+
class UntraversableAssociationError < StandardError; end;
|
7
|
+
end
|
8
|
+
|
9
|
+
Ransack.configure do |config|
|
10
|
+
Ransack::Constants::AREL_PREDICATES.each do |name|
|
11
|
+
config.add_predicate name, :arel_predicate => name
|
12
|
+
end
|
13
|
+
|
14
|
+
Ransack::Constants::DERIVED_PREDICATES.each do |args|
|
15
|
+
config.add_predicate *args
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'ransack/translate'
|
20
|
+
require 'ransack/search'
|
21
|
+
require 'ransack/ransacker'
|
22
|
+
require 'ransack/adapters/active_record'
|
23
|
+
require 'ransack/helpers'
|
24
|
+
require 'action_controller'
|
25
|
+
require 'chronic'
|
26
|
+
|
27
|
+
ActionController::Base.helper Ransack::Helpers::FormHelper
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "ransack/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "ransack_ffcrm"
|
7
|
+
s.version = Ransack::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Ernie Miller"]
|
10
|
+
s.email = ["ernie@metautonomo.us"]
|
11
|
+
s.homepage = "http://metautonomo.us/projects/ransack"
|
12
|
+
s.summary = %q{Object-based searching for ActiveRecord (currently).}
|
13
|
+
s.description = %q{Ransack is the successor to the MetaSearch gem. It improves and expands upon MetaSearch's functionality, but does not have a 100%-compatible API.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "ransack"
|
16
|
+
|
17
|
+
s.add_dependency 'activerecord', '~> 3.0'
|
18
|
+
s.add_dependency 'actionpack', '~> 3.0'
|
19
|
+
s.add_dependency 'polyamorous', '~> 0.5.0'
|
20
|
+
s.add_dependency 'chronic', '~> 0.6.7'
|
21
|
+
s.add_development_dependency 'rspec', '~> 2.8.0'
|
22
|
+
s.add_development_dependency 'machinist', '~> 1.0.6'
|
23
|
+
s.add_development_dependency 'faker', '~> 0.9.5'
|
24
|
+
s.add_development_dependency 'sqlite3', '~> 1.3.3'
|
25
|
+
|
26
|
+
s.files = `git ls-files`.split("\n")
|
27
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
28
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
29
|
+
s.require_paths = ["lib"]
|
30
|
+
end
|
data/spec/console.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Bundler.setup
|
2
|
+
require 'machinist/active_record'
|
3
|
+
require 'sham'
|
4
|
+
require 'faker'
|
5
|
+
require 'ransack'
|
6
|
+
|
7
|
+
Dir[File.expand_path('../../spec/{helpers,support,blueprints}/*.rb', __FILE__)].each do |f|
|
8
|
+
require f
|
9
|
+
end
|
10
|
+
|
11
|
+
Sham.define do
|
12
|
+
name { Faker::Name.name }
|
13
|
+
title { Faker::Lorem.sentence }
|
14
|
+
body { Faker::Lorem.paragraph }
|
15
|
+
salary {|index| 30000 + (index * 1000)}
|
16
|
+
tag_name { Faker::Lorem.words(3).join(' ') }
|
17
|
+
note { Faker::Lorem.words(7).join(' ') }
|
18
|
+
end
|
19
|
+
|
20
|
+
Schema.create
|
21
|
+
|