toolbox 0.1.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.
Files changed (56) hide show
  1. data/Rakefile +57 -0
  2. data/lib/dirs.rb +9 -0
  3. data/lib/toolbox/config.rb +211 -0
  4. data/lib/toolbox/default_controller.rb +393 -0
  5. data/lib/toolbox/helpers.rb +11 -0
  6. data/lib/toolbox/rendering.rb +413 -0
  7. data/lib/toolbox/searching.rb +85 -0
  8. data/lib/toolbox/session_params.rb +63 -0
  9. data/lib/toolbox/sorting.rb +74 -0
  10. data/lib/toolbox/version.rb +4 -0
  11. data/lib/toolbox.rb +29 -0
  12. data/locale/de/LC_MESSAGES/toolbox.mo +0 -0
  13. data/public/images/add.png +0 -0
  14. data/public/images/arrow_down.gif +0 -0
  15. data/public/images/arrow_up.gif +0 -0
  16. data/public/images/close.png +0 -0
  17. data/public/images/edit.gif +0 -0
  18. data/public/images/email.png +0 -0
  19. data/public/images/page.png +0 -0
  20. data/public/images/page_acrobat.png +0 -0
  21. data/public/images/page_add.png +0 -0
  22. data/public/images/page_copy.png +0 -0
  23. data/public/images/page_delete.png +0 -0
  24. data/public/images/page_edit.png +0 -0
  25. data/public/images/page_excel.png +0 -0
  26. data/public/images/page_list.png +0 -0
  27. data/public/images/page_save.png +0 -0
  28. data/public/images/page_word.png +0 -0
  29. data/public/images/remove.png +0 -0
  30. data/public/images/show.gif +0 -0
  31. data/public/images/spinner.gif +0 -0
  32. data/public/javascripts/popup.js +498 -0
  33. data/public/javascripts/toolbox.js +18 -0
  34. data/public/stylesheets/context_menu.css +168 -0
  35. data/public/stylesheets/popup.css +30 -0
  36. data/public/stylesheets/toolbox.css +107 -0
  37. data/view/toolbox/_collection.html.erb +24 -0
  38. data/view/toolbox/_collection_header.html.erb +7 -0
  39. data/view/toolbox/_context_menu.html.erb +17 -0
  40. data/view/toolbox/_dialogs.html.erb +6 -0
  41. data/view/toolbox/_form.html.erb +30 -0
  42. data/view/toolbox/_form_collection_row.html.erb +18 -0
  43. data/view/toolbox/_form_fieldset.html.erb +30 -0
  44. data/view/toolbox/_form_fieldset_row.html.erb +19 -0
  45. data/view/toolbox/_list.html.erb +25 -0
  46. data/view/toolbox/_list_row.html.erb +10 -0
  47. data/view/toolbox/_menu.html.erb +7 -0
  48. data/view/toolbox/_search_field.html.erb +8 -0
  49. data/view/toolbox/_show.html.erb +12 -0
  50. data/view/toolbox/_show_collection_row.html.erb +6 -0
  51. data/view/toolbox/_show_fieldset.html.erb +21 -0
  52. data/view/toolbox/edit.html.erb +5 -0
  53. data/view/toolbox/index.html.erb +3 -0
  54. data/view/toolbox/new.html.erb +9 -0
  55. data/view/toolbox/show.html.erb +39 -0
  56. metadata +178 -0
@@ -0,0 +1,413 @@
1
+ require File.dirname(__FILE__) << '/config'
2
+
3
+ # Helper method for form builders
4
+ module ActionView
5
+ module Helpers
6
+ class FormBuilder
7
+ def tag_id(method, options = {})
8
+ it = InstanceTag.new(@object_name, method, @template, nil, @object)
9
+ it.send :add_default_name_and_id, options #private method
10
+ options['id']
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ module Toolbox
17
+
18
+
19
+ class Renderer
20
+ attr_reader :widget_config
21
+
22
+ def initialize view, widget_config, dialog = false
23
+ @view = view
24
+ @widget_config = widget_config
25
+ @dialog = dialog
26
+ end
27
+
28
+
29
+ # Get the field value out of a model object
30
+ # If the attribute method is defined and it's a symbol, this method is called on the model object
31
+ # If the attribute method is defined and it's a block (Proc), the block is called with the model object as parameter
32
+ # In all other cases, the field name is interpreted as method and called on the model object
33
+ def value(rec)
34
+ val = rec.send(@widget_config.model_method) if @widget_config.model_method && @widget_config.model_method.is_a?(Symbol)
35
+ val = @widget_config.model_method.call(rec) if @widget_config.model_method && @widget_config.model_method.is_a?(Proc)
36
+ unless val # fallback
37
+ a = @widget_config.name.to_s.split('.')
38
+ a.each do |t|
39
+ rec = rec.send(t)
40
+ break unless rec # stop if nil is returned
41
+ end
42
+ val = rec
43
+ end
44
+ val = nil if val == ''
45
+ val
46
+ end
47
+
48
+ # Renders the label of the widget.
49
+ # If show_model is set to a model name, the model name of this widget
50
+ # will be added in parenthesis if it differs to the value in show_model.
51
+ def label(show_model = nil, html = true)
52
+ unless @widget_config.suppress_label
53
+ if @widget_config.label
54
+ html ? @view.send(:h, @widget_config.label) : @widget_config.label
55
+ else
56
+ translate_field(show_model, html)
57
+ end
58
+ end
59
+ end
60
+
61
+ # Translates a column name of a model using gettext
62
+ # The show_model argument is optional. If true, the model name will also be translated
63
+ # and appended to the column name. Ensure, that the model name is also translated
64
+ # (gettext does not extract this automatically).
65
+ #
66
+ def translate_field(show_model = nil, html = true)
67
+ col_name = @widget_config.name.to_s
68
+ model_name = @widget_config.model_name.classify
69
+ a = col_name.split('.')
70
+ if a.length > 1
71
+ model_name = a[-2].classify
72
+ col_name = a[-1]
73
+ end
74
+
75
+ text = @view.send(:s_, model_name + '|' + col_name.humanize)
76
+ text += ' (' + @view.send(:s_, model_name) + ')' if show_model && model_name != show_model
77
+
78
+ html ? @view.send(:h, text) : text
79
+ end
80
+
81
+ end
82
+
83
+ class FieldRenderer < Renderer
84
+ # Generate a sort link for the given field
85
+ # A list can only be sorted by one field.
86
+ # Multiple list on one page can be distinguished by the prefix
87
+ # The field parameter is a field form a list configuration (Symbol, String or Hash)
88
+ # The text is the link text.
89
+ #
90
+ # The following css classes are assigend regarding the current sort direction
91
+ # - no sorting: 'sort-none'
92
+ # - asc sorting: 'sort-asc'
93
+ # - desc sorting: 'sort-desc'
94
+ def sort_link(params, prefix = nil)
95
+ if @widget_config.suppress_sorting
96
+ label
97
+ else
98
+ # see if we are already sorted
99
+ current_dir = Toolbox::Sorting.current(params, prefix, @widget_config.name.to_s)
100
+ case current_dir
101
+ when nil
102
+ new_dir = 'asc'
103
+ class_attr = 'sort-none'
104
+ when 'asc'
105
+ new_dir = 'desc'
106
+ class_attr = 'sort-asc'
107
+ else
108
+ new_dir = nil
109
+ class_attr = 'sort-desc'
110
+ end
111
+ @view.link_to(label, Toolbox::Sorting.params(params, prefix, @widget_config.name.to_s, new_dir), {:class => class_attr})
112
+ end
113
+ end
114
+
115
+
116
+ # Renders the value of an attribute of a given object.
117
+ # Parameters:
118
+ # - object: the object to get the attribute of
119
+ # - html: if true, returns the value in html (escaped etc.)
120
+ # The following options are used:
121
+ # - :model_method => an alternative method or block of the object to get the display value
122
+ # - :render_method => an (helper/view) method or block that returns the rendered value (parameters: object, val, html)
123
+ # - :hide_empty: returns nil if the display value is empty. Otherwise, an empty string
124
+ # will be returned (or &nbsp; in case of html)
125
+ # - :suppress_link: if the display value is an ActiveRecord usually a link to it will
126
+ # be returned (only if html = true of course). If this option is set, only the string
127
+ # represenation will be returned
128
+ # - :join if the value is an array and :join is defined, the values are joined using the string
129
+ # representation of the :join value. Else, a unnumbered list (ul) is generated
130
+ #
131
+ # If the display value is an instance of Date, it will be formatted using the
132
+ # :local_date format
133
+ def render_value(object, html = true)
134
+ val = value(object)
135
+ unless val == nil && @widget_config.hide_empty
136
+ if @widget_config.render_method
137
+ render_own object, val, html
138
+ elsif val.is_a? Array
139
+ render_array object, val, html
140
+ else
141
+ value_text val, html, object
142
+ end
143
+ end
144
+ end
145
+
146
+ protected
147
+ # internal helper to get the text of a value-object
148
+ def value_text (val, html, context) #:nodoc:
149
+ text = val # default
150
+ unless val == nil && @widget_config.hide_empty
151
+ if val
152
+ if @widget_config.trunc && val.to_s.length > @widget_config.trunc
153
+ v = val.to_s
154
+ v = v[0,@widget_config.trunc] + '...'
155
+ text = html ? "<span title=\"#{@view.send(:h, val)}\">#{@view.send(:h, v)}</span>" : v
156
+ else
157
+ text = html ? @view.send(:h, val) : val
158
+ end
159
+ else
160
+ text = html ? '&nbsp;' : ''
161
+ end
162
+ if val.kind_of?(ActiveRecord::Base) && !@widget_config.suppress_link
163
+ if html
164
+ text = @view.send(:link_to, val.to_s, val)
165
+ text += @view.context_menu_link(val, context) unless @widget_config.suppress_context_menu
166
+ else
167
+ text = val.to_s
168
+ end
169
+ end
170
+ text = val.to_s(:local_date) if val.instance_of? Date
171
+ text = val.to_s(:local_time) if val.instance_of? Time
172
+ text = val.to_s(:local_datetime) if val.instance_of? ActiveSupport::TimeWithZone
173
+ text += @widget_config.suffix.to_s if @widget_config.suffix
174
+ end
175
+ text
176
+ end
177
+
178
+ private
179
+
180
+ def render_own (object, val, html)
181
+ if @widget_config.render_method.is_a? Proc
182
+ @widget_config.render_method.call @view, object, val, html
183
+ else
184
+ @view.send @widget_config.render_method, object, val, html
185
+ end
186
+ end
187
+
188
+ def render_array (object, val, html)
189
+ if val.size > 0
190
+ if @widget_config.join
191
+ val.map { |v| value_text(v, html, object) }.join(@widget_config.join.to_s)
192
+ else
193
+ @view.content_tag 'ul' do
194
+ val.map { |v| @view.content_tag 'li', value_text(v, html, object) }.join
195
+ end
196
+ end
197
+ else # map empty array to nil
198
+ value_text nil, html, object
199
+ end
200
+ end
201
+
202
+ end
203
+
204
+ class ControlRenderer < Renderer
205
+
206
+ def render_control(form, rec, has_error = false)
207
+ options = {}
208
+ options[:class] = 'error' if has_error
209
+ options[:class] = (options[:class] || '') + ' ' + @widget_config.css_class if @widget_config.css_class
210
+ options[:id] = "dialog_#{form.tag_id @widget_config.name}" if @dialog
211
+ options[:title] = @view.send(:h, @widget_config.info) if @widget_config.info
212
+ options[:disabled] = @widget_config.disabled if @widget_config.disabled and not @widget_config.disabled.is_a?(Proc)
213
+ options[:disabled] = @widget_config.disabled.call(rec) if @widget_config.disabled.is_a?(Proc)
214
+
215
+ case @widget_config.type
216
+ when :select
217
+ render_select form, rec, options
218
+ when :collection_select
219
+ render_collection_select form, rec, options
220
+ when :auto_complete
221
+ render_autocomplete form, rec, options
222
+ when :radio
223
+ render_radio form, rec, options
224
+ when :check_box
225
+ render_checkbox form, rec, options
226
+ when :date
227
+ render_date form, rec, options
228
+ when :textfield
229
+ render_textfield form, rec, options
230
+ when :textarea
231
+ render_textarea form, rec, options
232
+ when :filefield
233
+ render_filefield form, rec, options
234
+ when :hidden
235
+ render_hidden form, rec, options
236
+ else
237
+ raise "Unkown control type #{@widget_config.type}"
238
+ end
239
+ end
240
+
241
+ private
242
+
243
+ def render_select form, rec, options
244
+ form.select @widget_config.name, @widget_config.values, {}, options
245
+ end
246
+
247
+ def render_collection_select form, rec, options
248
+ form.collection_select @widget_config.name,
249
+ @widget_config.values,
250
+ @widget_config.model_method,
251
+ @widget_config.text_method,
252
+ {},
253
+ options.merge({ :multiple => true })
254
+ end
255
+
256
+ def render_autocomplete form, rec, options
257
+ t = rec.class.name.underscore
258
+ #id = "#{t}_#{@widget_config.name.to_s}"
259
+ id = options[:id] || form.tag_id(@widget_config.name)
260
+ name = @widget_config.name.to_s.foreign_key.to_sym
261
+ opt = {}
262
+ opt[:id] = "dialog_#{form.tag_id name}" if @dialog
263
+ s = form.hidden_field name, opt
264
+ s += form.text_field @widget_config.name, options.merge(:size => (@widget_config.size || 40))
265
+ s += @view.content_tag("div", "", :id => "#{id}_auto_complete", :class => "auto_complete")
266
+ s += @view.auto_complete_field(id,
267
+ :url => @view.send(@widget_config.url),
268
+ :method => :get,
269
+ :param_name => :autocomplete_query,
270
+ :select => 'autocomplete_name',
271
+ :after_update_element => 'auto_complete_update_hidden')
272
+ s
273
+ end
274
+
275
+ def render_radio form, rec, options
276
+ s = ''
277
+ @widget_config.values.each do |val|
278
+ s += form.radio_button @widget_config.name, val[1].to_s, options
279
+ s += @view.content_tag('span', val[0])
280
+ end
281
+ s
282
+ end
283
+
284
+ def render_checkbox form, rec, options
285
+ form.check_box @widget_config.name, options
286
+ end
287
+
288
+ def render_date form, rec, options
289
+ form.calendar_date_select @widget_config.name, options.merge(:size => (@widget_config.size || 15))
290
+ end
291
+
292
+ def render_textfield form, rec, options
293
+ val = value(rec) #rec.send @widget_config.name
294
+ text = val.to_s
295
+ text = val.to_s(:local_date) if val.instance_of? Date
296
+ text = val.to_s(:local_time) if val.instance_of? Time
297
+ text = val.to_s(:local_datetime) if val.instance_of? ActiveSupport::TimeWithZone
298
+ form.text_field @widget_config.name, options.merge({:size => (@widget_config.size || 40), :value => text})
299
+ end
300
+
301
+ def render_textarea form, rec, options
302
+ size = @widget_config.size || '40x10'
303
+ #val = rec.send @widget_config.name
304
+ #if val
305
+ # row = 0
306
+ # col = 0
307
+ # val.each_line do |line|
308
+ # row = row.succ
309
+ # col = [col, line.length].max
310
+ # end
311
+ # size = "#{col}x#{row}"
312
+ #end
313
+ form.text_area @widget_config.name, options.merge({:size => size})
314
+ end
315
+
316
+ def render_filefield form, rec, options
317
+ form.file_field @widget_config.name, options
318
+ end
319
+
320
+ def render_hidden form, rec, options
321
+ form.hidden_field @widget_config.name, options.merge({:value => value(rec)})
322
+ end
323
+
324
+ end
325
+
326
+ class ActionRenderer < Renderer
327
+
328
+ def label
329
+ if @widget_config.label
330
+ @widget_config.label
331
+ else
332
+ t = @widget_config.name.to_s.split '_'
333
+ l = @view.send(:_, t[0].humanize)
334
+ if t.size > 1 # e.g. new_group
335
+ model_name = t[1..-1].join '_'
336
+ l += ' ' + @view.send(:s_, model_name.humanize.downcase)
337
+ end
338
+ l
339
+ end
340
+ end
341
+
342
+ def render_action rec, context, current_action = nil
343
+ t = @widget_config.name.to_s.split '_'
344
+ action = t[0]
345
+ foreign_action = t.size > 1
346
+ if foreign_action # e.g. new_group
347
+ model_name = t[1..-1].join '_'
348
+ else
349
+ model_name = @widget_config.model_name
350
+ return nil if action == current_action
351
+ end
352
+ css_class = @widget_config.active == 1 ? 'enabled ' : 'disabled '
353
+ css_class += @widget_config.css_class || action
354
+
355
+ if @widget_config.active == 1
356
+ case action
357
+ when 'separator'
358
+ @widget_config.name if context
359
+ when 'new'
360
+ # copy the _id params to save the context
361
+ pars = {}
362
+ pars.merge! context if context
363
+ # delete self-reference
364
+ pars.delete model_name + '_id'
365
+ # set the object as parameter
366
+ pars[rec.class.name.underscore + '_id'] = rec #if foreign_action
367
+ url = @view.send("#{action}_#{model_name}_path".to_sym, pars)
368
+ if !@widget_config.direct_link && (context || foreign_action)
369
+ @view.link_to_remote label, { :url => url, :method => :get }, {:class => css_class }
370
+ else
371
+ @view.link_to label, url, {:class => css_class}
372
+ end
373
+ when 'show'
374
+ @view.link_to label, @view.polymorphic_path(rec), :class => css_class
375
+ when 'destroy'
376
+ msg = _('Are you sure?') #@view.send(:_, 'Are you sure?')
377
+ if context
378
+ url = @view.polymorphic_path(rec)
379
+ @view.link_to_remote label, { :url => url, :method => :delete, :confirm => msg }, {:class => css_class }
380
+ else
381
+ @view.link_to label, rec, :confirm => msg, :method => :delete, :class => css_class
382
+ end
383
+ when 'list'
384
+ @view.link_to label, @view.send("#{model_name.pluralize}_path".to_sym), :class => css_class
385
+ when 'edit'
386
+ url = @view.edit_polymorphic_path rec
387
+ if context
388
+ @view.link_to_remote label, { :url => url, :method => :get }, {:class => css_class }
389
+ else
390
+ @view.link_to label, url
391
+ end
392
+ else
393
+ url = @widget_config.url || @view.formatted_polymorphic_path([rec, action]) #eval("@view.#{action}_#{model_name}_path(rec)")
394
+ if context
395
+ if @widget_config.direct_link
396
+ @view.link_to label, url, :class => css_class
397
+ else
398
+ @view.link_to_remote label, { :url => url, :method => :get }, {:class => css_class }
399
+ end
400
+ else
401
+ @view.link_to label, url
402
+ end
403
+ end # case
404
+ else # if disabled
405
+ if context
406
+ @view.content_tag :a, label, :href => '#', :class => css_class
407
+ else
408
+ label
409
+ end
410
+ end
411
+ end
412
+ end
413
+ end
@@ -0,0 +1,85 @@
1
+ require 'gettext_rails'
2
+ require File.expand_path("../dirs", File.dirname(__FILE__))
3
+ require File.dirname(__FILE__) << '/config'
4
+ require File.dirname(__FILE__) << '/rendering'
5
+ module Toolbox
6
+ module Searching
7
+
8
+ def self.setup #:nodoc:
9
+ ActionController::Base.send(:extend, ControllerClassMethods)
10
+ ActionController::Base.send(:include, ControllerInstanceMethods)
11
+ ActionView::Base.send(:include, HelperMethods)
12
+ end
13
+
14
+ # Creates a SQL where condition with the given filter and a list of fields
15
+ # filter: the text to search for. Separated multiple value by space
16
+ # widgets_list: an instance of Toolbox::WidgetList. See ControllerClassMethods::search_fields
17
+ # for more information about the syntax.
18
+ # Example:
19
+ # search_field :firstname, :lastname, :address
20
+ # create_search_condition('dav ny')
21
+ # -> (firstname like 'dav' or lastname like 'dav' or address like 'dav')
22
+ # and (firstname like 'ny' or lastname like 'ny' or address like 'ny')
23
+ def self.create_search_condition(filter, widgets_list)
24
+ raise 'widgets_list are not of type Toolbox:WidgetList' unless widgets_list.is_a? Toolbox::WidgetList
25
+ cond_template = []
26
+ widgets_list.widgets.each do |w|
27
+ table = w.model_name.tableize
28
+ col_name = w.name
29
+ a = col_name.to_s.split('.')
30
+ if a.length > 1
31
+ table = a[-2].tableize
32
+ col_name = a[-1]
33
+ end
34
+ cond_template << "#{table}.#{col_name} like ? "
35
+ end
36
+ cond_template = '(' + cond_template.join(' or ') + ')'
37
+ cond = Array.new
38
+ cond.push('')
39
+ filter.squeeze(' ').split(' ').each do |text|
40
+ text = '%' + text + '%'
41
+ cond[0] += ' and ' if !cond[0].empty?
42
+ cond[0] += cond_template
43
+ cond_template.count('?').times do cond.push(text) end
44
+ end
45
+ # in case of empty search text
46
+ cond[0] = '1=1' if cond.length == 1
47
+ cond
48
+ end
49
+
50
+ module ControllerClassMethods
51
+ # Defines the search fields
52
+ # Example:
53
+ # search_field :firstname, :lastname, :address
54
+ # If symbols are given, the model is determined using the controllers name.
55
+ # If you want to search in associated tables, use this format:
56
+ # search_fields :firstname, :lastname, 'orders.desc'
57
+ def search_fields *fields
58
+ write_inheritable_attribute('search_fields', Toolbox::WidgetList.new(controller_name.singularize, fields, Toolbox::FieldConfig))
59
+ end
60
+
61
+ end
62
+
63
+ module ControllerInstanceMethods
64
+
65
+ # Returns the fields (array) defined with Toolbox::Searching::ControllerClassMethods::search_fields for this controller.
66
+ def search_fields
67
+ self.class.read_inheritable_attribute('search_fields') || nil
68
+ end
69
+
70
+ end
71
+
72
+ module HelperMethods
73
+ include GetText
74
+ bindtextdomain('toolbox', :path => Toolbox::Dirs::LOCALE)
75
+
76
+ def search_field_hint widgets_list
77
+ labels = []
78
+ widgets_list.widgets.each {|w| labels << Toolbox::Renderer.new(self, w).label(controller.model_name) }
79
+
80
+ _('Search in this fields:') + ' ' + labels.join(', ')
81
+ end
82
+ end
83
+ end
84
+
85
+ end
@@ -0,0 +1,63 @@
1
+ require 'gettext_rails'
2
+
3
+ module Toolbox
4
+ module SessionParams
5
+
6
+ def self.setup #:nodoc:
7
+ ActionController::Base.send(:extend, ClassMethods)
8
+ ActionController::Base.send(:include, InstanceMethods)
9
+
10
+ end
11
+
12
+ module ClassMethods
13
+
14
+ # Makes the given parameters "persistend' (per controller).
15
+ # The listed parameters will be stored in the session.
16
+ # In a before filter, the params hash is set with the value from
17
+ # the session, if (and only if) the parameter does not exist in the session.
18
+ # Otherwise the session value is updated.
19
+ def session_params *params
20
+ write_inheritable_attribute('session_params', params)
21
+ end
22
+ end
23
+
24
+ module InstanceMethods #:nodoc:all
25
+
26
+ def self.included(base)
27
+ base.send :before_filter, :set_session_params
28
+ base.send :after_filter, :update_session_params
29
+ end
30
+
31
+ private
32
+
33
+ # update the parameter hash from the session
34
+ def set_session_params
35
+ if self.class.read_inheritable_attribute('session_params')
36
+ create_session_params
37
+ session[:session_params][controller_name].each_key do |field|
38
+ params[field] = session[:session_params][controller_name][field] unless params[field]
39
+ end
40
+ end
41
+ end
42
+ # update the session from the params hash
43
+ def update_session_params
44
+ if p = self.class.read_inheritable_attribute('session_params')
45
+ create_session_params
46
+ p.each do |field|
47
+ if params[field]
48
+ session[:session_params][controller_name][field] = params[field]
49
+ else
50
+ session[:session_params][controller_name].delete(field)
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ def create_session_params
57
+ session[:session_params] = {} unless session[:session_params]
58
+ session[:session_params][controller_name] = {} unless session[:session_params][controller_name]
59
+ end
60
+ end
61
+ end
62
+
63
+ end
@@ -0,0 +1,74 @@
1
+ require File.dirname(__FILE__) << '/config'
2
+ module Toolbox
3
+
4
+ module Sorting
5
+
6
+ def self.quote_order_by(table, field)
7
+ table = ActiveRecord::Base.connection.quote_table_name(table)
8
+ field = ActiveRecord::Base.connection.quote_column_name(field)
9
+ "#{table}.#{field}"
10
+ end
11
+
12
+ # Create an oder-by string
13
+ # Parameters
14
+ # - prefix: an optional prefix used to get the right sort-field from the params hash
15
+ # - params: the parameter hash
16
+ # - widgets: an array of field configuration
17
+ def self.order_by(params, widgets, prefix = nil)
18
+ val = params[params_key(prefix)]
19
+ return nil unless val # no sort parameter set
20
+ field, dir = val.split '-'
21
+ dir = fix_direction dir
22
+ return nil if dir == 'none' # no sort direction
23
+ # find the field in field list
24
+ field = widgets.select{ |w| w == field }.first
25
+ order_by_string = nil
26
+ if field
27
+ if field.order_by
28
+ order_by_string = field.order_by
29
+ order_by_string = order_by_string.join(" #{dir}, ") if order_by_string.is_a? Array
30
+ else
31
+ order_by_string = quote_order_by(field.model_name.tableize, field.name.to_s)
32
+ end
33
+ order_by_string += " #{dir}"
34
+ end
35
+ order_by_string
36
+ end
37
+
38
+ # inhibit any sql injection
39
+ def self.fix_direction dir #:nodoc:
40
+ dir == 'asc' ? 'asc' : (dir == 'desc' ? 'desc' : 'none')
41
+ end
42
+
43
+ # Get the current sorting direction from the given params hash
44
+ def self.current(params, prefix, field_name) #:nodoc:
45
+ dir = nil
46
+ if val = params[params_key(prefix)]
47
+ f, d = val.split '-'
48
+ dir = fix_direction(d) if f == field_name
49
+ end
50
+ dir
51
+ end
52
+
53
+ # Gets a parameter hash for link generation
54
+ def self.params(params, prefix, field_name, new_dir) #:nodoc:
55
+ if new_dir
56
+ params.merge({params_key(prefix) => "#{field_name}-#{new_dir}"})
57
+ else
58
+ params.delete params_key(prefix)
59
+ params
60
+ end
61
+ end
62
+
63
+ # Get the sort-key for the params hash regarding the prefix
64
+ def self.params_key(prefix) #:nodoc:
65
+ if prefix
66
+ "#{prefix}-sort".to_sym
67
+ else
68
+ :sort
69
+ end
70
+ end
71
+
72
+
73
+ end
74
+ end
@@ -0,0 +1,4 @@
1
+
2
+ module Toolbox
3
+ VERSION = '0.1.0'
4
+ end
data/lib/toolbox.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'gettext'
2
+ require File.dirname(__FILE__) << '/toolbox/searching'
3
+ require File.dirname(__FILE__) << '/toolbox/sorting'
4
+ require File.dirname(__FILE__) << '/toolbox/session_params'
5
+ require File.dirname(__FILE__) << '/toolbox/helpers'
6
+ require File.dirname(__FILE__) << '/toolbox/default_controller'
7
+ require File.dirname(__FILE__) << '/toolbox/config'
8
+ require File.dirname(__FILE__) << '/toolbox/version'
9
+ require File.dirname(__FILE__) << '/dirs'
10
+
11
+ if Object.const_defined?(:Rails) && File.directory?(Rails.root.to_s + "/public")
12
+
13
+ Toolbox::Searching.setup
14
+ Toolbox::SessionParams.setup
15
+ Toolbox::DefaultController.setup
16
+ ActionController::Base.send(:append_view_path, Toolbox::Dirs::VIEW)
17
+
18
+ # install files
19
+ unless File.exists?(RAILS_ROOT + "/public/javascripts/toolbox-#{Toolbox::VERSION}/toolbox.js")
20
+ ['/public/javascripts', '/public/stylesheets', '/public/images'].each do |dir|
21
+ source = File.join(Toolbox::Dirs::GEM_ROOT, dir)
22
+ appendix = dir.ends_with?('javascripts') ? "toolbox-#{Toolbox::VERSION}" : 'toolbox'
23
+ dest = File.join(RAILS_ROOT, dir, appendix)
24
+ FileUtils.mkdir_p(dest)
25
+ FileUtils.cp(Dir.glob(source + '/*.*'), dest)
26
+ end
27
+ end
28
+ end
29
+
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file