Class: FacetFor::Facet

Inherits:
Object
  • Object
show all
Includes:
ActionView::Helpers::AssetTagHelper, ActionView::Helpers::CaptureHelper, ActionView::Helpers::FormOptionsHelper, ActionView::Helpers::FormTagHelper, ActionView::Helpers::TagHelper
Defined in:
lib/facet_for.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(facet = {}) ⇒ Facet

Returns a new instance of Facet.



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/facet_for.rb', line 79

def initialize(facet = {})
  @facet = facet

  # If they didn't provide a model, try and use the search object

  if @facet[:object] && @facet[:model].nil?
    @facet[:model] = @facet[:object].klass
  end

  @facet[:column] = @facet[:model].content_columns.select { |x| x.name == @facet[:column_name].to_s }

  if @facet[:column].empty? # facet doesn't exist

    # Is it an association?
    #
    # We check for :singular_name, :plural_name and :singular_id

    if association = associations.select { |x|
      x.plural_name == @facet[:column_name].to_s or
      x.plural_name.singularize == @facet[:column_name].to_s }.uniq and association.count > 0

      if association.first.macro == :has_many
        # For a has_many relationship, we want the plural name with _id.
        # Ransack will then look at the _id column for the associated model.
        # This won't work properly on models with nonstandard id column
        # names. That's a problem, but whatevs for the time being.
        @facet[:association_class]  = association.first.klass
        @facet[:association_name] = "#{association.first.plural_name}"
        @facet[:column_name] = "#{association.first.plural_name}_id"
        @facet[:clean_column_name] = "#{association.first.name.to_s.singularize}_id"
        @facet[:association_primary_key] = association.first.primary_key_column.name

      elsif association.first.macro == :belongs_to

        # If we're dealing with belongs_to, we can assume we just want
        # to look at the foreign key on the current model. Much simpler.
        @facet[:association_class] = association.first.klass
        @facet[:association_name] = association.first.name
        @facet[:column_name] = association.first.foreign_key
        @facet[:association_primary_key] = association.first.primary_key_column.name

      end

      # By default, we want to use a select box with a collection of
      # what this model has_many of. If the user has specified something,
      # we'll run with what they specified.

      @facet[:type] = @facet[:type] || :collection

      # If the user hasn't specified a collection, we'll provide one now
      # based on this association. We only want to use distinct values.
      # This could probably be cleaner, but it works.

      unless @facet[:collection]
        unique_objects = @facet[:model].unscoped.select("DISTINCT #{clean_column}").where("#{clean_column} IS NOT NULL").map { |x| x.send(clean_column) }.join(", ")

        if unique_objects and unique_objects.length > 0
          @facet[:collection] = @facet[:association_class].unscoped.where("#{@facet[:association_primary_key]} IN (#{unique_objects})")
        else
          @facet[:collection] = nil
        end
      end
    end
  else

    # We found a column. Let's yank some useful information out of it
    @facet[:column] = @facet[:column].first
    @facet[:column_name] = @facet[:column].name
    @facet[:column_type] = @facet[:column_type] || @facet[:column].type
  end

  @facet[:type] = @facet[:type] || default_facet_type

end

Instance Attribute Details

#facetObject

Returns the value of attribute facet.



77
78
79
# File 'lib/facet_for.rb', line 77

def facet
  @facet
end

Instance Method Details

#additional_classesObject



284
285
286
# File 'lib/facet_for.rb', line 284

def additional_classes
  additional_class = "#{@facet[:type]} #{@facet[:column_type]}"
end

#associationsObject



154
155
156
# File 'lib/facet_for.rb', line 154

def associations
  @facet[:model].reflections.values
end

#check_box(value = "1") ⇒ Object



295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
# File 'lib/facet_for.rb', line 295

def check_box(value = "1")
  name = "#{@facet[:column_name]}_#{@facet[:type]}"



  if @facet[:type] == :cont_any
    check_box_label =  label_tag(self.name_for(name, true),
                                 value.to_s.humanize)
    check_box_name = self.name_for(name, true)
  else

    label_value = @facet[:column_name].to_s.humanize

    case @facet[:type]
    when :false
      label_value = "Is Not #{label_value}"
    when :null
      label_value += "Is Null"
    when :not_null
      label_value += "Is Not Null"
    end

    check_box_label =  label_tag(self.name_for(name, true), label_value)
    check_box_name = self.name_for(name, false)
  end

  check_box_tag(check_box_name, value, check_box_checked(value)) + check_box_label
end

#check_box_checked(value = "1") ⇒ Object



324
325
326
327
328
329
330
331
332
333
334
# File 'lib/facet_for.rb', line 324

def check_box_checked(value = "1")
  name = "#{@facet[:column_name]}_#{@facet[:type]}"
  selected = @facet[:object].send(name)

  if @facet[:type] == :cont_any
    return (!selected.nil? and selected.include?(value))
  else
    return selected
  end

end

#clean_columnObject



370
371
372
# File 'lib/facet_for.rb', line 370

def clean_column
  @facet[:clean_column_name] || @facet[:column_name]
end

#default_facet_typeObject

If no options is specified, we’ll look at the column type for the column in the model and make an educated guess.



274
275
276
277
278
279
280
281
282
# File 'lib/facet_for.rb', line 274

def default_facet_type

  case @facet[:column_type]
  when :string, :text
    return :cont
  when :datetime, :date, :float, :integer, :double
    return :between
  end
end

#default_label_for_facetObject

If the user doesn’t pass options, we’ll build a default label based on the type of facet we’re rendering.



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/facet_for.rb', line 161

def default_label_for_facet
  case @facet[:type]
  when :true, :false
    return ''
  when :null
    return label("#{@facet[:column_name]}_null",
                 "#{@facet[:column_name].to_s.humanize} Is Null?")
  when :not_null
    return label("#{@facet[:column_name]}_not_null",
                 "#{@facet[:column_name].to_s.humanize} Is Not Null?")
  when :cont, :cont_any
    return label("#{@facet[:column_name]}_cont",
                 "#{@facet[:column_name].to_s.humanize} Contains")
  when :not_cont
    return label("#{@facet[:column_name]}_not_cont",
                 "#{@facet[:column_name].to_s.humanize} Doesn't Contain")
  when :start
    return label("#{@facet[:column_name]}_start",
                 "#{@facet[:column_name].to_s.humanize} Starts With")
  when :not_start
    return label("#{@facet[:column_name]}_not_start",
                 "#{@facet[:column_name].to_s.humanize} Doesn't Start With")
  when :end
    return label("#{@facet[:column_name]}_end",
                 "#{@facet[:column_name].to_s.humanize} Ends With")
  when :not_end
    return label("#{@facet[:column_name]}_not_end",
                 "#{@facet[:column_name].to_s.humanize} Doesn't End With")
  when :between
    return label("#{@facet[:column_name]}",
                 "#{@facet[:column_name].to_s.humanize} Between")
  when :gte
    return label("#{@facet[:column_name]}_gte",
                 "#{@facet[:column_name].to_s.humanize} Greater Than")
  when :lte
    return label("#{@facet[:column_name]}_lte",
                 "#{@facet[:column_name].to_s.humanize} Less Than")
  else
    return label("#{@facet[:column_name]}")
  end
end

#facet_collectionObject



336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'lib/facet_for.rb', line 336

def facet_collection
  name = "#{@facet[:column_name]}_eq"

  if @facet[:default]
    selected = @facet[:default]
  else
    selected = @facet[:object].send(name)
  end

  # @facet[:collection] should be set if we've given it a valid
  # association, or passed in a collection by hand.
  #
  # this assumes that we want to see all unique values from the database
  # for the given column

  if @facet[:collection].nil?
    @facet[:collection] = unique_value_collection
  end

  if @facet[:collection].class == Array and
      @facet[:collection].first.class == String

    return select_tag self.name_for(name),
    options_for_select(@facet[:collection], selected),
    :include_blank => true, :class => @facet[:class]
  else
    return select_tag self.name_for(name),
    options_from_collection_for_select(@facet[:collection], :id, :to_s,
                                       selected), :include_blank => true,
    :class => @facet[:class]
  end

end

#label(string_name, string_label = nil) ⇒ Object



378
379
380
381
# File 'lib/facet_for.rb', line 378

def label(string_name, string_label = nil)
  display_label = string_label || string_name.humanize
  label_tag self.name_for(string_name), display_label
end

#name_for(string_name, array = false) ⇒ Object



383
384
385
386
387
388
# File 'lib/facet_for.rb', line 383

def name_for(string_name, array = false)
  name = "#{@facet[:object_name]}[#{string_name}]"
  name += '[]' if array

  name.to_sym
end

#render_facetObject

Now that we have our type, we can render the actual form field for Ransack



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/facet_for.rb', line 206

def render_facet

  # Insert our label first

  facet_html = "<div class=\"facet_label #{additional_classes}\">"

  if @facet[:label]
    facet_html << label("#{@facet[:column_name]}", @facet[:label])
  else
    facet_html << default_label_for_facet
  end

  facet_html << "</div>"

  # And now the fields

  facet_html << "<div class=\"facet_input #{additional_classes}\">"

  case @facet[:type]
  when :cont, :not_cont, :start, :not_start, :end, :not_end, :gteq, :lteq
    facet_html << text_field
  when :cont_any
    collection_type = :array

    if @facet[:collection].nil?
      @facet[:collection] = unique_value_collection
    end

    if @facet[:collection].first.class == 'String'
      collection_type = :string
    end

    @facet[:collection].each do |check|
      facet_html << "<div class=\"facet_input cont_any check_box\">"

      if collection_type == :array
        facet_html << check_box(check[1])
      else
        facet_html << check_box(check)
      end

      if collection_type == :array
        facet_html << label(self.name_for("#{@facet[:column_name]}_#{@facet[:type].to_s}", true), check[0])
      else
        facet_html << label(self.name_for("#{@facet[:column_name]}_#{@facet[:type].to_s}", true), check)
      end

      facet_html << "</div>"
    end

    facet_html
  when :collection
    facet_html << facet_collection
  when :null, :not_null, :true, :false
    facet_html << check_box
  when :between
    facet_html << text_field(:predicate => :gteq)
    facet_html << "<span class=\"facet_divider\">&mdash;</span>"
    facet_html << text_field(:predicate => :lteq)
  end

  facet_html << "</div>"
  facet_html
end

#text_field(options = {}) ⇒ Object



288
289
290
291
292
293
# File 'lib/facet_for.rb', line 288

def text_field(options = {})
  predicate = options[:predicate] || @facet[:type]
  name = "#{@facet[:column_name]}_#{predicate.to_s}"

  text_field_tag self.name_for(name), @facet[:object].send(name)
end

#unique_value_collectionObject



374
375
376
# File 'lib/facet_for.rb', line 374

def unique_value_collection
  @facet[:collection] = @facet[:model].unscoped.select("DISTINCT #{clean_column}").where("#{clean_column} IS NOT NULL").map { |x| x.send(clean_column) }
end