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 CHANGED
@@ -2,3 +2,6 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ \#*#
6
+ .\#*#
7
+
data/README.textile ADDED
@@ -0,0 +1,8 @@
1
+ h1. facet_for
2
+
3
+ facet_for is a collection of FormBuilder helpers that speed up the process of
4
+ creating complex search forms with "Ransack":http://github.com/ernie/ransack
5
+
6
+ h2. Installation
7
+
8
+ Simply include 'facet_for' in your Gemfile
@@ -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
@@ -1,3 +1,3 @@
1
1
  module FacetFor
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.3"
3
3
  end
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
- class ActionView::Helpers::FormBuilder
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 facet_for(column, *args)
26
+ def self.create_facet(*args)
11
27
  options = args.extract_options!
12
28
 
13
- facet_html = ''
29
+ @facet = Facet.new(options)
30
+ @facet.render_facet.html_safe
31
+ end
32
+
14
33
 
15
- # Here, we collect information about the facet. What the user doesn't
16
- # supply, we'll make our best guess for.
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
- facet = { }
19
- facet[:model] = @object.klass
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
- facet[:column] = facet[:model].content_columns.select { |x| x.name == column.to_s }
43
+ column_options
44
+ end
22
45
 
23
- # Information about the column. This is used to generate the default
24
- # label, and to pass the right selector to the Ransack fields.
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
- facet[:column_name] = options[:column_name]
27
- facet[:column_type] = options[:column_type]
49
+ field_options
50
+ end
28
51
 
29
- # This is the type of field we will render. If this isn't provided, we'll
30
- # determine this based on facet_column_type
52
+ def self.options_for_model(model, association = nil, grouped = false)
53
+ options = []
54
+ preface = ''
31
55
 
32
- facet[:type] = options[:type]
56
+ if association # preface field names for cross model displaying
57
+ preface = "#{association}."
58
+ end
33
59
 
34
- # In the specific case of facet_type == :collection, we'll allow the user
35
- # to specify their own collection. If it's an association, we'll attempt
36
- # to provide one.
60
+ if model.ransackable_attributes
61
+ options = model.ransackable_attributes.map { |a| [a.titleize, "#{preface}#{a}".to_sym] }
62
+ end
37
63
 
38
- facet[:collection] = options[:collection]
64
+ options
65
+ end
39
66
 
40
- if facet[:column].empty? # facet doesn't exist
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
- # Is it an association?
43
- #
44
- # We check for :singular_name, :plural_name and :singular_id
74
+ attr_accessor :facet
45
75
 
46
- if association = associations_for(facet[:model]).select { |x|
47
- x.plural_name == column.to_s or
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
- if association.first.macro == :has_many
79
+ # If they didn't provide a model, try and use the search object
52
80
 
53
- # For a has_many relationship, we want the plural name with _id.
54
- # Ransack will then look at the _id column for the associated model.
55
- # This won't work properly on models with nonstandard id column
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
- facet[:column_name] = "#{association.first.plural_name}_id"
85
+ @facet[:column] = @facet[:model].content_columns.select { |x| x.name == @facet[:column_name].to_s }
59
86
 
60
- elsif association.first.macro == :belongs_to
87
+ if @facet[:column].empty? # facet doesn't exist
61
88
 
62
- # If we're dealing with belongs_to, we can assume we just want
63
- # to look at the foreign key on the current model. Much simpler.
89
+ # Is it an association?
90
+ #
91
+ # We check for :singular_name, :plural_name and :singular_id
64
92
 
65
- facet[:column_name] = association.first.foreign_key
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
- end
98
+ if association.first.macro == :has_many
68
99
 
69
- # By default, we want to use a select box with a collection of
70
- # what this model has_many of. If the user has specified something,
71
- # we'll run with what they specified.
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
- facet[:type] = facet[:type] || :collection
105
+ @facet[:column_name] = "#{association.first.plural_name}_id"
74
106
 
75
- # If the user hasn't specified a collection, we'll provide one now
76
- # based on this association
107
+ elsif association.first.macro == :belongs_to
77
108
 
78
- facet[:collection] = facet[:collection] || association.first.klass.all
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
- return # nothing found
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
- # We found a column. Let's yank some useful information out of it
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
- facet[:type] = facet[:type] || default_facet_type(facet)
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
- # Insert our label first
188
+ # Now that we have our type, we can render the actual form field for
189
+ # Ransack
96
190
 
97
- facet_html = "<div class=\"label #{additional_classes(facet)}\">"
191
+ def render_facet
98
192
 
99
- if options[:label]
100
- facet_html << self.label(column, options[:label])
101
- else
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
- facet_html << "</div>"
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
- # And now the fields
203
+ facet_html << "</div>"
108
204
 
109
- facet_html << render_facet_for(facet)
205
+ # And now the fields
110
206
 
111
- facet_html.html_safe
112
- end
207
+ facet_html << "<div class=\"facet_input #{additional_classes}\">"
113
208
 
114
- # If the user doesn't pass options[:label], we'll build a default label
115
- # based on the type of facet we're rendering.
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\">&mdash;</span>"
219
+ facet_html << text_field(:predicate => :lteq)
220
+ end
116
221
 
117
- def default_label_for_facet(facet)
118
- case facet[:type]
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
- # Now that we have our type, we can render the actual form field for
127
- # Ransack
128
-
129
- def render_facet_for(facet)
130
-
131
- facet_html = "<div class=\"input #{additional_classes(facet)}\">"
132
-
133
- case facet[:type]
134
- when :cont
135
- facet_html << self.text_field("#{facet[:column_name]}_cont".to_sym)
136
- when :collection
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
- facet_html << "</div>"
150
- facet_html
151
- end
239
+ def additional_classes
240
+ additional_class = "#{@facet[:type]} #{@facet[:column_type]}"
241
+ end
152
242
 
153
- # If no options[:type] is specified, we'll look at the column type for
154
- # the column in the model and make an educated guess.
243
+ def text_field(options = {})
244
+ predicate = options[:predicate] || @facet[:type]
245
+ name = "#{@facet[:column_name]}_#{predicate.to_s}"
155
246
 
156
- def default_facet_type(facet)
247
+ if @facet[:params] and @facet[:params][:q]
248
+ value = @facet[:params][:q][name.to_sym]
249
+ end
157
250
 
158
- case facet[:column_type]
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
- def additional_classes(facet)
167
- additional_class = "#{facet[:type]} #{facet[:column_type]}"
168
- end
254
+ def check_box
255
+ check_box_tag self.name_for("#{@facet[:column_name]}_#{@facet[:type].to_s}")
256
+ end
169
257
 
170
- # We can use reflections to determine has_many and belongs_to associations
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
- def associations_for(model)
173
- model.reflections.values
174
- end
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.1
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-19 00:00:00.000000000Z
12
+ date: 2011-10-28 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ransack
16
- requirement: &84397210 !ruby/object:Gem::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: *84397210
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: []