datagrid 0.9.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Readme.markdown +6 -4
- data/VERSION +1 -1
- data/app/assets/stylesheets/datagrid.css.sass +132 -0
- data/app/views/datagrid/_form.html.erb +5 -2
- data/app/views/datagrid/_order_for.html.erb +2 -2
- data/app/views/datagrid/_table.html.erb +1 -1
- data/datagrid.gemspec +10 -3
- data/lib/datagrid.rb +1 -0
- data/lib/datagrid/column_names_attribute.rb +38 -7
- data/lib/datagrid/columns.rb +38 -4
- data/lib/datagrid/columns/column.rb +29 -1
- data/lib/datagrid/drivers/abstract_driver.rb +8 -0
- data/lib/datagrid/drivers/active_record.rb +29 -1
- data/lib/datagrid/drivers/array.rb +14 -2
- data/lib/datagrid/drivers/mongo_mapper.rb +8 -0
- data/lib/datagrid/drivers/mongoid.rb +9 -1
- data/lib/datagrid/filters.rb +24 -6
- data/lib/datagrid/filters/base_filter.rb +42 -14
- data/lib/datagrid/filters/boolean_enum_filter.rb +1 -1
- data/lib/datagrid/filters/dynamic_filter.rb +57 -0
- data/lib/datagrid/filters/enum_filter.rb +4 -21
- data/lib/datagrid/filters/select_options.rb +26 -0
- data/lib/datagrid/form_builder.rb +41 -8
- data/lib/datagrid/helper.rb +2 -1
- data/lib/datagrid/i18n.rb +0 -0
- data/lib/datagrid/locale/en.yml +28 -0
- data/lib/datagrid/ordering.rb +33 -19
- data/lib/datagrid/utils.rb +8 -9
- data/spec/datagrid/column_names_attribute_spec.rb +44 -1
- data/spec/datagrid/columns_spec.rb +16 -0
- data/spec/datagrid/filters/dynamic_filter_spec.rb +37 -0
- data/spec/datagrid/filters/integer_filter_spec.rb +18 -0
- data/spec/datagrid/filters/string_filter_spec.rb +25 -0
- data/spec/datagrid/filters_spec.rb +15 -1
- data/spec/datagrid/form_builder_spec.rb +83 -0
- data/spec/datagrid/helper_spec.rb +1 -0
- data/spec/datagrid/ordering_spec.rb +41 -1
- data/spec/datagrid/utils_spec.rb +7 -2
- metadata +11 -4
data/lib/datagrid/helper.rb
CHANGED
@@ -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
|
+
"——"
|
5
|
+
table:
|
6
|
+
order:
|
7
|
+
asc: "↑"
|
8
|
+
desc: "↓"
|
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
|
+
"=~": "=~"
|
data/lib/datagrid/ordering.rb
CHANGED
@@ -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.
|
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
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
-
|
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
|
data/lib/datagrid/utils.rb
CHANGED
@@ -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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
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 |
|
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=">=">>=</option>
|
378
|
+
<option value="<="><=</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=">=" selected>>=</option>
|
394
|
+
<option value="<="><=</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=">=">>=</option>
|
410
|
+
<option value="<="><=</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
|