ransack_ffcrm 0.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.
- 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
|
+
|