wice_grid 3.4.2 → 3.4.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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