facet_for 0.0.1 → 0.0.3
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 +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: []
|