facet_for 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/README.textile +8 -0
- data/lib/facet_for/facet_helper.rb +16 -0
- data/lib/facet_for/form_builder.rb +52 -0
- data/lib/facet_for/version.rb +1 -1
- data/lib/facet_for.rb +215 -118
- metadata +7 -4
data/.gitignore
CHANGED
data/README.textile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module FacetHelper
|
2
|
+
def render_facets(object, view)
|
3
|
+
facet_html = ''
|
4
|
+
|
5
|
+
view.meta_select.meta_select_facets.each do |facet|
|
6
|
+
facet_html << FacetFor.create_facet(:object => object,
|
7
|
+
:object_name => 'q',
|
8
|
+
:type => facet.predicate.to_sym,
|
9
|
+
:column_name => facet.column_name,
|
10
|
+
:params => params)
|
11
|
+
end
|
12
|
+
facet_html.html_safe
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
ActionView::Base.send :include, FacetHelper
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class ActionView::Helpers::FormBuilder
|
2
|
+
include ActionView::Helpers::TagHelper
|
3
|
+
include ActionView::Helpers::FormTagHelper
|
4
|
+
include ActionView::Helpers::FormOptionsHelper
|
5
|
+
include ActionView::Helpers::CaptureHelper
|
6
|
+
include ActionView::Helpers::AssetTagHelper
|
7
|
+
|
8
|
+
def facet_for(column, *args)
|
9
|
+
|
10
|
+
options = args.extract_options!
|
11
|
+
|
12
|
+
facet_html = ''
|
13
|
+
|
14
|
+
# Here, we collect information about the facet. What the user doesn't
|
15
|
+
# supply, we'll make our best guess for.
|
16
|
+
|
17
|
+
facet = { }
|
18
|
+
facet[:object] = @object
|
19
|
+
facet[:object_name] = @object_name
|
20
|
+
# Information about the column. This is used to generate the default
|
21
|
+
# label, and to pass the right selector to the Ransack fields.
|
22
|
+
|
23
|
+
facet[:column_name] = column
|
24
|
+
facet[:column_type] = options[:column_type]
|
25
|
+
|
26
|
+
# This is the type of field we will render. If this isn't provided, we'll
|
27
|
+
# determine this based on facet_column_type
|
28
|
+
|
29
|
+
facet[:type] = options[:type]
|
30
|
+
|
31
|
+
# Did we pass in the params? Send it down to determine if we have a value.
|
32
|
+
|
33
|
+
facet[:params] = options[:params]
|
34
|
+
|
35
|
+
# In the specific case of facet_type == :collection, we'll allow the user
|
36
|
+
# to specify their own collection. If it's an association, we'll attempt
|
37
|
+
# to provide one.
|
38
|
+
|
39
|
+
facet[:collection] = options[:collection]
|
40
|
+
|
41
|
+
# Are we labeling it differently?
|
42
|
+
facet[:label] = options[:label]
|
43
|
+
|
44
|
+
@facet = FacetFor::Facet.new(facet)
|
45
|
+
|
46
|
+
facet_html << @facet.render_facet
|
47
|
+
|
48
|
+
facet_html.html_safe
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
end
|
data/lib/facet_for/version.rb
CHANGED
data/lib/facet_for.rb
CHANGED
@@ -1,176 +1,273 @@
|
|
1
1
|
require "facet_for/version"
|
2
|
+
require "facet_for/form_builder"
|
3
|
+
require "facet_for/facet_helper"
|
4
|
+
|
5
|
+
module FacetFor
|
6
|
+
|
7
|
+
PREDICATES = [
|
8
|
+
['Contains', :cont],
|
9
|
+
['Doesn\'t Contain', :not_cont],
|
10
|
+
['Starts With', :start],
|
11
|
+
['Doesn\'t Start With', :not_start],
|
12
|
+
['Ends With', :end],
|
13
|
+
['Doesn\'t End With', :not_end],
|
14
|
+
['Between', :between],
|
15
|
+
['Is Null', :null],
|
16
|
+
['Is Not Null', :not_null],
|
17
|
+
['Collection', :collection]
|
18
|
+
]
|
19
|
+
|
20
|
+
def self.predicates
|
21
|
+
PREDICATES
|
22
|
+
end
|
2
23
|
|
3
|
-
|
4
|
-
include ActionView::Helpers::TagHelper
|
5
|
-
include ActionView::Helpers::FormTagHelper
|
6
|
-
include ActionView::Helpers::FormOptionsHelper
|
7
|
-
include ActionView::Helpers::CaptureHelper
|
8
|
-
include ActionView::Helpers::AssetTagHelper
|
24
|
+
# We can use reflections to determine has_many and belongs_to associations
|
9
25
|
|
10
|
-
def
|
26
|
+
def self.create_facet(*args)
|
11
27
|
options = args.extract_options!
|
12
28
|
|
13
|
-
|
29
|
+
@facet = Facet.new(options)
|
30
|
+
@facet.render_facet.html_safe
|
31
|
+
end
|
32
|
+
|
14
33
|
|
15
|
-
|
16
|
-
|
34
|
+
def self.column_options(q)
|
35
|
+
column_options = []
|
36
|
+
column_options.push([q.klass.to_s, options_for_model(q.klass)])
|
17
37
|
|
18
|
-
|
19
|
-
|
38
|
+
q.context.searchable_associations.each do |association|
|
39
|
+
association_model = association.camelcase.singularize.constantize
|
40
|
+
column_options.push([association_model.to_s, options_for_model(association_model, association)])
|
41
|
+
end
|
20
42
|
|
21
|
-
|
43
|
+
column_options
|
44
|
+
end
|
22
45
|
|
23
|
-
|
24
|
-
|
46
|
+
def self.field_options(q)
|
47
|
+
field_options = options_for_model(q.klass) | q.context.searchable_associations.map { |a| [a.titleize, a.to_sym] }
|
25
48
|
|
26
|
-
|
27
|
-
|
49
|
+
field_options
|
50
|
+
end
|
28
51
|
|
29
|
-
|
30
|
-
|
52
|
+
def self.options_for_model(model, association = nil, grouped = false)
|
53
|
+
options = []
|
54
|
+
preface = ''
|
31
55
|
|
32
|
-
|
56
|
+
if association # preface field names for cross model displaying
|
57
|
+
preface = "#{association}."
|
58
|
+
end
|
33
59
|
|
34
|
-
|
35
|
-
|
36
|
-
|
60
|
+
if model.ransackable_attributes
|
61
|
+
options = model.ransackable_attributes.map { |a| [a.titleize, "#{preface}#{a}".to_sym] }
|
62
|
+
end
|
37
63
|
|
38
|
-
|
64
|
+
options
|
65
|
+
end
|
39
66
|
|
40
|
-
|
67
|
+
class Facet
|
68
|
+
include ActionView::Helpers::TagHelper
|
69
|
+
include ActionView::Helpers::FormTagHelper
|
70
|
+
include ActionView::Helpers::FormOptionsHelper
|
71
|
+
include ActionView::Helpers::CaptureHelper
|
72
|
+
include ActionView::Helpers::AssetTagHelper
|
41
73
|
|
42
|
-
|
43
|
-
#
|
44
|
-
# We check for :singular_name, :plural_name and :singular_id
|
74
|
+
attr_accessor :facet
|
45
75
|
|
46
|
-
|
47
|
-
|
48
|
-
x.plural_name.singularize == column.to_s or
|
49
|
-
x.primary_key_name == column.to_s }.uniq
|
76
|
+
def initialize(facet = {})
|
77
|
+
@facet = facet
|
50
78
|
|
51
|
-
|
79
|
+
# If they didn't provide a model, try and use the search object
|
52
80
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
# names. That's a problem, but whatevs for the time being.
|
81
|
+
if @facet[:object] && @facet[:model].nil?
|
82
|
+
@facet[:model] = @facet[:object].klass
|
83
|
+
end
|
57
84
|
|
58
|
-
|
85
|
+
@facet[:column] = @facet[:model].content_columns.select { |x| x.name == @facet[:column_name].to_s }
|
59
86
|
|
60
|
-
|
87
|
+
if @facet[:column].empty? # facet doesn't exist
|
61
88
|
|
62
|
-
|
63
|
-
|
89
|
+
# Is it an association?
|
90
|
+
#
|
91
|
+
# We check for :singular_name, :plural_name and :singular_id
|
64
92
|
|
65
|
-
|
93
|
+
if association = associations.select { |x|
|
94
|
+
x.plural_name == @facet[:column_name].to_s or
|
95
|
+
x.plural_name.singularize == @facet[:column_name].to_s or
|
96
|
+
x.primary_key_name == @facet[:column_name].to_s }.uniq and association.count > 0
|
66
97
|
|
67
|
-
|
98
|
+
if association.first.macro == :has_many
|
68
99
|
|
69
|
-
|
70
|
-
|
71
|
-
|
100
|
+
# For a has_many relationship, we want the plural name with _id.
|
101
|
+
# Ransack will then look at the _id column for the associated model.
|
102
|
+
# This won't work properly on models with nonstandard id column
|
103
|
+
# names. That's a problem, but whatevs for the time being.
|
72
104
|
|
73
|
-
|
105
|
+
@facet[:column_name] = "#{association.first.plural_name}_id"
|
74
106
|
|
75
|
-
|
76
|
-
# based on this association
|
107
|
+
elsif association.first.macro == :belongs_to
|
77
108
|
|
78
|
-
|
109
|
+
# If we're dealing with belongs_to, we can assume we just want
|
110
|
+
# to look at the foreign key on the current model. Much simpler.
|
79
111
|
|
112
|
+
@facet[:column_name] = association.first.foreign_key
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
# By default, we want to use a select box with a collection of
|
117
|
+
# what this model has_many of. If the user has specified something,
|
118
|
+
# we'll run with what they specified.
|
119
|
+
|
120
|
+
@facet[:type] = @facet[:type] || :collection
|
121
|
+
|
122
|
+
# If the user hasn't specified a collection, we'll provide one now
|
123
|
+
# based on this association
|
124
|
+
|
125
|
+
@facet[:collection] = @facet[:collection] || association.first.klass.all
|
126
|
+
end
|
80
127
|
else
|
81
|
-
|
128
|
+
|
129
|
+
# We found a column. Let's yank some useful information out of it
|
130
|
+
@facet[:column] = @facet[:column].first
|
131
|
+
@facet[:column_name] = @facet[:column].name
|
132
|
+
@facet[:column_type] = @facet[:column_type] || @facet[:column].type
|
82
133
|
end
|
83
|
-
else
|
84
134
|
|
85
|
-
|
135
|
+
@facet[:type] = @facet[:type] || default_facet_type
|
86
136
|
|
87
|
-
facet[:column] = facet[:column].first
|
88
|
-
facet[:column_name] = facet[:column].name
|
89
|
-
facet[:column_type] = facet[:column_type] || facet[:column].type
|
90
137
|
end
|
91
138
|
|
92
|
-
|
139
|
+
def associations
|
140
|
+
@facet[:model].reflections.values
|
141
|
+
end
|
93
142
|
|
143
|
+
# If the user doesn't pass options[:label], we'll build a default label
|
144
|
+
# based on the type of facet we're rendering.
|
145
|
+
|
146
|
+
def default_label_for_facet
|
147
|
+
case @facet[:type]
|
148
|
+
when :true, :false
|
149
|
+
return ''
|
150
|
+
when :null
|
151
|
+
return label("#{@facet[:column_name]}_null",
|
152
|
+
"#{@facet[:column_name].to_s.humanize} Is Null?")
|
153
|
+
when :not_null
|
154
|
+
return label("#{@facet[:column_name]}_not_null",
|
155
|
+
"#{@facet[:column_name].to_s.humanize} Is Not Null?")
|
156
|
+
when :cont
|
157
|
+
return label("#{@facet[:column_name]}_cont",
|
158
|
+
"#{@facet[:column_name].to_s.humanize} Contains")
|
159
|
+
when :not_cont
|
160
|
+
return label("#{@facet[:column_name]}_not_cont",
|
161
|
+
"#{@facet[:column_name].to_s.humanize} Doesn't Contain")
|
162
|
+
when :start
|
163
|
+
return label("#{@facet[:column_name]}_start",
|
164
|
+
"#{@facet[:column_name].to_s.humanize} Starts With")
|
165
|
+
when :not_start
|
166
|
+
return label("#{@facet[:column_name]}_not_start",
|
167
|
+
"#{@facet[:column_name].to_s.humanize} Doesn't Start With")
|
168
|
+
when :end
|
169
|
+
return label("#{@facet[:column_name]}_end",
|
170
|
+
"#{@facet[:column_name].to_s.humanize} Ends With")
|
171
|
+
when :not_end
|
172
|
+
return label("#{@facet[:column_name]}_not_end",
|
173
|
+
"#{@facet[:column_name].to_s.humanize} Doesn't End With")
|
174
|
+
when :between
|
175
|
+
return label("#{@facet[:column_name]}",
|
176
|
+
"#{@facet[:column_name].to_s.humanize} Between")
|
177
|
+
when :gte
|
178
|
+
return label("#{@facet[:column_name]}_gte",
|
179
|
+
"#{@facet[:column_name].to_s.humanize} Greater Than")
|
180
|
+
when :lte
|
181
|
+
return label("#{@facet[:column_name]}_lte",
|
182
|
+
"#{@facet[:column_name].to_s.humanize} Less Than")
|
183
|
+
else
|
184
|
+
return label("#{@facet[:column_name]}")
|
185
|
+
end
|
186
|
+
end
|
94
187
|
|
95
|
-
#
|
188
|
+
# Now that we have our type, we can render the actual form field for
|
189
|
+
# Ransack
|
96
190
|
|
97
|
-
|
191
|
+
def render_facet
|
98
192
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
facet_html << default_label_for_facet(facet)
|
103
|
-
end
|
193
|
+
# Insert our label first
|
194
|
+
|
195
|
+
facet_html = "<div class=\"facet_label #{additional_classes}\">"
|
104
196
|
|
105
|
-
|
197
|
+
if @facet[:label]
|
198
|
+
facet_html << label("#{@facet[:column_name]}", @facet[:label])
|
199
|
+
else
|
200
|
+
facet_html << default_label_for_facet
|
201
|
+
end
|
106
202
|
|
107
|
-
|
203
|
+
facet_html << "</div>"
|
108
204
|
|
109
|
-
|
205
|
+
# And now the fields
|
110
206
|
|
111
|
-
|
112
|
-
end
|
207
|
+
facet_html << "<div class=\"facet_input #{additional_classes}\">"
|
113
208
|
|
114
|
-
|
115
|
-
|
209
|
+
case @facet[:type]
|
210
|
+
when :cont, :not_cont, :start, :not_start, :end, :not_end, :gteq, :lteq
|
211
|
+
facet_html << text_field
|
212
|
+
when :collection
|
213
|
+
facet_html << facet_collection
|
214
|
+
when :null, :not_null, :true, :false
|
215
|
+
facet_html << check_box
|
216
|
+
when :between
|
217
|
+
facet_html << text_field(:predicate => :gteq)
|
218
|
+
facet_html << "<span class=\"facet_divider\">—</span>"
|
219
|
+
facet_html << text_field(:predicate => :lteq)
|
220
|
+
end
|
116
221
|
|
117
|
-
|
118
|
-
|
119
|
-
when :cont
|
120
|
-
return self.label("#{facet[:column_name]}_cont".to_sym)
|
121
|
-
else
|
122
|
-
return self.label("#{facet[:column_name]}")
|
222
|
+
facet_html << "</div>"
|
223
|
+
facet_html
|
123
224
|
end
|
124
|
-
end
|
125
225
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
facet_html << self.collection_select("#{facet[:column_name]}_eq".to_sym, facet[:collection], :id, :to_s, :include_blank => true)
|
138
|
-
when :between
|
139
|
-
facet_html << "From "
|
140
|
-
facet_html << self.text_field("#{facet[:column_name]}_gteq".to_sym, :size => 11)
|
141
|
-
facet_html << "To "
|
142
|
-
facet_html << self.text_field("#{facet[:column_name]}_lteq".to_sym, :size => 11)
|
143
|
-
when :gteq
|
144
|
-
facet_html << self.text_field("#{facet[:column_name]}_gteq".to_sym, :size => 5)
|
145
|
-
when :lteq
|
146
|
-
facet_html << self.text_field("#{facet[:column_name]}_gteq".to_sym, :size => 5)
|
226
|
+
# If no options[:type] is specified, we'll look at the column type for
|
227
|
+
# the column in the model and make an educated guess.
|
228
|
+
|
229
|
+
def default_facet_type
|
230
|
+
|
231
|
+
case @facet[:column_type]
|
232
|
+
when :string, :text
|
233
|
+
return :cont
|
234
|
+
when :datetime, :date, :float, :integer, :double
|
235
|
+
return :between
|
236
|
+
end
|
147
237
|
end
|
148
238
|
|
149
|
-
|
150
|
-
|
151
|
-
|
239
|
+
def additional_classes
|
240
|
+
additional_class = "#{@facet[:type]} #{@facet[:column_type]}"
|
241
|
+
end
|
152
242
|
|
153
|
-
|
154
|
-
|
243
|
+
def text_field(options = {})
|
244
|
+
predicate = options[:predicate] || @facet[:type]
|
245
|
+
name = "#{@facet[:column_name]}_#{predicate.to_s}"
|
155
246
|
|
156
|
-
|
247
|
+
if @facet[:params] and @facet[:params][:q]
|
248
|
+
value = @facet[:params][:q][name.to_sym]
|
249
|
+
end
|
157
250
|
|
158
|
-
|
159
|
-
when :string, :text
|
160
|
-
return :cont
|
161
|
-
when :datetime, :date, :float, :integer, :double
|
162
|
-
return :between
|
251
|
+
text_field_tag self.name_for(name), value
|
163
252
|
end
|
164
|
-
end
|
165
253
|
|
166
|
-
|
167
|
-
|
168
|
-
|
254
|
+
def check_box
|
255
|
+
check_box_tag self.name_for("#{@facet[:column_name]}_#{@facet[:type].to_s}")
|
256
|
+
end
|
169
257
|
|
170
|
-
|
258
|
+
def facet_collection
|
259
|
+
collection_select @facet[:object_name].to_sym,
|
260
|
+
"#{@facet[:column_name]}_eq".to_sym, @facet[:collection],
|
261
|
+
:id, :to_s, :include_blank => true
|
262
|
+
end
|
171
263
|
|
172
|
-
|
173
|
-
|
174
|
-
|
264
|
+
def label(string_name, string_label = nil)
|
265
|
+
display_label = string_label || string_name.humanize
|
266
|
+
label_tag self.name_for(string_name), display_label
|
267
|
+
end
|
175
268
|
|
269
|
+
def name_for(string_name)
|
270
|
+
"#{@facet[:object_name]}[#{string_name}]".to_sym
|
271
|
+
end
|
272
|
+
end
|
176
273
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: facet_for
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-10-
|
12
|
+
date: 2011-10-28 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ransack
|
16
|
-
requirement: &
|
16
|
+
requirement: &79711100 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *79711100
|
25
25
|
description: Provides helpers for creating search forms with Ransack
|
26
26
|
email:
|
27
27
|
- jbarket@sleepunit.com
|
@@ -31,9 +31,12 @@ extra_rdoc_files: []
|
|
31
31
|
files:
|
32
32
|
- .gitignore
|
33
33
|
- Gemfile
|
34
|
+
- README.textile
|
34
35
|
- Rakefile
|
35
36
|
- facet_for.gemspec
|
36
37
|
- lib/facet_for.rb
|
38
|
+
- lib/facet_for/facet_helper.rb
|
39
|
+
- lib/facet_for/form_builder.rb
|
37
40
|
- lib/facet_for/version.rb
|
38
41
|
homepage: ''
|
39
42
|
licenses: []
|