wice_grid 3.4.2 → 3.4.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.
@@ -66,6 +66,28 @@ if defined?(Wice::Defaults)
66
66
  Wice::Defaults::NEGATION_IN_STRING_FILTERS = false
67
67
 
68
68
 
69
+ # Each WiceGrid filter column is defined in two classes, one used for rendering the filter, the other
70
+ # for generating query conditions. All these columns are in lib/wice/columns/*.rb .
71
+ # File lib/wice/columns/column_processor_index.rb lists all predefined processors.
72
+ # In most cases a processor is chosen automatically based on the DB column type,
73
+ # for example, integer columns
74
+ # can have two of processors, the default one with one input field, and a processor called "range",
75
+ # with 2 input fields. In this case it is possible to specify a processor in the column definition:
76
+ #
77
+ # g.column filter_type: :range
78
+ #
79
+ # It is also possible to define you own processors:
80
+ #
81
+ # Wice::Defaults::ADDITIONAL_COLUMN_PROCESSORS = {
82
+ # some_key_identifying_new_column_type: ['AViewColumnProcessorClass', 'ConditionsGeneratorClass'],
83
+ # another_key_identifying_new_column_type: ['AnotherViewColumnProcessorClass', 'AnotherConditionsGeneratorClass']
84
+ # }
85
+ #
86
+ # Column processor keys/names should not coincide with the existing keys/names (see lib/wice/columns/column_processor_index.rb)
87
+ # the value is a 2-element array with 2 strings, the first should be a name of view processor class inherited from
88
+ # Wice::Columns::ViewColumn, the second should be a name of conditions generator class inherited from
89
+ # Wice::Columns::ConditionsGeneratorColumn .
90
+
69
91
 
70
92
  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
71
93
  # Showing All Queries #
data/lib/wice/columns.rb CHANGED
@@ -3,7 +3,10 @@ module Wice #:nodoc:
3
3
 
4
4
  module Columns #:nodoc:
5
5
 
6
+ mattr_reader :handled_type_view, :handled_type_conditions_generator
7
+
6
8
  class << self #:nodoc:
9
+
7
10
  def load_column_processors #:nodoc:
8
11
 
9
12
  require_columns
@@ -12,6 +15,51 @@ module Wice #:nodoc:
12
15
 
13
16
  @@handled_type_view = build_table_of_processors 'view'
14
17
  @@handled_type_conditions_generator = build_table_of_processors 'conditions_generator'
18
+
19
+ if Wice::Defaults.const_defined?(:ADDITIONAL_COLUMN_PROCESSORS)
20
+
21
+ common_error_prefix = "Error loading Wice::Defaults::ADDITIONAL_COLUMN_PROCESSORS. "
22
+
23
+ Wice::Defaults::ADDITIONAL_COLUMN_PROCESSORS.each do |key, value|
24
+ unless key.is_a?(Symbol)
25
+ raise common_error_prefix + "A key of Wice::Defaults::ADDITIONAL_COLUMN_PROCESSORS should be a Symbol!"
26
+ end
27
+
28
+ if @@handled_type_view.has_key?(key)
29
+ raise common_error_prefix +
30
+ "Column with key \"#{key}\" already exists in WiceGrid, overwriting existing columns is forbidden, please choose another key!"
31
+ end
32
+
33
+ if ! value.is_a?(Array) || value.size != 2
34
+ raise common_error_prefix +
35
+ "A value of Wice::Defaults::ADDITIONAL_COLUMN_PROCESSORS should be a a 2-element array!"
36
+ end
37
+
38
+ view_processor, conditions_generator = value.map(&:to_s).map do |klass|
39
+ begin
40
+ eval(klass)
41
+ rescue NameError
42
+ raise common_error_prefix + "Class #{klass} is not defined!"
43
+ end
44
+ end
45
+
46
+ unless view_processor.ancestors.include?(::Wice::Columns::ViewColumn)
47
+ raise common_error_prefix +
48
+ "#{view_processor} should be inherited from Wice::Columns::ViewColumn!"
49
+ end
50
+
51
+
52
+ unless conditions_generator.ancestors.include?(::Wice::Columns::ConditionsGeneratorColumn)
53
+ raise common_error_prefix +
54
+ "#{conditions_generator} should be inherited from Wice::Columns::ConditionsGeneratorColumn!"
55
+ end
56
+
57
+ @@handled_type_view[key] = view_processor
58
+ @@handled_type_conditions_generator[key] = conditions_generator
59
+
60
+ end
61
+ end
62
+
15
63
  end
16
64
 
17
65
  def get_view_column_processor(column_type) #:nodoc:
@@ -88,6 +136,7 @@ module Wice #:nodoc:
88
136
 
89
137
 
90
138
  def add_css_class(klass_value)
139
+ self.html ||= {}
91
140
  if html[:class].nil?
92
141
  html[:class] = klass_value
93
142
  else
@@ -97,7 +146,11 @@ module Wice #:nodoc:
97
146
  end
98
147
 
99
148
  def css_class #:nodoc:
100
- html[:class] || ''
149
+ if html && html[:class]
150
+ html[:class]
151
+ else
152
+ ''
153
+ end
101
154
  end
102
155
 
103
156
  def yield_declaration_of_column_filter #:nodoc:
@@ -109,12 +109,12 @@ module Wice
109
109
  conditions = [[]]
110
110
  if opts[:fr]
111
111
  conditions[0] << " #{@column_wrapper.alias_or_table_name(table_alias)}.#{@column_wrapper.name} >= ? "
112
- conditions << opts[:fr]
112
+ conditions << opts[:fr].to_date
113
113
  end
114
114
 
115
115
  if opts[:to]
116
- conditions[0] << " #{@column_wrapper.alias_or_table_name(table_alias)}.#{@column_wrapper.name} <= ? "
117
- conditions << opts[:to]
116
+ conditions[0] << " #{@column_wrapper.alias_or_table_name(table_alias)}.#{@column_wrapper.name} < ? "
117
+ conditions << (opts[:to].to_date + 1)
118
118
  end
119
119
 
120
120
  return false if conditions.size == 1
@@ -35,6 +35,23 @@ module Wice
35
35
 
36
36
 
37
37
  class ConditionsGeneratorColumnInteger < ConditionsGeneratorColumn #:nodoc:
38
+ def get_op_and_value(val)
39
+ num = nil
40
+ op = nil
41
+
42
+ # remove spaces
43
+ val = val.gsub(' ','')
44
+
45
+ first_digit_or_dot_index = val =~ /[0-9.]/
46
+ if first_digit_or_dot_index
47
+ op = val[0...first_digit_or_dot_index]
48
+ op = '=' if op == ''
49
+ num = Float(val[first_digit_or_dot_index..-1]) rescue nil
50
+
51
+ op = nil unless ['<','>','<=','>=','='].include?(op)
52
+ end
53
+ [op, num]
54
+ end
38
55
 
39
56
  def generate_conditions(table_alias, opts) #:nodoc:
40
57
  unless opts.kind_of? Hash
@@ -43,9 +60,10 @@ module Wice
43
60
  end
44
61
  conditions = [[]]
45
62
  if opts[:eq]
46
- if opts[:eq] =~ /\d/
47
- conditions[0] << " #{@column_wrapper.alias_or_table_name(table_alias)}.#{@column_wrapper.name} = ? "
48
- conditions << opts[:eq]
63
+ op, num = get_op_and_value(opts[:eq])
64
+ if op && num
65
+ conditions[0] << " #{@column_wrapper.alias_or_table_name(table_alias)}.#{@column_wrapper.name} " + op + " ? "
66
+ conditions << num
49
67
  else
50
68
  opts.delete(:eq)
51
69
  end
@@ -62,4 +80,4 @@ module Wice
62
80
  end
63
81
 
64
82
  end
65
- end
83
+ end
@@ -11,6 +11,7 @@ module Wice
11
11
  attr_reader :page_parameter_name
12
12
  attr_reader :after_row_handler
13
13
  attr_reader :before_row_handler
14
+ attr_reader :replace_row_handler
14
15
  attr_reader :blank_slate_handler
15
16
  attr_reader :last_row_handler
16
17
  attr_reader :grid
@@ -311,6 +312,7 @@ module Wice
311
312
  end
312
313
 
313
314
  if options[:class]
315
+ options[:html] ||= {}
314
316
  Wice::WgHash.add_or_append_class_value!(options[:html], options[:class])
315
317
  options.delete(:class)
316
318
  end
@@ -409,6 +411,12 @@ module Wice
409
411
  @before_row_handler = block
410
412
  end
411
413
 
414
+ # Can be used to replace the HTML code (for example to make a multi-column spanning row) of a row.
415
+ # Nothing is replaced if the block return +false+ or +nil+.
416
+ def replace_row(&block)
417
+ @replace_row_handler = block
418
+ end
419
+
412
420
  # Can be used to add HTML code (calculation results, for example) after all rows.
413
421
  # Nothing is added if the block return +false+ or +nil+.
414
422
  def last_row(&block)
@@ -464,16 +472,14 @@ module Wice
464
472
  end
465
473
 
466
474
  new_params[:only_path] = false
467
- base_link_with_pp_info = controller.url_for(new_params).gsub(/\?+$/,'')
475
+ base_link_with_pp_info = omit_empty_query controller.url_for(new_params)
468
476
 
469
477
  if new_params[@grid.name]
470
478
  new_params[@grid.name].delete(:pp) # and reset back to pagination if show all mode is on
471
479
  end
472
- [base_link_with_pp_info, controller.url_for(new_params).gsub(/\?+$/,'')]
480
+ [base_link_with_pp_info, omit_empty_query(controller.url_for(new_params))]
473
481
  end
474
482
 
475
-
476
-
477
483
  def link_for_export(controller, format, extra_parameters = {}) #:nodoc:
478
484
  new_params = Wice::WgHash.deep_clone controller.params
479
485
  new_params.merge!(extra_parameters)
@@ -519,5 +525,15 @@ module Wice
519
525
  method_name ? @columns.select(&method_name) : @columns
520
526
  end
521
527
 
528
+ def omit_empty_query(url) #:nodoc:
529
+ # /foo? --> /foo
530
+ # /foo?grid= --> /foo
531
+ # /foo?grid=&page=1 --> /foo?page=1
532
+ # /foo?grid=some_value --> /foo?grid=some_value
533
+
534
+ empty_hash_rx = Regexp.new "([&?])#{Regexp.escape @grid.name}=([&?]|$)" # c.f., issue #140
535
+ url.gsub(empty_hash_rx, '\1').gsub /\?+$/, ''
536
+ end
537
+
522
538
  end
523
539
  end
@@ -166,6 +166,56 @@ module Wice
166
166
  extra_argument ? block.call(ar, extra_argument) : block.call(ar)
167
167
  end
168
168
 
169
+ def get_row_content(rendering, ar, sorting_dependant_row_cycling)
170
+ cell_value_of_the_ordered_column = nil
171
+ row_content = ''
172
+ rendering.each_column(:in_html) do |column|
173
+ cell_block = column.cell_rendering_block
174
+
175
+ opts = column.html
176
+
177
+ opts = opts ? opts.clone : {}
178
+
179
+ column_block_output = if column.class == Columns.get_view_column_processor(:action)
180
+ cell_block.call(ar, params)
181
+ else
182
+ call_block(cell_block, ar)
183
+ end
184
+
185
+ if column_block_output.kind_of?(Array)
186
+
187
+ unless column_block_output.size == 2
188
+ raise WiceGridArgumentError.new('When WiceGrid column block returns an array it is expected to contain 2 elements only - '+
189
+ 'the first is the contents of the table cell and the second is a hash containing HTML attributes for the <td> tag.')
190
+ end
191
+
192
+ column_block_output, additional_opts = column_block_output
193
+
194
+ unless additional_opts.is_a?(Hash)
195
+ raise WiceGridArgumentError.new('When WiceGrid column block returns an array its second element is expected to be a ' +
196
+ "hash containing HTML attributes for the <td> tag. The returned value is #{additional_opts.inspect}. Read documentation.")
197
+ end
198
+
199
+ additional_css_class = nil
200
+ if additional_opts.has_key?(:class)
201
+ additional_css_class = additional_opts[:class]
202
+ additional_opts.delete(:class)
203
+ elsif additional_opts.has_key?('class')
204
+ additional_css_class = additional_opts['class']
205
+ additional_opts.delete('class')
206
+ end
207
+ opts.merge!(additional_opts)
208
+ Wice::WgHash.add_or_append_class_value!(opts, additional_css_class) unless additional_css_class.blank?
209
+ end
210
+
211
+ if sorting_dependant_row_cycling && column.attribute && grid.ordered_by?(column)
212
+ cell_value_of_the_ordered_column = column_block_output
213
+ end
214
+ row_content += content_tag(:td, column_block_output, opts)
215
+ end
216
+ [row_content, cell_value_of_the_ordered_column]
217
+ end
218
+
169
219
  # the longest method? :(
170
220
  def grid_html(grid, options, rendering, reuse_last_column_for_filter_buttons) #:nodoc:
171
221
 
@@ -192,7 +242,7 @@ module Wice
192
242
  # Ruby 1.9.x
193
243
  grid.output_buffer.force_encoding('UTF-8') if grid.output_buffer.respond_to?(:force_encoding)
194
244
 
195
- grid.output_buffer << %!<div class="wice-grid-container" id="#{grid.name}"><div id="#{grid.name}_title">!
245
+ grid.output_buffer << %!<div class="wice-grid-container" data-grid-name="#{grid.name}" id="#{grid.name}"><div id="#{grid.name}_title">!
196
246
  grid.output_buffer << content_tag(:h3, grid.saved_query.name) if grid.saved_query
197
247
  grid.output_buffer << "</div><table #{tag_options(table_html_attrs, true)}>"
198
248
  grid.output_buffer << "<caption>#{rendering.kaption}</caption>" if rendering.kaption
@@ -241,6 +291,13 @@ module Wice
241
291
 
242
292
  column_name = column.name
243
293
 
294
+ opts = column.html
295
+
296
+ opts = opts ? opts.clone : {}
297
+
298
+ Wice::WgHash.add_or_append_class_value!(opts, column.css_class)
299
+
300
+
244
301
  if column.attribute && column.ordering
245
302
 
246
303
  column.add_css_class('active-filter') if grid.filtered_by?(column)
@@ -258,7 +315,7 @@ module Wice
258
315
  rendering.column_link(column, direction, params, options[:extra_request_parameters]),
259
316
  :class => link_style)
260
317
 
261
- grid.output_buffer << content_tag(:th, col_link, Wice::WgHash.make_hash(:class, column.css_class))
318
+ grid.output_buffer << content_tag(:th, col_link, opts)
262
319
 
263
320
  else
264
321
  if reuse_last_column_for_filter_buttons && last
@@ -266,7 +323,7 @@ module Wice
266
323
  hide_show_icon(filter_row_id, grid, filter_shown, no_filter_row, options[:show_filters], rendering)
267
324
  )
268
325
  else
269
- grid.output_buffer << content_tag(:th, column_name)
326
+ grid.output_buffer << content_tag(:th, column_name, opts)
270
327
  end
271
328
  end
272
329
  end
@@ -299,23 +356,27 @@ module Wice
299
356
  grid.output_buffer << '>'
300
357
 
301
358
  rendering.each_column_aware_of_one_last_one(:in_html) do |column, last|
359
+
360
+ opts = column.html ? column.html.clone : {}
361
+ Wice::WgHash.add_or_append_class_value!(opts, column.css_class)
362
+
302
363
  if column.filter_shown?
303
364
 
304
365
  filter_html_code = column.render_filter.html_safe
305
366
  if column.detach_with_id
306
- grid.output_buffer << content_tag(:th, '', Wice::WgHash.make_hash(:class, column.css_class))
367
+ grid.output_buffer << content_tag(:th, '', opts)
307
368
  grid.output_buffer.add_filter(column.detach_with_id, filter_html_code)
308
369
  else
309
- grid.output_buffer << content_tag(:th, filter_html_code, Wice::WgHash.make_hash(:class, column.css_class))
370
+ grid.output_buffer << content_tag(:th, filter_html_code, opts)
310
371
  end
311
372
  else
312
373
  if reuse_last_column_for_filter_buttons && last
313
374
  grid.output_buffer << content_tag(:th,
314
375
  reset_submit_buttons(options, grid, rendering),
315
- Wice::WgHash.add_or_append_class_value!(Wice::WgHash.make_hash(:class, column.css_class), 'filter_icons')
376
+ Wice::WgHash.add_or_append_class_value!(opts, 'filter_icons')
316
377
  )
317
378
  else
318
- grid.output_buffer << content_tag(:th, '', Wice::WgHash.make_hash(:class, column.css_class))
379
+ grid.output_buffer << content_tag(:th, '', opts)
319
380
  end
320
381
  end
321
382
  end
@@ -359,48 +420,19 @@ module Wice
359
420
  nil
360
421
  end
361
422
 
362
- row_content = ''
363
- rendering.each_column(:in_html) do |column|
364
- cell_block = column.cell_rendering_block
365
-
366
- opts = column.html.clone
367
-
368
- column_block_output = if column.class == Columns.get_view_column_processor(:action)
369
- cell_block.call(ar, params)
370
- else
371
- call_block(cell_block, ar)
372
- end
373
-
374
- if column_block_output.kind_of?(Array)
375
-
376
- unless column_block_output.size == 2
377
- raise WiceGridArgumentError.new('When WiceGrid column block returns an array it is expected to contain 2 elements only - '+
378
- 'the first is the contents of the table cell and the second is a hash containing HTML attributes for the <td> tag.')
379
- end
380
-
381
- column_block_output, additional_opts = column_block_output
382
-
383
- unless additional_opts.is_a?(Hash)
384
- raise WiceGridArgumentError.new('When WiceGrid column block returns an array its second element is expected to be a ' +
385
- "hash containing HTML attributes for the <td> tag. The returned value is #{additional_opts.inspect}. Read documentation.")
386
- end
387
-
388
- additional_css_class = nil
389
- if additional_opts.has_key?(:class)
390
- additional_css_class = additional_opts[:class]
391
- additional_opts.delete(:class)
392
- elsif additional_opts.has_key?('class')
393
- additional_css_class = additional_opts['class']
394
- additional_opts.delete('class')
395
- end
396
- opts.merge!(additional_opts)
397
- Wice::WgHash.add_or_append_class_value!(opts, additional_css_class) unless additional_css_class.blank?
398
- end
423
+ replace_row_output = if rendering.replace_row_handler
424
+ call_block(rendering.replace_row_handler, ar, number_of_columns_for_extra_rows)
425
+ else
426
+ nil
427
+ end
399
428
 
400
- if sorting_dependant_row_cycling && column.attribute && grid.ordered_by?(column)
401
- cell_value_of_the_ordered_column = column_block_output
402
- end
403
- row_content += content_tag(:td, column_block_output, opts)
429
+ row_content = if replace_row_output
430
+ no_rightmost_column = true
431
+ replace_row_output
432
+ else
433
+ row_content, tmp_cell_value_of_the_ordered_column = get_row_content(rendering,ar,sorting_dependant_row_cycling)
434
+ cell_value_of_the_ordered_column = tmp_cell_value_of_the_ordered_column if tmp_cell_value_of_the_ordered_column
435
+ row_content
404
436
  end
405
437
 
406
438
  row_attributes = rendering.get_row_attributes(ar)
@@ -0,0 +1,172 @@
1
+ # encoding: UTF-8
2
+ module Wice
3
+ module MemoryAdapter
4
+ class MemoryAdapter
5
+ def initialize(rows, columns, kaminarafy = true)
6
+ @rows = rows
7
+ @klass = MemoryAdapterKlass.new(columns, self)
8
+ @columns = Set.new
9
+ columns.each do |col|
10
+ @columns << col.name.to_sym
11
+ end
12
+ @page_num = 1
13
+ @per_page = 20
14
+ end
15
+
16
+ def klass
17
+ @klass
18
+ end
19
+
20
+ def total_count
21
+ length
22
+ end
23
+
24
+ def length
25
+ @rows.length
26
+ end
27
+
28
+ # 1-based page number
29
+ def current_page
30
+ @page_num
31
+ end
32
+
33
+ def limit_value
34
+ @per_page
35
+ end
36
+
37
+ def total_pages
38
+ total_pages=(length.to_f / @per_page).ceil
39
+ total_pages
40
+ end
41
+
42
+ # 0-based index into array
43
+ def offset_value
44
+ @page_num = total_pages if current_page > total_pages
45
+
46
+ offset_value = (current_page-1) * @per_page
47
+ offset_value
48
+ end
49
+
50
+ def num_pages
51
+ total_pages
52
+ end
53
+
54
+ def last_page?
55
+ last_page = current_page==total_pages
56
+ last_page
57
+ end
58
+
59
+ def includes(*opts)
60
+ self
61
+ end
62
+
63
+ def joins(*opts)
64
+ self
65
+ end
66
+
67
+ def order(*opts)
68
+ self
69
+ end
70
+
71
+ def where(*opts)
72
+ self
73
+ end
74
+
75
+ def page(num)
76
+ @page_num=num.to_i
77
+ @page_num =1 if @page_num < 1
78
+ self
79
+ end
80
+
81
+ def per(num)
82
+ @per_page=num.to_i
83
+ @per_page = 1 if @per_page < 1
84
+ self
85
+ end
86
+
87
+ def each
88
+ start_index = offset_value
89
+ end_index = offset_value + @per_page
90
+ slice = @rows[start_index...end_index]
91
+ if slice
92
+ slice.each do |row|
93
+ yield Row.new(row, self)
94
+ end
95
+ end
96
+ end
97
+
98
+ def has_column?(col)
99
+ @columns.include?(col)
100
+ end
101
+ end
102
+
103
+ class MemoryAdapterKlass
104
+ def initialize(columns, memory_adapter)
105
+ @columns = columns
106
+ @memory_adapter = memory_adapter
107
+ end
108
+
109
+ def columns
110
+ @columns
111
+ end
112
+
113
+ def table_name
114
+ SecureRandom.hex
115
+ end
116
+
117
+ def merge_conditions(*conditions)
118
+ ""
119
+ end
120
+
121
+ def unscoped(&code)
122
+ #@memory_adapter.unscope
123
+ code.call
124
+ self
125
+ end
126
+
127
+ def connection
128
+ end
129
+ end
130
+
131
+ class Row
132
+ def initialize(row, memory_adapter)
133
+ @row=row
134
+ @memory_adapter = memory_adapter
135
+ end
136
+
137
+ def method_missing(m, *args, &block)
138
+ if @memory_adapter.has_column?(m)
139
+ @row[m]
140
+ else
141
+ super
142
+ end
143
+ end
144
+ end
145
+
146
+ class Column
147
+ def initialize(name)
148
+ @name = name.to_s
149
+ @model = Model.new
150
+ end
151
+
152
+ def name
153
+ @name
154
+ end
155
+
156
+ def model
157
+ @model
158
+ end
159
+
160
+ def model=(model)
161
+ @model=model
162
+ end
163
+
164
+ def type
165
+ :string
166
+ end
167
+ end
168
+
169
+ class Model
170
+ end
171
+ end
172
+ end