datagrid 1.4.4 → 1.5.0
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.
- checksums.yaml +4 -4
- data/.travis.yml +0 -1
- data/Gemfile +2 -2
- data/Rakefile +1 -0
- data/Readme.markdown +6 -0
- data/VERSION +1 -1
- data/datagrid.gemspec +92 -52
- data/lib/datagrid/columns.rb +18 -3
- data/lib/datagrid/columns/column.rb +27 -13
- data/lib/datagrid/core.rb +11 -2
- data/lib/datagrid/drivers/abstract_driver.rb +8 -0
- data/lib/datagrid/drivers/active_record.rb +8 -0
- data/lib/datagrid/drivers/array.rb +5 -1
- data/lib/datagrid/drivers/mongo_mapper.rb +8 -0
- data/lib/datagrid/drivers/mongoid.rb +9 -0
- data/lib/datagrid/drivers/sequel.rb +9 -0
- data/lib/datagrid/filters.rb +44 -8
- data/lib/datagrid/filters/base_filter.rb +11 -2
- data/lib/datagrid/filters/ranged_filter.rb +4 -2
- data/lib/datagrid/helper.rb +27 -4
- data/lib/datagrid/ordering.rb +5 -5
- data/lib/datagrid/renderer.rb +8 -4
- data/lib/datagrid/utils.rb +18 -0
- data/spec/datagrid/columns_spec.rb +45 -0
- data/spec/datagrid/core_spec.rb +23 -1
- data/spec/datagrid/filters/date_filter_spec.rb +9 -0
- data/spec/datagrid/filters/dynamic_filter_spec.rb +27 -1
- data/spec/datagrid/filters_spec.rb +49 -19
- data/spec/datagrid/helper_spec.rb +19 -0
- metadata +187 -5
@@ -90,6 +90,15 @@ module Datagrid
|
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
93
|
+
def default_preload(scope, value)
|
94
|
+
scope.eager(value)
|
95
|
+
end
|
96
|
+
|
97
|
+
def can_preload?(scope, association)
|
98
|
+
!! scope.model.association_reflection(association)
|
99
|
+
end
|
100
|
+
|
101
|
+
|
93
102
|
protected
|
94
103
|
|
95
104
|
def prefix_table_name(scope, field)
|
data/lib/datagrid/filters.rb
CHANGED
@@ -31,14 +31,17 @@ module Datagrid
|
|
31
31
|
:dynamic => Filters::DynamicFilter
|
32
32
|
}
|
33
33
|
|
34
|
+
class DefaultFilterScope
|
35
|
+
end
|
36
|
+
|
34
37
|
def self.included(base) #:nodoc:
|
35
38
|
base.extend ClassMethods
|
36
39
|
base.class_eval do
|
37
40
|
|
38
41
|
include Datagrid::Core
|
39
42
|
include Datagrid::Filters::CompositeFilters
|
40
|
-
class_attribute :
|
41
|
-
self.
|
43
|
+
class_attribute :filters_array
|
44
|
+
self.filters_array = []
|
42
45
|
|
43
46
|
end
|
44
47
|
base.send :include, InstanceMethods
|
@@ -80,6 +83,10 @@ module Datagrid
|
|
80
83
|
# by adding it after the filter passed here (when using datagrid_form_for helper)
|
81
84
|
# * <tt>:dummy</tt> - if true, this filter will not be applied automatically
|
82
85
|
# and will be just displayed in form. In case you may want to apply it manually.
|
86
|
+
# * <tt>:if</tt> - specify the condition when the filter can be dislayed and used.
|
87
|
+
# Accepts a block or a symbol with an instance method name
|
88
|
+
# * <tt>:unless</tt> - specify the reverse condition when the filter can be dislayed and used.
|
89
|
+
# Accepts a block or a symbol with an instance method name
|
83
90
|
#
|
84
91
|
# See: https://github.com/bogdan/datagrid/wiki/Filters for examples
|
85
92
|
def filter(name, type = :default, options = {}, &block)
|
@@ -91,9 +98,9 @@ module Datagrid
|
|
91
98
|
klass = type.is_a?(Class) ? type : FILTER_TYPES[type]
|
92
99
|
raise ConfigurationError, "filter class #{type.inspect} not found" unless klass
|
93
100
|
|
94
|
-
position = Datagrid::Utils.extract_position_from_options(
|
101
|
+
position = Datagrid::Utils.extract_position_from_options(filters_array, options)
|
95
102
|
filter = klass.new(self, name, options, &block)
|
96
|
-
|
103
|
+
filters_array.insert(position, filter)
|
97
104
|
|
98
105
|
datagrid_attribute(name) do |value|
|
99
106
|
filter.parse_values(value)
|
@@ -101,20 +108,38 @@ module Datagrid
|
|
101
108
|
|
102
109
|
end
|
103
110
|
|
111
|
+
def default_filter
|
112
|
+
DefaultFilterScope.new
|
113
|
+
end
|
114
|
+
|
115
|
+
def inspect
|
116
|
+
"#{super}(#{filters_inspection})"
|
117
|
+
end
|
118
|
+
|
119
|
+
def filters
|
120
|
+
filters_array
|
121
|
+
end
|
122
|
+
|
104
123
|
protected
|
105
124
|
|
106
125
|
def inherited(child_class)
|
107
126
|
super(child_class)
|
108
|
-
child_class.
|
127
|
+
child_class.filters_array = self.filters_array.clone
|
109
128
|
end
|
110
129
|
|
130
|
+
def filters_inspection
|
131
|
+
return "no filters" if filters.empty?
|
132
|
+
filters.map do |filter|
|
133
|
+
"#{filter.name}: #{filter.type}"
|
134
|
+
end.join(", ")
|
135
|
+
end
|
111
136
|
end # ClassMethods
|
112
137
|
|
113
138
|
module InstanceMethods
|
114
139
|
|
115
140
|
def initialize(*args, &block) # :nodoc:
|
116
|
-
self.
|
117
|
-
|
141
|
+
self.filters_array = self.class.filters_array.clone
|
142
|
+
filters.each do |filter|
|
118
143
|
self[filter.name] = filter.default(self)
|
119
144
|
end
|
120
145
|
super(*args, &block)
|
@@ -133,7 +158,7 @@ module Datagrid
|
|
133
158
|
def filter_value_as_string(name)
|
134
159
|
filter = filter_by_name(name)
|
135
160
|
value = filter_value(filter)
|
136
|
-
if value.is_a?(Array)
|
161
|
+
if value.is_a?(Array)
|
137
162
|
value.map {|v| filter.format(v) }.join(filter.separator)
|
138
163
|
else
|
139
164
|
filter.format(value)
|
@@ -161,6 +186,17 @@ module Datagrid
|
|
161
186
|
filter.select(self)
|
162
187
|
end
|
163
188
|
|
189
|
+
def default_filter
|
190
|
+
self.class.default_filter
|
191
|
+
end
|
192
|
+
|
193
|
+
# Returns all currently enabled filters
|
194
|
+
def filters
|
195
|
+
self.class.filters.select do |filter|
|
196
|
+
filter.enabled?(self)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
164
200
|
protected
|
165
201
|
|
166
202
|
def apply_filters(current_scope, filters)
|
@@ -24,10 +24,15 @@ class Datagrid::Filters::BaseFilter #:nodoc:
|
|
24
24
|
return scope if unapplicable_value?(value)
|
25
25
|
|
26
26
|
result = execute(value, scope, grid_object)
|
27
|
+
|
27
28
|
return scope unless result
|
29
|
+
if result.is_a?(Datagrid::Filters::DefaultFilterScope)
|
30
|
+
result = default_filter(value, scope, grid_object)
|
31
|
+
end
|
28
32
|
unless grid_object.driver.match?(result)
|
29
33
|
raise Datagrid::FilteringError, "Can not apply #{name.inspect} filter: result #{result.inspect} no longer match #{grid_object.driver.class}."
|
30
34
|
end
|
35
|
+
|
31
36
|
result
|
32
37
|
end
|
33
38
|
|
@@ -49,7 +54,7 @@ class Datagrid::Filters::BaseFilter #:nodoc:
|
|
49
54
|
end
|
50
55
|
|
51
56
|
def header
|
52
|
-
if header = options[:header]
|
57
|
+
if header = options[:header]
|
53
58
|
callable(header)
|
54
59
|
else
|
55
60
|
Datagrid::Utils.translate_from_namespace(:filters, grid_class, name)
|
@@ -68,7 +73,7 @@ class Datagrid::Filters::BaseFilter #:nodoc:
|
|
68
73
|
Datagrid::Utils.warn_once(":default as a Symbol is now treated as a method name. Use String instead or -> { default } if you really want default value to be a Symbol but not a String.")
|
69
74
|
default
|
70
75
|
end
|
71
|
-
elsif default.respond_to?(:call)
|
76
|
+
elsif default.respond_to?(:call)
|
72
77
|
Datagrid::Utils.apply_args(object, &default)
|
73
78
|
else
|
74
79
|
default
|
@@ -128,6 +133,10 @@ class Datagrid::Filters::BaseFilter #:nodoc:
|
|
128
133
|
raise "wtf is #{inspect}"
|
129
134
|
end
|
130
135
|
|
136
|
+
def enabled?(grid)
|
137
|
+
::Datagrid::Utils.process_availability(grid, options[:if], options[:unless])
|
138
|
+
end
|
139
|
+
|
131
140
|
protected
|
132
141
|
|
133
142
|
def default_filter_where(scope, value)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Datagrid::Filters::RangedFilter
|
2
|
-
|
2
|
+
|
3
3
|
|
4
4
|
def initialize(grid, name, options, &block)
|
5
5
|
super(grid, name, options, &block)
|
@@ -23,6 +23,8 @@ module Datagrid::Filters::RangedFilter
|
|
23
23
|
if result.first && result.last && result.first > result.last
|
24
24
|
# If wrong range is given - reverse it to be always valid
|
25
25
|
result.reverse
|
26
|
+
elsif !result.first && !result.last
|
27
|
+
nil
|
26
28
|
else
|
27
29
|
result
|
28
30
|
end
|
@@ -46,7 +48,7 @@ module Datagrid::Filters::RangedFilter
|
|
46
48
|
scope = driver.less_equal(scope, name, right)
|
47
49
|
end
|
48
50
|
scope
|
49
|
-
else
|
51
|
+
else
|
50
52
|
super(scope, value)
|
51
53
|
end
|
52
54
|
end
|
data/lib/datagrid/helper.rb
CHANGED
@@ -55,7 +55,8 @@ module Datagrid
|
|
55
55
|
end
|
56
56
|
|
57
57
|
|
58
|
-
# Renders HTML table rows using given grid definition using columns defined in it
|
58
|
+
# Renders HTML table rows using given grid definition using columns defined in it.
|
59
|
+
# Allows to provide a custom layout for each for in place with a block
|
59
60
|
#
|
60
61
|
# Supported options:
|
61
62
|
#
|
@@ -64,8 +65,15 @@ module Datagrid
|
|
64
65
|
# and needs different columns. Default: all defined columns.
|
65
66
|
# * <tt>:partials</tt> - Path for partials lookup.
|
66
67
|
# Default: 'datagrid'.
|
67
|
-
|
68
|
-
|
68
|
+
#
|
69
|
+
# = datagrid_rows(grid) # Generic table rows Layout
|
70
|
+
#
|
71
|
+
# = datagrid_rows(grid) do |row| # Custom Layout
|
72
|
+
# %tr
|
73
|
+
# %td= row.project_name
|
74
|
+
# %td.project-status{class: row.status}= row.status
|
75
|
+
def datagrid_rows(grid, assets = grid.assets, **options, &block)
|
76
|
+
datagrid_renderer.rows(grid, assets, options, &block)
|
69
77
|
end
|
70
78
|
|
71
79
|
# Renders ordering controls for the given column name
|
@@ -116,6 +124,8 @@ module Datagrid
|
|
116
124
|
# row.asset # => User object
|
117
125
|
class HtmlRow
|
118
126
|
|
127
|
+
include Enumerable
|
128
|
+
|
119
129
|
attr_reader :grid, :asset
|
120
130
|
|
121
131
|
def initialize(context, grid, asset) # :nodoc:
|
@@ -124,9 +134,22 @@ module Datagrid
|
|
124
134
|
@asset = asset
|
125
135
|
end
|
126
136
|
|
137
|
+
# Return a column value for given column name
|
138
|
+
def get(column)
|
139
|
+
@context.datagrid_value(@grid, column, @asset)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Iterates over all column values that are available in the row
|
143
|
+
def each
|
144
|
+
@grid.columns.each do |column|
|
145
|
+
yield(get(column))
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
protected
|
127
150
|
def method_missing(method, *args, &blk)
|
128
151
|
if column = @grid.column_by_name(method)
|
129
|
-
|
152
|
+
get(column)
|
130
153
|
else
|
131
154
|
super
|
132
155
|
end
|
data/lib/datagrid/ordering.rb
CHANGED
@@ -44,7 +44,7 @@ module Datagrid
|
|
44
44
|
end
|
45
45
|
|
46
46
|
# Returns a column definition that is currently used to order assets
|
47
|
-
#
|
47
|
+
#
|
48
48
|
# class MyGrid
|
49
49
|
# scope { Model }
|
50
50
|
# column(:id)
|
@@ -87,11 +87,11 @@ module Datagrid
|
|
87
87
|
def check_order_valid!
|
88
88
|
return unless order
|
89
89
|
column = column_by_name(order)
|
90
|
-
unless column
|
90
|
+
unless column
|
91
91
|
self.class.order_unsupported(order, "no column #{order} in #{self.class}")
|
92
92
|
end
|
93
93
|
unless column.supports_order?
|
94
|
-
self.class.order_unsupported(column.name, "column don't support order" )
|
94
|
+
self.class.order_unsupported(column.name, "column don't support order" )
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
@@ -99,7 +99,7 @@ module Datagrid
|
|
99
99
|
if order.respond_to?(:call)
|
100
100
|
apply_block_order(assets, order)
|
101
101
|
else
|
102
|
-
driver.asc(assets, order)
|
102
|
+
driver.asc(assets, order)
|
103
103
|
end
|
104
104
|
end
|
105
105
|
|
@@ -118,7 +118,7 @@ module Datagrid
|
|
118
118
|
end
|
119
119
|
|
120
120
|
def apply_block_order(assets, order)
|
121
|
-
case order.arity
|
121
|
+
case order.arity
|
122
122
|
when -1, 0
|
123
123
|
assets.instance_eval(&order)
|
124
124
|
when 1
|
data/lib/datagrid/renderer.rb
CHANGED
@@ -55,15 +55,19 @@ module Datagrid
|
|
55
55
|
{ :grid => grid, :options => options })
|
56
56
|
end
|
57
57
|
|
58
|
-
def rows(grid, assets, options
|
58
|
+
def rows(grid, assets = grid.assets, **options, &block)
|
59
59
|
result = assets.map do |asset|
|
60
|
-
|
61
|
-
|
62
|
-
|
60
|
+
if block_given?
|
61
|
+
@template.capture do
|
62
|
+
yield(Datagrid::Helper::HtmlRow.new(@template, grid, asset))
|
63
|
+
end
|
64
|
+
else
|
65
|
+
_render_partial( 'row', options[:partials], {
|
63
66
|
:grid => grid,
|
64
67
|
:options => options,
|
65
68
|
:asset => asset
|
66
69
|
})
|
70
|
+
end
|
67
71
|
end.to_a.join
|
68
72
|
|
69
73
|
_safe(result)
|
data/lib/datagrid/utils.rb
CHANGED
@@ -127,6 +127,24 @@ module Datagrid
|
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
130
|
+
def process_availability(grid, if_option, unless_option)
|
131
|
+
property_availability(grid, if_option, true) &&
|
132
|
+
!property_availability(grid, unless_option, false)
|
133
|
+
end
|
134
|
+
|
135
|
+
protected
|
136
|
+
def property_availability(grid, option, default)
|
137
|
+
case option
|
138
|
+
when nil
|
139
|
+
default
|
140
|
+
when Proc
|
141
|
+
option.call(grid)
|
142
|
+
when Symbol, String
|
143
|
+
grid.send(option.to_sym)
|
144
|
+
else
|
145
|
+
raise Datagrid::ConfigurationError, "Incorrect column availability option: #{option.insepct}"
|
146
|
+
end
|
147
|
+
end
|
130
148
|
end
|
131
149
|
end
|
132
150
|
end
|
@@ -562,4 +562,49 @@ describe Datagrid::Columns do
|
|
562
562
|
expect(grid.rows).to eq([['Hello']])
|
563
563
|
end
|
564
564
|
end
|
565
|
+
|
566
|
+
describe "column scope" do
|
567
|
+
it "appends preload as non block" do
|
568
|
+
grid = test_report do
|
569
|
+
scope { Entry }
|
570
|
+
column(:id, preload: [:group])
|
571
|
+
end
|
572
|
+
expect(grid.assets.preload_values).to_not be_blank
|
573
|
+
end
|
574
|
+
|
575
|
+
it "appends preload with no args" do
|
576
|
+
grid = test_report do
|
577
|
+
scope { Entry }
|
578
|
+
column(:id, preload: -> { order(:id) })
|
579
|
+
end
|
580
|
+
expect(grid.assets.order_values).to_not be_blank
|
581
|
+
end
|
582
|
+
|
583
|
+
it "appends preload with arg" do
|
584
|
+
grid = test_report do
|
585
|
+
scope { Entry }
|
586
|
+
column(:id, preload: ->(a) { a.order(:id) })
|
587
|
+
end
|
588
|
+
expect(grid.assets.order_values).to_not be_blank
|
589
|
+
end
|
590
|
+
|
591
|
+
it "appends preload as true value" do
|
592
|
+
grid = test_report do
|
593
|
+
scope { Entry }
|
594
|
+
column(:group, preload: true)
|
595
|
+
end
|
596
|
+
expect(grid.assets.preload_values).to eq([:group])
|
597
|
+
end
|
598
|
+
|
599
|
+
it "doesn't append preload when column is invisible" do
|
600
|
+
grid = test_report do
|
601
|
+
scope { Entry }
|
602
|
+
column(:id1, preload: ->(a) { a.order(:id) })
|
603
|
+
column(:id2, preload: ->(a) { a.order(:id) }, if: ->(a) { false })
|
604
|
+
column(:name)
|
605
|
+
end
|
606
|
+
grid.column_names = [:name]
|
607
|
+
expect(grid.assets.order_values).to be_blank
|
608
|
+
end
|
609
|
+
end
|
565
610
|
end
|
data/spec/datagrid/core_spec.rb
CHANGED
@@ -6,9 +6,11 @@ describe Datagrid::Core do
|
|
6
6
|
before { 2.times { Entry.create } }
|
7
7
|
|
8
8
|
let(:report_class) do
|
9
|
-
|
9
|
+
class ScopeTestReport
|
10
|
+
include Datagrid
|
10
11
|
scope { Entry.order("id desc") }
|
11
12
|
end
|
13
|
+
ScopeTestReport
|
12
14
|
end
|
13
15
|
|
14
16
|
describe '#scope' do
|
@@ -16,6 +18,22 @@ describe Datagrid::Core do
|
|
16
18
|
let(:report) { report_class.new }
|
17
19
|
|
18
20
|
it { expect(report.scope.to_a.size).to eq(2) }
|
21
|
+
it { expect(report).to_not be_redefined_scope }
|
22
|
+
|
23
|
+
context "when redefined" do
|
24
|
+
it "should accept previous scope" do
|
25
|
+
module Ns83827
|
26
|
+
class TestGrid < ScopeTestReport
|
27
|
+
scope do |previous|
|
28
|
+
previous.reorder("id asc")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
expect(Ns83827::TestGrid.new.assets.order_values).to eq(["id asc"])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
19
37
|
end
|
20
38
|
|
21
39
|
context 'changes scope on the fly' do
|
@@ -26,11 +44,13 @@ describe Datagrid::Core do
|
|
26
44
|
end
|
27
45
|
|
28
46
|
it { expect(report.scope.to_a.size).to eq(1) }
|
47
|
+
it { expect(report).to be_redefined_scope }
|
29
48
|
end
|
30
49
|
|
31
50
|
context 'overriding scope by initializer' do
|
32
51
|
let(:report) { report_class.new { Entry.limit(1) } }
|
33
52
|
|
53
|
+
it { expect(report).to be_redefined_scope }
|
34
54
|
it { expect(report.scope.to_a.size).to eq(1) }
|
35
55
|
|
36
56
|
context "reset scope to default" do
|
@@ -38,6 +58,7 @@ describe Datagrid::Core do
|
|
38
58
|
report.reset_scope
|
39
59
|
end
|
40
60
|
it { expect(report.scope.to_a.size).to eq(2) }
|
61
|
+
it { expect(report).to_not be_redefined_scope }
|
41
62
|
end
|
42
63
|
end
|
43
64
|
|
@@ -45,6 +66,7 @@ describe Datagrid::Core do
|
|
45
66
|
let(:report) { report_class.new {|scope| scope.limit(1)} }
|
46
67
|
it { expect(report.scope.to_a.size).to eq(1) }
|
47
68
|
it { expect(report.scope.order_values.size).to eq(1) }
|
69
|
+
it { expect(report).to be_redefined_scope }
|
48
70
|
end
|
49
71
|
end
|
50
72
|
end
|