datagrid 0.9.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/Readme.markdown +6 -4
  2. data/VERSION +1 -1
  3. data/app/assets/stylesheets/datagrid.css.sass +132 -0
  4. data/app/views/datagrid/_form.html.erb +5 -2
  5. data/app/views/datagrid/_order_for.html.erb +2 -2
  6. data/app/views/datagrid/_table.html.erb +1 -1
  7. data/datagrid.gemspec +10 -3
  8. data/lib/datagrid.rb +1 -0
  9. data/lib/datagrid/column_names_attribute.rb +38 -7
  10. data/lib/datagrid/columns.rb +38 -4
  11. data/lib/datagrid/columns/column.rb +29 -1
  12. data/lib/datagrid/drivers/abstract_driver.rb +8 -0
  13. data/lib/datagrid/drivers/active_record.rb +29 -1
  14. data/lib/datagrid/drivers/array.rb +14 -2
  15. data/lib/datagrid/drivers/mongo_mapper.rb +8 -0
  16. data/lib/datagrid/drivers/mongoid.rb +9 -1
  17. data/lib/datagrid/filters.rb +24 -6
  18. data/lib/datagrid/filters/base_filter.rb +42 -14
  19. data/lib/datagrid/filters/boolean_enum_filter.rb +1 -1
  20. data/lib/datagrid/filters/dynamic_filter.rb +57 -0
  21. data/lib/datagrid/filters/enum_filter.rb +4 -21
  22. data/lib/datagrid/filters/select_options.rb +26 -0
  23. data/lib/datagrid/form_builder.rb +41 -8
  24. data/lib/datagrid/helper.rb +2 -1
  25. data/lib/datagrid/i18n.rb +0 -0
  26. data/lib/datagrid/locale/en.yml +28 -0
  27. data/lib/datagrid/ordering.rb +33 -19
  28. data/lib/datagrid/utils.rb +8 -9
  29. data/spec/datagrid/column_names_attribute_spec.rb +44 -1
  30. data/spec/datagrid/columns_spec.rb +16 -0
  31. data/spec/datagrid/filters/dynamic_filter_spec.rb +37 -0
  32. data/spec/datagrid/filters/integer_filter_spec.rb +18 -0
  33. data/spec/datagrid/filters/string_filter_spec.rb +25 -0
  34. data/spec/datagrid/filters_spec.rb +15 -1
  35. data/spec/datagrid/form_builder_spec.rb +83 -0
  36. data/spec/datagrid/helper_spec.rb +1 -0
  37. data/spec/datagrid/ordering_spec.rb +41 -1
  38. data/spec/datagrid/utils_spec.rb +7 -2
  39. metadata +11 -4
@@ -57,6 +57,7 @@ module Datagrid
57
57
 
58
58
  # Provides access to datagrid columns data.
59
59
  #
60
+ # # Suppose that <tt>grid</tt> has first_name and last_name columns
60
61
  # <%= datagrid_row(grid, user) do |row| %>
61
62
  # <tr>
62
63
  # <td><%= row.first_name %></td>
@@ -73,7 +74,7 @@ module Datagrid
73
74
  end
74
75
  end
75
76
 
76
- class HtmlRow
77
+ class HtmlRow #:nodoc:
77
78
  def initialize(context, grid, asset)
78
79
  @context = context
79
80
  @grid = grid
File without changes
@@ -0,0 +1,28 @@
1
+ en:
2
+ datagrid:
3
+ no_results:
4
+ "&mdash;&mdash;"
5
+ table:
6
+ order:
7
+ asc: "&uarr;"
8
+ desc: "&darr;"
9
+ form:
10
+ search: "Search"
11
+ reset: "Reset"
12
+ filters:
13
+ integer:
14
+ range_separator:
15
+ "<span class=\"separator integer\"> - </span>"
16
+ date:
17
+ range_separator:
18
+ "<span class=\"separator date\"> - </span>"
19
+
20
+ eboolean:
21
+ "yes": "Yes"
22
+ "no": "No"
23
+ dynamic:
24
+ operations:
25
+ ">=": ">="
26
+ "<=": "<="
27
+ "=": "="
28
+ "=~": "=~"
@@ -17,10 +17,8 @@ module Datagrid
17
17
  unless column
18
18
  order_unsupported(value, "no column #{value} in #{self.class}")
19
19
  end
20
- unless column.order
21
- order_unsupported(
22
- name, "#{self.class}##{name} don't support order"
23
- )
20
+ unless column.supports_order?
21
+ order_unsupported(column.name, "column don't support order" )
24
22
  end
25
23
  value
26
24
  else
@@ -49,25 +47,41 @@ module Datagrid
49
47
  module InstanceMethods
50
48
 
51
49
  def assets # :nodoc:
52
- result = super
53
- if order
54
- column = column_by_name(order)
55
- result = apply_order(result, column)
56
- end
57
- result
50
+ apply_order(super)
51
+ end
52
+
53
+ # Returns a column definition that is currently used to order assets
54
+ #
55
+ # class MyGrid
56
+ # scope { Model }
57
+ # column(:id)
58
+ # column(:name)
59
+ # end
60
+ # MyGrid.new(:order => "name").order_column # => #<Column name: "name", ...>
61
+ #
62
+ def order_column
63
+ column_by_name(order)
58
64
  end
59
65
 
60
66
  private
61
67
 
62
- def apply_order(assets, column)
63
- if descending?
64
- if column.order_desc
65
- apply_asc_order(assets, column.order_desc)
66
- else
67
- apply_desc_order(assets, column.order)
68
+ def apply_order(assets)
69
+ return assets unless order
70
+ if order_column.order_by_value?
71
+ assets = assets.sort_by do |asset|
72
+ order_column.order_by_value(asset, self)
68
73
  end
74
+ descending? ? assets.reverse : assets
69
75
  else
70
- apply_asc_order(assets, column.order)
76
+ if descending?
77
+ if order_column.order_desc
78
+ apply_asc_order(assets, order_column.order_desc)
79
+ else
80
+ apply_desc_order(assets, order_column.order)
81
+ end
82
+ else
83
+ apply_asc_order(assets, order_column.order)
84
+ end
71
85
  end
72
86
  end
73
87
 
@@ -90,7 +104,7 @@ module Datagrid
90
104
  def reverse_order(assets)
91
105
  driver.reverse_order(assets)
92
106
  rescue NotImplementedError
93
- self.class.order_unsupported("Your ORM do not support reverse order: please specify :order_desc option manually")
107
+ self.class.order_unsupported(order_column.name, "Your ORM do not support reverse order: please specify :order_desc option manually")
94
108
  end
95
109
 
96
110
  def apply_block_order(assets, order)
@@ -100,7 +114,7 @@ module Datagrid
100
114
  when 1
101
115
  order.call(assets)
102
116
  else
103
- self.class.order_unsupported("Order option proc can not handle more than one argument")
117
+ self.class.order_unsupported(order_column.name, "Order option proc can not handle more than one argument")
104
118
  end
105
119
  end
106
120
  end # InstanceMethods
@@ -1,5 +1,5 @@
1
- module Datagrid
2
- module Utils
1
+ module Datagrid
2
+ module Utils # :nodoc:
3
3
  class << self
4
4
 
5
5
 
@@ -9,14 +9,13 @@ module Datagrid
9
9
  TRUTH.include?(value)
10
10
  end
11
11
 
12
- def warn_once(message)
12
+ def warn_once(message, delay = 5)
13
13
  @warnings ||= {}
14
- if @warnings[message]
15
- false
16
- else
17
- warn message
18
- @warnings[message] = true
19
- end
14
+ timestamp = @warnings[message]
15
+ return false if timestamp && timestamp >= Time.now - delay
16
+ warn message
17
+ @warnings[message] = Time.now
18
+ true
20
19
  end
21
20
 
22
21
  def add_html_classes(options, *classes)
@@ -2,6 +2,49 @@ require "spec_helper"
2
2
 
3
3
  describe Datagrid::ColumnNamesAttribute do
4
4
 
5
- describe ".column_names_filter" do
5
+ let(:column_names_filter_options) do
6
+ {}
7
+ end
8
+
9
+ let(:report) do
10
+ options = column_names_filter_options
11
+ test_report do
12
+ scope { Entry }
13
+ column_names_filter(options)
14
+ column(:id)
15
+ column(:name, :mandatory => true)
16
+ column(:category)
17
+ end
18
+ end
19
+ subject { report }
20
+
21
+
22
+ let!(:entry) do
23
+ Entry.create!(:name => 'hello', :category => 'greeting')
24
+ end
25
+
26
+ it "should work" do
27
+ subject.column_names = [:id]
28
+ subject.mandatory_columns.map(&:name).should == [:name]
29
+ subject.optional_columns.map(&:name).should == [:id, :category]
30
+ subject.data.should == [["Id", "Name"], [entry.id, "hello"]]
31
+ columns_filter = subject.filter_by_name(:column_names)
32
+ columns_filter.should_not be_nil
33
+ columns_filter.select(subject).should == [["Id", :id], ["Category", :category]]
34
+ end
35
+
36
+ it "should show only mandatory columns by default" do
37
+ subject.row_for(entry).should == [ "hello" ]
38
+ subject.column_names = ["name", "category"]
39
+ subject.row_for(entry).should == ["hello", "greeting"]
40
+ end
41
+
42
+
43
+ context "when default option is passed to column_names_filter" do
44
+ let(:column_names_filter_options) do
45
+ { :default => [:id] }
46
+ end
47
+ its(:data) { should == [["Id", "Name"], [entry.id, 'hello']] }
48
+
6
49
  end
7
50
  end
@@ -161,4 +161,20 @@ describe Datagrid::Columns do
161
161
  report.rows.should == [["Hello World"]]
162
162
  end
163
163
  end
164
+
165
+ describe ".default_column_options" do
166
+ it "should pass default options to each column definition" do
167
+ report = test_report do
168
+ scope {Entry}
169
+ self.default_column_options = {:order => false}
170
+ column(:id)
171
+ column(:name, :order => "name")
172
+ end
173
+ first = Entry.create(:name => '1st')
174
+ second = Entry.create(:name => '2nd')
175
+ proc { report.attributes = {:order => :id} }.should raise_error(Datagrid::OrderUnsupported)
176
+ report.attributes = {:order => :name, :descending => true}
177
+ report.assets.should == [second, first]
178
+ end
179
+ end
164
180
  end
@@ -0,0 +1,37 @@
1
+ require "spec_helper"
2
+
3
+
4
+ describe Datagrid::Filters::DynamicFilter do
5
+ let(:report) do
6
+ test_report do
7
+ scope {Entry}
8
+ filter(:condition, :dynamic)
9
+ end
10
+ end
11
+
12
+ it "should support = operation" do
13
+ report.condition = [:name, "=", "hello"]
14
+ report.assets.should include(Entry.create!(:name => 'hello'))
15
+ report.assets.should_not include(Entry.create!(:name => 'bye'))
16
+ end
17
+ it "should blank value" do
18
+ report.condition = [:name, "=", ""]
19
+ report.assets.should include(Entry.create!(:name => 'hello'))
20
+ end
21
+ it "should support =~ operation" do
22
+ report.condition = [:name, "=~", "ell"]
23
+ report.assets.should include(Entry.create!(:name => 'hello'))
24
+ report.assets.should_not include(Entry.create!(:name => 'bye'))
25
+ end
26
+ it "should support >= operation" do
27
+ report.condition = [:group_id, ">=", 2]
28
+ report.assets.should include(Entry.create!(:group_id => 3))
29
+ report.assets.should_not include(Entry.create!(:group_id => 1))
30
+ end
31
+ it "should support <= operation" do
32
+ report.condition = [:group_id, "<=", 2]
33
+ report.assets.should include(Entry.create!(:group_id => 1))
34
+ report.assets.should_not include(Entry.create!(:group_id => 3))
35
+ end
36
+
37
+ end
@@ -103,5 +103,23 @@ describe Datagrid::Filters::IntegerFilter do
103
103
  report.assets.should include(Entry.create!(:group => Group.create!(:rating => 5)))
104
104
  end
105
105
 
106
+ it "should support multiple values" do
107
+ report = test_report(:group_id => "1,2") do
108
+ scope {Entry}
109
+ filter(:group_id, :string, :multiple => true)
110
+ end
111
+ report.assets.should include(Entry.create!( :group_id => 1))
112
+ report.assets.should include(Entry.create!( :group_id => 2))
113
+ report.assets.should_not include(Entry.create!( :group_id => 3))
114
+ end
115
+ it "should support custom separator multiple values" do
116
+ report = test_report(:group_id => "1|2") do
117
+ scope {Entry}
118
+ filter(:group_id, :string, :multiple => '|')
119
+ end
120
+ report.assets.should include(Entry.create!( :group_id => 1))
121
+ report.assets.should include(Entry.create!( :group_id => 2))
122
+ report.assets.should_not include(Entry.create!( :group_id => 3))
123
+ end
106
124
 
107
125
  end
@@ -0,0 +1,25 @@
1
+ require "spec_helper"
2
+
3
+
4
+ describe Datagrid::Filters::StringFilter do
5
+
6
+ it "should support multiple values" do
7
+ report = test_report(:name => "one,two") do
8
+ scope {Entry}
9
+ filter(:name, :string, :multiple => true)
10
+ end
11
+ report.assets.should include(Entry.create!( :name => "one"))
12
+ report.assets.should include(Entry.create!( :name => "two"))
13
+ report.assets.should_not include(Entry.create!( :name => "three"))
14
+ end
15
+ it "should support custom separator multiple values" do
16
+ report = test_report(:name => "one,1|two,2") do
17
+ scope {Entry}
18
+ filter(:name, :string, :multiple => '|')
19
+ end
20
+ report.assets.should include(Entry.create!( :name => "one,1"))
21
+ report.assets.should include(Entry.create!( :name => "two,2"))
22
+ report.assets.should_not include(Entry.create!( :name => "one"))
23
+ end
24
+
25
+ end
@@ -75,7 +75,7 @@ describe Datagrid::Filters do
75
75
  $FILTER_PERFORMED = false
76
76
  report = test_report(:name => value) do
77
77
  scope {Entry}
78
- filter(:name, options) do |value|
78
+ filter(:name, options) do |_|
79
79
  $FILTER_PERFORMED = true
80
80
  self
81
81
  end
@@ -161,4 +161,18 @@ describe Datagrid::Filters do
161
161
  Entry.create!(:created_at => 3.days.ago)
162
162
  grid.assets.should_not be_empty
163
163
  end
164
+
165
+ describe "#filter_by" do
166
+ it "should allow partial filtering" do
167
+ grid = test_report do
168
+ scope {Entry}
169
+ filter(:id)
170
+ filter(:name)
171
+ end
172
+ e = Entry.create!(:name => 'hello')
173
+ grid.attributes = {:id => -1, :name => 'hello'}
174
+ grid.assets.should be_empty
175
+ grid.filter_by(:name).should_not be_empty
176
+ end
177
+ end
164
178
  end
@@ -245,6 +245,19 @@ describe Datagrid::FormBuilder do
245
245
  let(:_filter) { :name }
246
246
 
247
247
  it {should equal_to_dom('<input class="name string_filter" id="report_name" name="report[name]" size="30" type="text">')}
248
+
249
+ context "when multiple option is set" do
250
+ let(:_grid) do
251
+ test_report(:name => "one,two") do
252
+ scope {Entry}
253
+ filter(:name, :string, :multiple => true)
254
+ end
255
+ end
256
+
257
+ let(:_filter) { :name }
258
+
259
+ it {should equal_to_dom('<input class="name string_filter" id="report_name" name="report[name]" size="30" type="text" value="one,two">')}
260
+ end
248
261
  end
249
262
 
250
263
  context "with non multiple filter" do
@@ -332,8 +345,78 @@ describe Datagrid::FormBuilder do
332
345
 
333
346
  it { should equal_to_dom(expected_html) }
334
347
  end
348
+
349
+ context "with dynamic filter" do
350
+ let(:filter_options) do
351
+ {}
352
+ end
353
+
354
+ let(:_grid) do
355
+ options = filter_options
356
+ test_report do
357
+ scope {Entry}
358
+ filter(:condition, :dynamic, options)
359
+ end
360
+ end
361
+ let(:_filter) { :condition }
362
+ context "with no options" do
363
+ let(:expected_html) do
364
+ <<-HTML
365
+ <select class="condition dynamic_filter field" id="report_condition" name="report[condition][]"><option value="id">Id</option>
366
+ <option value="group_id">Group</option>
367
+ <option value="name">Name</option>
368
+ <option value="category">Category</option>
369
+ <option value="access_level">Access level</option>
370
+ <option value="pet">Pet</option>
371
+ <option value="disabled">Disabled</option>
372
+ <option value="confirmed">Confirmed</option>
373
+ <option value="shipping_date">Shipping date</option>
374
+ <option value="created_at">Created at</option>
375
+ <option value="updated_at">Updated at</option></select><select class="condition dynamic_filter operation" id="report_condition" name="report[condition][]"><option value="=">=</option>
376
+ <option value="=~">=~</option>
377
+ <option value="&gt;=">&gt;=</option>
378
+ <option value="&lt;=">&lt;=</option></select><input class="condition dynamic_filter value" id="report_condition" name="report[condition][]" size="30" type="text">
379
+ HTML
380
+ end
381
+ it {should equal_to_dom(expected_html)}
382
+
383
+ end
384
+ context "when default option passed" do
385
+ let(:filter_options) do
386
+ {:select => [:id, :name], :default => [:id, '>=', 1]}
387
+ end
388
+ let(:expected_html) do
389
+ <<-HTML
390
+ <select class="condition dynamic_filter field" id="report_condition" name="report[condition][]"><option value="id" selected>id</option>
391
+ <option value="name">name</option></select><select class="condition dynamic_filter operation" id="report_condition" name="report[condition][]"><option value="=">=</option>
392
+ <option value="=~">=~</option>
393
+ <option value="&gt;=" selected>&gt;=</option>
394
+ <option value="&lt;=">&lt;=</option></select><input class="condition dynamic_filter value" id="report_condition" name="report[condition][]" size="30" type="text" value="1">
395
+ HTML
396
+ end
397
+ it {should equal_to_dom(expected_html)}
398
+
399
+ end
400
+ context "when select option passed" do
401
+ let(:filter_options) do
402
+ {:select => [:id, :name]}
403
+ end
404
+ let(:expected_html) do
405
+ <<-HTML
406
+ <select class="condition dynamic_filter field" id="report_condition" name="report[condition][]"><option value="id">id</option>
407
+ <option value="name">name</option></select><select class="condition dynamic_filter operation" id="report_condition" name="report[condition][]"><option value="=">=</option>
408
+ <option value="=~">=~</option>
409
+ <option value="&gt;=">&gt;=</option>
410
+ <option value="&lt;=">&lt;=</option></select><input class="condition dynamic_filter value" id="report_condition" name="report[condition][]" size="30" type="text">
411
+ HTML
412
+ end
413
+ it {should equal_to_dom(expected_html)}
414
+
415
+ end
416
+ end
335
417
  end
336
418
 
419
+
337
420
  describe ".datagrid_label" do
338
421
  let(:_grid) do
339
422
  test_report do