datagrid 0.9.0 → 0.9.2

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.
@@ -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: