datagrid 0.9.0 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -13,6 +13,7 @@ Ruby library that helps you to build and represent table-like data with:
13
13
  * ActiveRecord
14
14
  * Mongoid
15
15
  * MongoMapper
16
+ * Array (slow but possible)
16
17
 
17
18
  [Create an issue](https://github.com/bogdan/datagrid/issues/new) if you want more.
18
19
 
@@ -40,8 +41,8 @@ class SimpleReport
40
41
  filter(:disabled, :eboolean)
41
42
  filter(:confirmed, :boolean)
42
43
  filter(:group_id, :integer, :multiple => true)
43
- filter(:logins_count, :integer, :range => true)
44
- filter(:group_name, :string, :header => "Group") do |value|
44
+ filter(:logins_count, :integer, :range => true, :before => :group_id)
45
+ filter(:group_name, :string, :header => "Group", :after => :category_id) do |value|
45
46
  self.joins(:group).where(:groups => {:name => value})
46
47
  end
47
48
 
@@ -129,7 +130,7 @@ Datagrid supports different type of filters including:
129
130
  Each column is represented by name and code block to calculate the value.
130
131
 
131
132
  ``` ruby
132
- column(:activated, :header => "Active", :order => "activated") do
133
+ column(:activated, :header => "Active", :order => "activated", :after => :name) do
133
134
  self.activated?
134
135
  end
135
136
  ```
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.0
1
+ 0.9.2
@@ -1,5 +1,5 @@
1
1
  <tr>
2
- <% grid.columns(*options[:columns]).each do |column| %>
2
+ <% grid.html_columns(*options[:columns]).each do |column| %>
3
3
  <th class="<%= datagrid_column_classes(grid, column) %>">
4
4
  <%= column.header %>
5
5
  <%= datagrid_order_for(grid, column) if column.order && options[:order]%>
@@ -1,5 +1,5 @@
1
1
  <tr class="<%= options[:cycle] && cycle(*options[:cycle]) %>">
2
- <% grid.columns(*options[:columns]).each do |column| %>
2
+ <% grid.html_columns(*options[:columns]).each do |column| %>
3
3
  <td class="<%= datagrid_column_classes(grid, column) %>"><%= datagrid_format_value(grid, column, asset) %></td>
4
4
  <% end %>
5
5
  </tr>
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "datagrid"
8
- s.version = "0.9.0"
8
+ s.version = "0.9.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Bogdan Gusiev"]
12
- s.date = "2013-06-06"
12
+ s.date = "2013-08-23"
13
13
  s.description = "This allows you to easily build datagrid aka data tables with sortable columns and filters"
14
14
  s.email = "agresso@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -17,6 +17,7 @@ module Datagrid
17
17
  end
18
18
 
19
19
  module ClassMethods
20
+ # Adds a filter that acts like a column selection
20
21
  def column_names_filter
21
22
  filter(:column_names, :enum, :select => proc { |grid| grid.class.columns.map {|c| [c.header, c.name] }}, :multiple => true ) do |value|
22
23
  scoped
@@ -24,7 +25,7 @@ module Datagrid
24
25
  end
25
26
  end
26
27
 
27
- def columns(*args)
28
+ def columns(*args) #:nodoc:
28
29
  options = args.extract_options!
29
30
  column_names = selected_column_names(*args)
30
31
  column_names << options
@@ -32,17 +32,36 @@ module Datagrid
32
32
  args.compact!
33
33
  args.map!(&:to_sym)
34
34
  columns_array.select do |column|
35
- (!options[:data] || column.data?) && (args.empty? || args.include?(column.name))
35
+ (!options[:data] || column.data?) && (!options[:html] || column.html?)&& (args.empty? || args.include?(column.name))
36
36
  end
37
37
  end
38
38
 
39
39
  # Defines new datagrid column
40
+ #
41
+ # Arguments:
42
+ #
43
+ # * <tt>name</tt> - column name
44
+ # * <tt>options</tt> - hash of options
45
+ # * <tt>block</tt> - proc to calculate a column value
46
+ #
47
+ # Available options:
48
+ #
49
+ # * <tt>:html</tt> - determines if current column should be present in html table and how is it formatted
50
+ # * <tt>:order</tt> - determines if this column could be sortable and how
51
+ # * <tt>:order_desc</tt> - determines a descending order for given column (only in case when <tt>:order</tt> can not be easily inverted
52
+ # * <tt>:url</tt> - a proc with one argument, pass this option to easily convert the value into an URL
53
+ # * <tt>:before</tt> - determines the position of this column, by adding it before the column passed here
54
+ # * <tt>:after</tt> - determines the position of this column, by adding it after the column passed here
55
+ #
56
+ # See: https://github.com/bogdan/datagrid/wiki/Columns for examples
40
57
  def column(name, options = {}, &block)
41
58
  check_scope_defined!("Scope should be defined before columns")
42
59
  block ||= lambda do |model|
43
60
  model.send(name)
44
61
  end
45
- columns_array << Datagrid::Columns::Column.new(self, name, options, &block)
62
+ position = Datagrid::Utils.extract_position_from_options(columns_array, options)
63
+ column = Datagrid::Columns::Column.new(self, name, options, &block)
64
+ columns_array.insert(position, column)
46
65
  end
47
66
 
48
67
  # Returns column definition with given name
@@ -52,6 +71,30 @@ module Datagrid
52
71
  end
53
72
  end
54
73
 
74
+ # Returns an array of all defined column names
75
+ def column_names
76
+ columns.map(&:name)
77
+ end
78
+
79
+ def respond_to(&block) #:nodoc:
80
+ Datagrid::Columns::Column::ResponseFormat.new(&block)
81
+ end
82
+
83
+ def format(value, &block)
84
+ if block_given?
85
+ respond_to do |f|
86
+ f.data { value }
87
+ f.html do
88
+ instance_exec(value, &block)
89
+ end
90
+ end
91
+ else
92
+ # Ruby Object#format exists.
93
+ # We don't want to change the behaviour and overwrite it.
94
+ super
95
+ end
96
+ end
97
+
55
98
  def inherited(child_class) #:nodoc:
56
99
  super(child_class)
57
100
  child_class.columns_array = self.columns_array.clone
@@ -62,14 +105,22 @@ module Datagrid
62
105
  module InstanceMethods
63
106
 
64
107
  # Returns <tt>Array</tt> of human readable column names. See also "Localization" section
108
+ #
109
+ # Arguments:
110
+ #
111
+ # * <tt>column_names</tt> - list of column names if you want to limit data only to specified columns
65
112
  def header(*column_names)
66
113
  data_columns(*column_names).map(&:header)
67
114
  end
68
115
 
69
116
  # Returns <tt>Array</tt> column values for given asset
117
+ #
118
+ # Arguments:
119
+ #
120
+ # * <tt>column_names</tt> - list of column names if you want to limit data only to specified columns
70
121
  def row_for(asset, *column_names)
71
122
  data_columns(*column_names).map do |column|
72
- column.value(asset, self)
123
+ column.data_value(asset, self)
73
124
  end
74
125
  end
75
126
 
@@ -77,12 +128,16 @@ module Datagrid
77
128
  def hash_for(asset)
78
129
  result = {}
79
130
  self.data_columns.each do |column|
80
- result[column.name] = column.value(asset, self)
131
+ result[column.name] = column.data_value(asset, self)
81
132
  end
82
133
  result
83
134
  end
84
135
 
85
136
  # Returns Array of Arrays with data for each row in datagrid assets without header.
137
+ #
138
+ # Arguments:
139
+ #
140
+ # * <tt>column_names</tt> - list of column names if you want to limit data only to specified columns
86
141
  def rows(*column_names)
87
142
  #TODO: find in batches
88
143
  self.assets.map do |asset|
@@ -91,12 +146,30 @@ module Datagrid
91
146
  end
92
147
 
93
148
  # Returns Array of Arrays with data for each row in datagrid assets with header.
94
- def data
95
- self.rows.unshift(self.header)
149
+ #
150
+ # Arguments:
151
+ #
152
+ # * <tt>column_names</tt> - list of column names if you want to limit data only to specified columns
153
+ def data(*column_names)
154
+ self.rows(*column_names).unshift(self.header(*column_names))
96
155
  end
97
156
 
98
157
  # Return Array of Hashes where keys are column names and values are column values
99
158
  # for each row in datagrid <tt>#assets</tt>
159
+ #
160
+ # Example:
161
+ #
162
+ # class MyGrid
163
+ # scope { Model }
164
+ # column(:id)
165
+ # column(:name)
166
+ # end
167
+ #
168
+ # Model.create!(:name => "One")
169
+ # Model.create!(:name => "Two")
170
+ #
171
+ # MyGrid.new.data_hash # => [{:name => "One"}, {:name => "Two"}]
172
+ #
100
173
  def data_hash
101
174
  self.assets.map do |asset|
102
175
  hash_for(asset)
@@ -139,12 +212,13 @@ module Datagrid
139
212
  #
140
213
  # MyGrid.new.columns # => all defined columns
141
214
  # grid = MyGrid.new(:column_names => [:id, :name])
142
- # grid.columns # => id and name columsn
215
+ # grid.columns # => id and name columns
143
216
  # grid.columns(:id, :category) # => id and category column
144
217
  def columns(*args)
145
218
  self.class.columns(*args)
146
219
  end
147
220
 
221
+ # Returns all columns that can be represented in plain data(non-html) way
148
222
  def data_columns(*names)
149
223
  options = names.extract_options!
150
224
  options[:data] = true
@@ -152,9 +226,28 @@ module Datagrid
152
226
  self.columns(*names)
153
227
  end
154
228
 
229
+ # Returns all columns that can be represented in HTML table
230
+ def html_columns(*names)
231
+ options = names.extract_options!
232
+ options[:html] = true
233
+ names << options
234
+ self.columns(*names)
235
+ end
236
+
237
+ # Finds a column by name
155
238
  def column_by_name(name)
156
239
  self.class.column_by_name(name)
157
240
  end
241
+
242
+
243
+ def format(value, &block)
244
+ if block_given?
245
+ self.class.format(value, &block)
246
+ else
247
+ super
248
+ end
249
+ end
250
+
158
251
  end # InstanceMethods
159
252
 
160
253
  end
@@ -1,41 +1,52 @@
1
1
  class Datagrid::Columns::Column
2
2
 
3
- attr_accessor :grid, :options, :block, :name, :html_block
3
+ class ResponseFormat # :nodoc:
4
4
 
5
- def initialize(grid, name, options = {}, &block)
6
- self.grid = grid
5
+ attr_accessor :data_block, :html_block
6
+
7
+ def initialize
8
+ yield(self)
9
+ end
10
+
11
+ def data(&block)
12
+ self.data_block = block
13
+ end
14
+
15
+ def html(&block)
16
+ self.html_block = block
17
+ end
18
+
19
+ def data_value
20
+ data_block.call
21
+ end
22
+
23
+ def html_value(context)
24
+ context.instance_eval(&html_block)
25
+ end
26
+ end
27
+
28
+ attr_accessor :grid_class, :options, :data_block, :name, :html_block
29
+
30
+ def initialize(grid_class, name, options = {}, &block)
31
+ self.grid_class = grid_class
7
32
  self.name = name.to_sym
8
33
  self.options = options
9
34
  if options[:html] == true
10
35
  self.html_block = block
11
36
  else
37
+ self.data_block = block
38
+
12
39
  if options[:html].is_a? Proc
13
40
  self.html_block = options[:html]
14
41
  end
15
- self.block = block
16
- end
17
- if format
18
- ::Datagrid::Utils.warn_once(":format column option is deprecated. Use :url or :html option instead.")
19
42
  end
20
43
  end
21
44
 
22
- def value(model, grid)
23
- value_for(model, grid)
45
+ def data_value(model, grid)
46
+ result = generic_value(model,grid)
47
+ result.is_a?(ResponseFormat) ? result.data_value : result
24
48
  end
25
49
 
26
- def value_for(model, grid)
27
- if self.block.arity == 1
28
- self.block.call(model)
29
- elsif self.block.arity == 2
30
- self.block.call(model, grid)
31
- else
32
- model.instance_eval(&self.block)
33
- end
34
- end
35
-
36
- def format
37
- self.options[:format]
38
- end
39
50
 
40
51
  def label
41
52
  self.options[:label]
@@ -43,14 +54,14 @@ class Datagrid::Columns::Column
43
54
 
44
55
  def header
45
56
  self.options[:header] ||
46
- I18n.translate(self.name, :scope => "datagrid.#{self.grid.param_name}.columns", :default => self.name.to_s.humanize )
57
+ I18n.translate(self.name, :scope => "datagrid.#{self.grid_class.param_name}.columns", :default => self.name.to_s.humanize )
47
58
  end
48
59
 
49
60
  def order
50
61
  if options.has_key?(:order)
51
62
  self.options[:order]
52
63
  else
53
- grid.driver.default_order(grid.scope, name)
64
+ grid_class.driver.default_order(grid_class.scope, name)
54
65
  end
55
66
  end
56
67
 
@@ -60,11 +71,50 @@ class Datagrid::Columns::Column
60
71
  end
61
72
 
62
73
  def html?
63
- self.html_block != nil
74
+ options[:html] != false
64
75
  end
65
76
 
66
77
  def data?
67
- self.block != nil
78
+ self.data_block != nil
79
+ end
80
+
81
+ def html_value(context, asset, grid)
82
+ if html? && html_block
83
+ value_from_html_block(context, asset, grid)
84
+ else
85
+ result = generic_value(asset,grid)
86
+ result.is_a?(ResponseFormat) ? result.html_value(context) : result
87
+ end
88
+ end
89
+
90
+ def value_from_html_block(context, asset, grid)
91
+ args = []
92
+ remaining_arity = html_block.arity
93
+
94
+ if data?
95
+ args << data_value(asset,grid)
96
+ remaining_arity -= 1
97
+ end
98
+
99
+ args << asset if remaining_arity > 0
100
+ args << grid if remaining_arity > 1
101
+
102
+ return context.instance_exec(*args, &html_block)
103
+ end
104
+
105
+ def block
106
+ Datagrid::Utils.warn_once("Datagrid::Columns::Column#block is deprecated. Use #html_block or #data_block instead")
107
+ data_block
108
+ end
109
+
110
+ def generic_value(model, grid)
111
+ if self.data_block.arity == 1
112
+ self.data_block.call(model)
113
+ elsif self.data_block.arity == 2
114
+ self.data_block.call(model, grid)
115
+ else
116
+ model.instance_eval(&self.data_block)
117
+ end
68
118
  end
69
119
 
70
120
  end
@@ -16,7 +16,7 @@ module Datagrid
16
16
 
17
17
  module ClassMethods
18
18
 
19
- def datagrid_attribute(name, &block)
19
+ def datagrid_attribute(name, &block) #:nodoc:
20
20
  unless datagrid_attributes.include?(name)
21
21
  block ||= lambda do |value|
22
22
  value
@@ -32,6 +32,7 @@ module Datagrid
32
32
  end
33
33
  end
34
34
 
35
+ # Defines a scope at class level
35
36
  def scope(&block)
36
37
  if block
37
38
  self.scope_value = block
@@ -41,7 +42,7 @@ module Datagrid
41
42
  end
42
43
  end
43
44
 
44
- def driver
45
+ def driver #:nodoc:
45
46
  @driver ||= Drivers::AbstractDriver.guess_driver(scope).new
46
47
  end
47
48
 
@@ -60,12 +61,16 @@ module Datagrid
60
61
 
61
62
  module InstanceMethods
62
63
 
63
- def initialize(attributes = nil)
64
+ def initialize(attributes = nil, &block)
64
65
  super()
65
66
 
66
67
  if attributes
67
68
  self.attributes = attributes
68
69
  end
70
+
71
+ if block_given?
72
+ self.scope_value = block
73
+ end
69
74
  end
70
75
 
71
76
  def attributes
@@ -88,11 +93,14 @@ module Datagrid
88
93
  driver.to_scope(scope)
89
94
  end
90
95
 
91
- def attributes=(attributes)
96
+
97
+ def assign_attributes(attributes)
92
98
  attributes.each do |name, value|
93
99
  self[name] = value
94
100
  end
101
+ self
95
102
  end
103
+ alias attributes= assign_attributes
96
104
 
97
105
  def as_query
98
106
  attributes = self.attributes.clone
@@ -111,17 +119,19 @@ module Datagrid
111
119
  if block_given?
112
120
  self.scope_value = block
113
121
  self
122
+ elsif scope_value
123
+ scope_value.call
114
124
  else
115
125
  check_scope_defined!
116
126
  scope_value.call
117
127
  end
118
128
  end
119
129
 
120
- def driver
130
+ def driver #:nodoc:
121
131
  self.class.driver
122
132
  end
123
133
 
124
- def check_scope_defined!(message = nil)
134
+ def check_scope_defined!(message = nil) #:nodoc:
125
135
  self.class.send :check_scope_defined!, message
126
136
  end
127
137
 
@@ -46,6 +46,25 @@ module Datagrid
46
46
  end
47
47
  end
48
48
 
49
+ # Defines new datagrid filter
50
+ #
51
+ # Arguments:
52
+ #
53
+ # * <tt>name</tt> - filter name
54
+ # * <tt>options</tt> - hash of options
55
+ # * <tt>block</tt> - proc to apply the filter
56
+ #
57
+ # Available options:
58
+ #
59
+ # * <tt>:header</tt> - determines the header of the filter
60
+ # * <tt>:default</tt> - the default filter value
61
+ # * <tt>:multiple</tt> - determines if more than one option can be selected
62
+ # * <tt>:allow_nil</tt> - determines if the value can be nil
63
+ # * <tt>:allow_blank</tt> - determines if the value can be blank
64
+ # * <tt>:before</tt> - determines the position of this filter, by adding it before the filter passed here (when using datagrid_form_for helper)
65
+ # * <tt>:after</tt> - determines the position of this filter, by adding it after the filter passed here (when using datagrid_form_for helper)
66
+ #
67
+ # See: https://github.com/bogdan/datagrid/wiki/Columns for examples
49
68
  def filter(attribute, *args, &block)
50
69
  options = args.extract_options!
51
70
  type = args.shift || :default
@@ -53,9 +72,9 @@ module Datagrid
53
72
  klass = type.is_a?(Class) ? type : FILTER_TYPES[type]
54
73
  raise ConfigurationError, "filter class #{type.inspect} not found" unless klass
55
74
 
56
-
75
+ position = Datagrid::Utils.extract_position_from_options(self.filters, options)
57
76
  filter = klass.new(self, attribute, options, &block)
58
- self.filters << filter
77
+ self.filters.insert(position, filter)
59
78
 
60
79
  datagrid_attribute(attribute) do |value|
61
80
  filter.parse_values(value)
@@ -97,6 +116,11 @@ module Datagrid
97
116
  self[filter.name]
98
117
  end
99
118
 
119
+ # Returns filter object with the given name
120
+ def filter_by_name(name)
121
+ self.class.filter_by_name(name)
122
+ end
123
+
100
124
  end # InstanceMethods
101
125
 
102
126
  end
@@ -3,10 +3,10 @@ end
3
3
 
4
4
  class Datagrid::Filters::BaseFilter
5
5
 
6
- attr_accessor :grid, :options, :block, :name
6
+ attr_accessor :grid_class, :options, :block, :name
7
7
 
8
8
  def initialize(grid_class, name, options = {}, &block)
9
- self.grid = grid_class
9
+ self.grid_class = grid_class
10
10
  self.name = name
11
11
  self.options = options
12
12
  self.block = block || default_filter_block
@@ -33,7 +33,7 @@ class Datagrid::Filters::BaseFilter
33
33
 
34
34
  def parse_values(value)
35
35
  if !self.multiple && value.is_a?(Array)
36
- raise Datagrid::ArgumentError, "#{grid}##{name} filter can not accept Array argument. Use :multiple option."
36
+ raise Datagrid::ArgumentError, "#{grid_class}##{name} filter can not accept Array argument. Use :multiple option."
37
37
  end
38
38
  values = Array.wrap(value)
39
39
  values.map! do |v|
@@ -44,7 +44,7 @@ class Datagrid::Filters::BaseFilter
44
44
 
45
45
  def header
46
46
  options[:header] ||
47
- I18n.translate(self.name, :scope => "datagrid.#{grid.param_name}.filters", :default => self.name.to_s.humanize)
47
+ I18n.translate(self.name, :scope => "datagrid.#{grid_class.param_name}.filters", :default => self.name.to_s.humanize)
48
48
  end
49
49
 
50
50
  def default
@@ -34,7 +34,7 @@ class Datagrid::Filters::DateFilter < Datagrid::Filters::BaseFilter
34
34
  end
35
35
 
36
36
  def format(value)
37
- if formats.any?
37
+ if formats.any? && value
38
38
  value.strftime(formats.first)
39
39
  else
40
40
  super
@@ -11,11 +11,13 @@ class Datagrid::Filters::EnumFilter < Datagrid::Filters::BaseFilter
11
11
  end
12
12
 
13
13
  def select(object = nil)
14
- option = self.options[:select]
15
- if option.respond_to?(:call)
16
- option.arity == 1 ? option.call(object) : option.call
14
+ select = self.options[:select]
15
+ if select.is_a?(Symbol)
16
+ object.send(select)
17
+ elsif select.respond_to?(:call)
18
+ select.arity == 1 ? select.call(object) : select.call
17
19
  else
18
- option
20
+ select
19
21
  end
20
22
  end
21
23
 
@@ -94,13 +94,13 @@ module Datagrid
94
94
  end
95
95
 
96
96
  def datagrid_get_attribute(attribute_or_filter)
97
- attribute_or_filter.is_a?(Symbol) ? attribute_or_filter : attribute_or_filter.name
97
+ Utils.string_like?(attribute_or_filter) ? attribute_or_filter : attribute_or_filter.name
98
98
  end
99
99
 
100
100
  def datagrid_get_filter(attribute_or_filter)
101
- if attribute_or_filter.is_a?(Symbol)
101
+ if Utils.string_like?(attribute_or_filter)
102
102
  object.class.filter_by_name(attribute_or_filter) ||
103
- raise(Error, "filter #{attribute_or_filter} not found")
103
+ raise(Error, "Datagrid filter #{attribute_or_filter} not found")
104
104
  else
105
105
  attribute_or_filter
106
106
  end
@@ -21,8 +21,6 @@ module Datagrid
21
21
  # * <tt>:html</tt> - hash of attributes for <table> tag
22
22
  # * <tt>:order</tt> - If false do not generate ordering controlls.
23
23
  # Default: true.
24
- # * <tt>:cycle</tt> - Used as arguments for cycle for each row.
25
- # Default: false. Example: <tt>["odd", "even"]</tt>.
26
24
  # * <tt>:columns</tt> - Array of column names to display.
27
25
  # Used in case when same grid class is used in different places
28
26
  # and needs different columns. Default: all defined columns.
@@ -57,7 +55,7 @@ module Datagrid
57
55
  datagrid_renderer.form_for(grid, options)
58
56
  end
59
57
 
60
- # Provides access to datagrid column data.
58
+ # Provides access to datagrid columns data.
61
59
  #
62
60
  # <%= datagrid_row(grid, user) do |row| %>
63
61
  # <tr>
@@ -66,7 +64,7 @@ module Datagrid
66
64
  # </tr>
67
65
  # <% end %>
68
66
  #
69
- # Used in case you want to build datagrid table completelly manually
67
+ # Used in case you want to build html table completelly manually
70
68
  def datagrid_row(grid, asset, &block)
71
69
  HtmlRow.new(self, grid, asset).tap do |row|
72
70
  if block_given?
@@ -1,7 +1,7 @@
1
1
  require "action_view"
2
2
 
3
3
  module Datagrid
4
- class Renderer
4
+ class Renderer #:nodoc:
5
5
 
6
6
  def self.for(template)
7
7
  new(template)
@@ -16,33 +16,13 @@ module Datagrid
16
16
  column = grid.column_by_name(column)
17
17
  end
18
18
 
19
- value = if column.html?
20
- args = []
21
- remaining_arity = column.html_block.arity
22
-
23
- if column.data?
24
- args << column.value(asset,grid)
25
- remaining_arity -= 1
26
- end
27
-
28
- args << asset if remaining_arity > 0
29
- args << grid if remaining_arity > 1
30
-
31
- @template.instance_exec(*args, &column.html_block)
32
- else
33
- column.value(asset,grid)
34
- end
19
+ value = column.html_value(@template, asset, grid)
35
20
 
36
21
  url = column.options[:url] && column.options[:url].call(asset)
37
22
  if url
38
23
  @template.link_to(value, url)
39
24
  else
40
- case column.format
41
- when :url
42
- @template.link_to(column.label ? asset.send(column.label) : I18n.t("datagrid.table.url_label", :default => "URL"), value)
43
- else
44
- _safe(value)
45
- end
25
+ _safe(value)
46
26
  end
47
27
  end
48
28
 
@@ -57,10 +37,13 @@ module Datagrid
57
37
  options = args.extract_options!
58
38
  options[:html] ||= {}
59
39
  options[:html][:class] ||= "datagrid #{html_class(grid)}"
40
+ if options[:cycle]
41
+ ::Datagrid::Utils.warn_once("datagrid_table cycle option is deprecated. Use css to stylee odd/even rows instead.")
42
+ end
60
43
  assets = args.any? ? args.shift : grid.assets
61
44
  paginate = options[:paginate]
62
45
  if paginate
63
- ::Datagrid::Utils.warn_once(":paginate option is deprecated. Looks to https://github.com/bogdan/datagrid/wiki/Frontend.")
46
+ ::Datagrid::Utils.warn_once(":paginate option is deprecated. Look to https://github.com/bogdan/datagrid/wiki/Frontend.")
64
47
  assets = assets.paginate(paginate)
65
48
  end
66
49
 
@@ -32,7 +32,20 @@ module Datagrid
32
32
  options
33
33
  end
34
34
 
35
+ def string_like?(value)
36
+ value.is_a?(Symbol) || value.is_a?(String)
37
+ end
35
38
 
39
+ def extract_position_from_options(array, options)
40
+ position = options.extract!(:before, :after)
41
+ if position[:before]
42
+ array.index {|c| c.name.to_sym == position[:before].to_sym }
43
+ elsif position[:after]
44
+ array.index {|c| c.name.to_sym == position[:after].to_sym } + 1
45
+ else
46
+ -1
47
+ end
48
+ end
36
49
  end
37
50
  end
38
51
  end
@@ -17,17 +17,40 @@ describe Datagrid::Columns do
17
17
  :confirmed => false,
18
18
  :category => "first",
19
19
  :access_level => 'admin',
20
- :pet => 'rottweiler'
20
+ :pet => 'rottweiler',
21
+ :shipping_date => Date.new(2013, 8, 1)
21
22
  ) }
23
+ let(:date) { Date.new(2013, 8, 1) }
22
24
 
23
25
  it "should have data columns without html columns" do
24
26
  subject.data_columns.size.should == subject.columns.size - 1
25
27
  end
26
28
  it "should build rows of data" do
27
- subject.rows.should == [["Pop", "Star", "admin", "ROTTWEILER"]]
29
+ subject.rows.should == [[date, "Pop", "Star", "admin", "ROTTWEILER"]]
28
30
  end
29
31
  it "should generate header" do
30
- subject.header.should == ["Group", "Name", "Access level", "Pet"]
32
+ subject.header.should == ["Shipping date", "Group", "Name", "Access level", "Pet"]
33
+ end
34
+
35
+ it "should return html_columns" do
36
+ report = test_report do
37
+ scope {Entry}
38
+ column(:id)
39
+ column(:name, :html => false)
40
+ end
41
+ report.html_columns.map(&:name).should == [:id]
42
+ end
43
+
44
+ it "should return html_columns when column definition has 2 arguments" do
45
+ report = test_report(:name => "Hello") do
46
+ scope {Entry}
47
+ filter(:name)
48
+ column(:id)
49
+ column(:name, :html => false) do |model, grid|
50
+ "'#{model.name}' filtered by '#{grid.name}'"
51
+ end
52
+ end
53
+ report.row_for(Entry.create!(:name => "Hello World")).should == [8, "'Hello World' filtered by 'Hello'"]
31
54
  end
32
55
 
33
56
  it "should generate table data" do
@@ -42,12 +65,13 @@ describe Datagrid::Columns do
42
65
  :group => "Pop",
43
66
  :name => "Star",
44
67
  :access_level => 'admin',
45
- :pet => 'ROTTWEILER'
68
+ :pet => 'ROTTWEILER',
69
+ :shipping_date => date
46
70
  }
47
71
  end
48
72
 
49
73
  it "should support csv export" do
50
- subject.to_csv.should == "Group,Name,Access level,Pet\nPop,Star,admin,ROTTWEILER\n"
74
+ subject.to_csv.should == "Shipping date,Group,Name,Access level,Pet\n#{date},Pop,Star,admin,ROTTWEILER\n"
51
75
  end
52
76
 
53
77
  it "should support csv export of particular columns" do
@@ -55,7 +79,7 @@ describe Datagrid::Columns do
55
79
  end
56
80
 
57
81
  it "should support csv export options" do
58
- subject.to_csv(:col_sep => ";").should == "Group;Name;Access level;Pet\nPop;Star;admin;ROTTWEILER\n"
82
+ subject.to_csv(:col_sep => ";").should == "Shipping date;Group;Name;Access level;Pet\n#{date};Pop;Star;admin;ROTTWEILER\n"
59
83
  end
60
84
  end
61
85
 
@@ -121,4 +145,19 @@ describe Datagrid::Columns do
121
145
 
122
146
  end
123
147
 
148
+
149
+ context "when grid has formatted column" do
150
+ it "should output correct data" do
151
+ report = test_report do
152
+ scope {Entry}
153
+ column(:name) do |entry|
154
+ format(entry.name) do |value|
155
+ "<strong>#{value}</strong"
156
+ end
157
+ end
158
+ end
159
+ Entry.create!(:name => "Hello World")
160
+ report.rows.should == [["Hello World"]]
161
+ end
162
+ end
124
163
  end
@@ -1,15 +1,39 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Datagrid::Core do
4
-
5
- it "should support change scope on the fly" do
6
- report = test_report do
7
- scope { Entry }
4
+
5
+ context 'with 2 persisted entries' do
6
+ before { 2.times { Entry.create } }
7
+
8
+ let(:limit) { Entry.limit(1) }
9
+ let(:report_class) do
10
+ test_report_class do
11
+ scope { Entry }
12
+ end
8
13
  end
9
- report.scope do
10
- Entry.limit(1)
14
+
15
+ describe '#scope' do
16
+ context 'in the class' do
17
+ let(:report) { report_class.new }
18
+
19
+ it { expect(report.scope).to have(2).item }
20
+ end
21
+
22
+ context 'changes scope on the fly' do
23
+ let(:report) do
24
+ report_class.new.tap do |r|
25
+ r.scope { limit }
26
+ end
27
+ end
28
+
29
+ it { expect(report.scope).to have(1).item }
30
+ end
31
+
32
+ context 'changes scope by initializer' do
33
+ let(:report) { report_class.new { limit } }
34
+
35
+ it { expect(report.scope).to have(1).item }
36
+ end
11
37
  end
12
- 2.times { Entry.create }
13
- report.assets.to_a.size.should == 1
14
38
  end
15
39
  end
@@ -33,4 +33,17 @@ describe Datagrid::Filters::EnumFilter do
33
33
  end
34
34
  end
35
35
 
36
+
37
+ it "should support select given as symbol" do
38
+ report = test_report do
39
+ scope {Entry}
40
+ filter(:group_id, :enum, :select => :selectable_group_ids)
41
+ def selectable_group_ids
42
+ [1,3,5]
43
+ end
44
+ end
45
+
46
+ report.filter_by_name(:group_id).select(report).should == [1,3,5]
47
+ end
48
+
36
49
  end
@@ -127,6 +127,29 @@ describe Datagrid::Filters do
127
127
  end
128
128
  grid.assets.should_not be_empty
129
129
  end
130
-
130
+
131
+ end
132
+
133
+ describe "positioning filter before another" do
134
+ it "should insert the filter before the specified element" do
135
+ grid = test_report do
136
+ scope {Entry}
137
+ filter(:limit)
138
+ filter(:name, :before => :limit)
139
+ end
140
+ grid.filters.index {|f| f.name == :name}.should == 0
141
+ end
142
+ end
143
+
144
+ describe "positioning filter after another" do
145
+ it "should insert the filter before the specified element" do
146
+ grid = test_report do
147
+ scope {Entry}
148
+ filter(:limit)
149
+ filter(:name)
150
+ filter(:group_id, :after => :limit)
151
+ end
152
+ grid.filters.index {|f| f.name == :group_id}.should == 1
153
+ end
131
154
  end
132
155
  end
@@ -163,6 +163,19 @@ describe Datagrid::FormBuilder do
163
163
  '<input class="created_at date_filter to" multiple name="report[created_at][]" size="30" type="text" value="2012-01-01"/>'
164
164
  )}
165
165
  end
166
+ context "with blank range value" do
167
+ around(:each) do |example|
168
+ with_date_format do
169
+ example.run
170
+ end
171
+ end
172
+ let(:_range) { [nil, nil] }
173
+ it { should equal_to_dom(
174
+ '<input class="created_at date_filter from" multiple name="report[created_at][]" size="30" type="text"/>' +
175
+ '<span class="separator date"> - </span>' +
176
+ '<input class="created_at date_filter to" multiple name="report[created_at][]" size="30" type="text"/>'
177
+ )}
178
+ end
166
179
  end
167
180
  context "with enum filter type" do
168
181
  let(:_filter) { :category }
@@ -271,11 +284,21 @@ describe Datagrid::FormBuilder do
271
284
  end
272
285
  end
273
286
  let(:_filter) { :group_id }
274
- it { should equal_to_dom(<<HTML) }
287
+ let(:expected_html) do
288
+ if ActionPack::VERSION::MAJOR == 3 && ActionPack::VERSION::MINOR < 2
289
+ <<-HTML
290
+ <select class="group_id enum_filter" id="report_group_id" multiple name="report[group_id][]">
291
+ <option value="hello">hello</option></select>
292
+ HTML
293
+ else
294
+ <<-HTML
275
295
  <input name="report[group_id][]" type="hidden" value=""><select class="group_id enum_filter" id="report_group_id" multiple name="report[group_id][]">
276
296
  <option value="hello">hello</option></select>
277
- HTML
297
+ HTML
298
+ end
299
+ end
278
300
 
301
+ it { should equal_to_dom(expected_html) }
279
302
  end
280
303
 
281
304
  context "with column names filter" do
@@ -291,11 +314,23 @@ HTML
291
314
  end
292
315
  end
293
316
  let(:_filter) { :column_names }
294
- it { should equal_to_dom(<<HTML)}
317
+ let(:expected_html) do
318
+ if ActionPack::VERSION::MAJOR == 3 && ActionPack::VERSION::MINOR < 2
319
+ <<-HTML
320
+ <select class="column_names enum_filter" id="report_column_names" multiple name="report[column_names][]"><option value="id" selected>Id</option>
321
+ <option value="name" selected>Name</option>
322
+ <option value="category">Category</option></select>
323
+ HTML
324
+ else
325
+ <<-HTML
295
326
  <input name="report[column_names][]" type="hidden" value=""><select class="column_names enum_filter" id="report_column_names" multiple name="report[column_names][]"><option value="id" selected>Id</option>
296
327
  <option value="name" selected>Name</option>
297
328
  <option value="category">Category</option></select>
298
- HTML
329
+ HTML
330
+ end
331
+ end
332
+
333
+ it { should equal_to_dom(expected_html) }
299
334
  end
300
335
  end
301
336
 
@@ -318,7 +353,3 @@ HTML
318
353
  end
319
354
  end
320
355
  end
321
-
322
-
323
-
324
-
@@ -7,13 +7,14 @@ require 'datagrid/renderer'
7
7
  describe Datagrid::Helper do
8
8
  subject do
9
9
  template = ActionView::Base.new
10
+ template.stub(:protect_against_forgery?).and_return(false)
10
11
  template.view_paths << File.expand_path("../../../app/views", __FILE__)
11
12
  template.view_paths << File.expand_path("../../support/test_partials", __FILE__)
12
13
  template
13
14
  end
14
15
 
15
16
  before(:each) do
16
- subject.stub!(:params).and_return({})
17
+ subject.stub(:params).and_return({})
17
18
  subject.stub(:url_for) do |options|
18
19
  options.to_param
19
20
  end
@@ -95,6 +96,7 @@ describe Datagrid::Helper do
95
96
  "table.datagrid td.group" => 0
96
97
  )
97
98
  end
99
+
98
100
  context "with column_names attribute" do
99
101
  let(:grid) do
100
102
  test_report(:column_names => "name") do
@@ -279,6 +281,22 @@ describe Datagrid::Helper do
279
281
  )
280
282
  end
281
283
 
284
+ context "when grid has complicated columns" do
285
+ let(:grid) do
286
+ test_report(:name => 'Hello') do
287
+ scope {Entry}
288
+ filter(:name)
289
+ column(:name) do |model, grid|
290
+ "'#{model.name}' filtered by '#{grid.name}'"
291
+ end
292
+ end
293
+ end
294
+ it "should ignore them" do
295
+ subject.datagrid_rows(grid, [entry]).should match_css_pattern(
296
+ "td.name" => 1
297
+ )
298
+ end
299
+ end
282
300
  end
283
301
 
284
302
  describe ".datagrid_order_for" do
@@ -361,5 +379,16 @@ describe Datagrid::Helper do
361
379
  end
362
380
  subject.datagrid_format_value(report, :name, entry).should == "<b>Star</b>"
363
381
  end
382
+ it "should support format in column" do
383
+ report = test_report do
384
+ scope {Entry}
385
+ column(:name) do |e|
386
+ format(e.name) do |value|
387
+ link_to value, "/profile"
388
+ end
389
+ end
390
+ end
391
+ subject.datagrid_format_value(report, :name, entry).should == "<a href=\"/profile\">Star</a>"
392
+ end
364
393
  end
365
394
  end
@@ -1,16 +1,21 @@
1
1
 
2
2
 
3
3
  def test_report(attributes = {}, &block)
4
- klass = Class.new
5
- klass.class_eval do
6
- include Datagrid
7
- end
8
- if block
9
- klass.class_eval(&block)
10
- end
4
+ klass = test_report_class(&block)
11
5
  klass.new(attributes)
12
6
  end
13
7
 
8
+ def test_report_class(&block)
9
+ Class.new.tap do |klass|
10
+ klass.class_eval do
11
+ include Datagrid
12
+ end
13
+ if block
14
+ klass.class_eval(&block)
15
+ end
16
+ end
17
+ end
18
+
14
19
  class SimpleReport
15
20
 
16
21
  include Datagrid
@@ -40,12 +45,14 @@ class SimpleReport
40
45
  render :partial => "actions", :locals => {:model => model}
41
46
  end
42
47
 
43
- column(:access_level, :html => lambda {|data| content_tag :h1, data})
44
-
45
48
  column(:pet, :html => lambda {|data| content_tag :em, data}) do
46
49
  self.pet.try(:upcase)
47
50
  end
48
51
 
52
+ column(:shipping_date, :before => :group)
53
+
54
+ column(:access_level, :html => lambda {|data| content_tag :h1, data}, :after => :actions)
55
+
49
56
  def param_name
50
57
  :report
51
58
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datagrid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.9.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-06-06 00:00:00.000000000 Z
12
+ date: 2013-08-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -337,7 +337,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
337
337
  version: '0'
338
338
  segments:
339
339
  - 0
340
- hash: 1228935778461264019
340
+ hash: 3747985271557157131
341
341
  required_rubygems_version: !ruby/object:Gem::Requirement
342
342
  none: false
343
343
  requirements: