dataview 0.2.0 → 0.3.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.
- data/lib/data_view.rb +172 -141
- data/test/tc_data_view.rb +149 -162
- metadata +13 -3
data/lib/data_view.rb
CHANGED
@@ -1,150 +1,181 @@
|
|
1
|
+
require 'ducktypechecker'
|
2
|
+
|
1
3
|
module DataView
|
2
4
|
|
3
5
|
# DataView is intended to reduce coupling between the Controller and View layers in a MVC application. The Controller can send a DataView to the View layer. The View layer does not need to have access to the Model.
|
4
6
|
class DataView
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
raise 'columns cannot be nil' if columns == nil
|
66
|
-
@model = model.respond_to?(:each_index) ? model : [model]
|
67
|
-
|
68
|
-
# @columns is an ordered array of the columns that will be shown
|
69
|
-
@columns = columns
|
70
|
-
end
|
71
|
-
# Gets a particular row of the view.
|
72
|
-
def [](index)
|
73
|
-
get_view_row(index)
|
74
|
-
end
|
75
|
-
# The number of rows in the result.
|
76
|
-
def length
|
77
|
-
@model.length
|
78
|
-
end
|
79
|
-
# Iterates through each row of the view.
|
80
|
-
def each
|
81
|
-
@model.each_index do |row_index|
|
82
|
-
yield(get_view_row(row_index))
|
83
|
-
end
|
84
|
-
end
|
85
|
-
# Iterates through each title of the columns.
|
86
|
-
def each_title
|
87
|
-
@columns.each do |column|
|
88
|
-
column_title = column[:title]
|
89
|
-
title = (column_title == nil) ? column[:field] : column[:title]
|
90
|
-
yield(column, title)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
# Iterates through each title of the columns with the index.
|
94
|
-
def each_title_with_index
|
95
|
-
i = 0
|
96
|
-
each_title do |f, t|
|
97
|
-
yield(f, t, i)
|
98
|
-
i = i + 1
|
99
|
-
end
|
100
|
-
end
|
101
|
-
private
|
102
|
-
def get_view_row(row_index)
|
103
|
-
model_row = @model[row_index]
|
104
|
-
view_row_values = {}
|
105
|
-
@columns.each_with_index do |column, column_index|
|
106
|
-
value = get_raw_value(model_row, column, row_index, column_index)
|
107
|
-
value = get_transformed_value(value, column, row_index, column_index)
|
108
|
-
view_row_values[column] = value
|
109
|
-
end
|
110
|
-
Row.new(self, view_row_values)
|
111
|
-
end
|
112
|
-
def get_raw_value(model_row, col, row_index, column_index)
|
113
|
-
value_param = col[:value]
|
114
|
-
return model_row[col[:field]] if value_param == nil
|
115
|
-
|
116
|
-
return @columns[value_param] if @columns.any? {|f| f[:field] == value_param}
|
7
|
+
class << self
|
8
|
+
# Creates a column definition struct.
|
9
|
+
def create_column(field=nil, title=nil, value=nil, *value_transforms)
|
10
|
+
@@Column_Struct_Class = Struct.new(
|
11
|
+
:field,
|
12
|
+
:title,
|
13
|
+
:value,
|
14
|
+
:value_transforms
|
15
|
+
) unless defined?(@@Column_Struct_Class)
|
16
|
+
|
17
|
+
@@Column_Struct_Class.new(field, title, value, value_transforms.flatten)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
public
|
22
|
+
|
23
|
+
include Enumerable
|
24
|
+
attr_reader :columns, :rows
|
25
|
+
# Initializes the DataView.
|
26
|
+
# ===Model
|
27
|
+
# The DataView supports a model that contains an array of structs or hashes.
|
28
|
+
# The keys in these hashes much be symbols of the field names.
|
29
|
+
# ===Columns
|
30
|
+
# An Array of Structs (see :create_column) that contain data
|
31
|
+
# about the columns and how the columns will be handled.
|
32
|
+
# The column order in the each method is the same as the array order.
|
33
|
+
# Each Struct contains the following keys:
|
34
|
+
# ====:field
|
35
|
+
# (Required) The database field should be a Symbol or String of
|
36
|
+
# the field that is called.
|
37
|
+
# ====:title
|
38
|
+
# The title of the column. If one is not given, the field value is used.
|
39
|
+
# ====:value
|
40
|
+
# The value of the particular cell. If a value is not given,
|
41
|
+
# the value of the database field in the model is used.
|
42
|
+
# If a Symbol or String is given and the model contains a field
|
43
|
+
# with that name, then the model value is returned.
|
44
|
+
# If the Symbol or string is not a field in the model,
|
45
|
+
# the value parameter is returned.
|
46
|
+
# If a Proc is given, the return value of the Proc is used.
|
47
|
+
# The Proc must accept a Struct of parameters and return
|
48
|
+
# the transformed value. The Struct contains the following keys:
|
49
|
+
# * :model - The Model of the DataView
|
50
|
+
# * :row_index - The Index of the current row.
|
51
|
+
# * :column_index - The Index of the current column.
|
52
|
+
# * :column - The column of the current value.
|
53
|
+
# ====:value_transforms
|
54
|
+
# An Array of Procs that act as event sinks to transform
|
55
|
+
# the value into the desired view. The transforms occur in the
|
56
|
+
# order of the Array. Each Proc must accept a Struct of parameters
|
57
|
+
# and return the transformed value.
|
58
|
+
# The Struct contains the following keys:
|
59
|
+
# * :value - The current value.
|
60
|
+
# * :model - The Model of the DataView
|
61
|
+
# * :row_index - The Index of the current row.
|
62
|
+
# * :column_index - The Index of the current column.
|
63
|
+
# * :column - The column of the current value.
|
64
|
+
def initialize(model, *columns)
|
65
|
+
columns = columns.flatten
|
66
|
+
@model = if model.respond_to?(:each_index) then model else [model] end
|
117
67
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
68
|
+
# @columns is an ordered array of the columns that will be shown
|
69
|
+
@columns = columns
|
70
|
+
end
|
71
|
+
|
72
|
+
# Creates a column definition struct.
|
73
|
+
def create_column(field=nil, title=nil, value=nil, *value_transforms)
|
74
|
+
DataView.create_column(field, title, value, *value_transforms)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Gets a particular row of the view.
|
78
|
+
def [](index)
|
79
|
+
v_r_s = create_view_row_struct
|
80
|
+
get_view_row(index, v_r_s)
|
81
|
+
end
|
82
|
+
|
83
|
+
# The number of rows in the result.
|
84
|
+
def length
|
85
|
+
@model.length
|
86
|
+
end
|
87
|
+
|
88
|
+
# Iterates through each row of the view.
|
89
|
+
def each
|
90
|
+
v_r_s = create_view_row_struct
|
91
|
+
@model.each_index do |row_index|
|
92
|
+
yield(get_view_row(row_index, v_r_s))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def create_view_row_struct
|
99
|
+
fields = @columns.collect{|c| c.field}
|
100
|
+
Struct.new(*fields)
|
101
|
+
end
|
102
|
+
|
103
|
+
def get_view_row(row_index, view_row_struct)
|
104
|
+
model_row = @model[row_index]
|
105
|
+
|
106
|
+
view_row_values = view_row_struct.new
|
107
|
+
@columns.each_with_index do |column, column_index|
|
108
|
+
value = get_raw_value(model_row, column, row_index, column_index)
|
109
|
+
value = get_transformed_value(value, column, row_index, column_index)
|
110
|
+
view_row_values[column.field] = value
|
111
|
+
end
|
112
|
+
view_row_values
|
113
|
+
end
|
114
|
+
|
115
|
+
def get_raw_value(model_row, col, row_index, column_index)
|
116
|
+
return model_row[col.field] if col.value.nil?
|
117
|
+
|
118
|
+
return col.value.call(
|
119
|
+
create_value_proc_struct(row_index, column_index, col)
|
120
|
+
) if col.value.respond_to?(:call)
|
121
|
+
|
122
|
+
return col.value
|
123
|
+
end
|
124
|
+
|
125
|
+
def create_value_proc_struct(row_index, column_index, column)
|
126
|
+
@@Value_Proc_Struct_Class = Struct.new(
|
127
|
+
:model,
|
128
|
+
:row_index,
|
129
|
+
:column_index,
|
130
|
+
:column
|
131
|
+
) unless defined?(@@Value_Proc_Struct_Class)
|
132
|
+
|
133
|
+
@@Value_Proc_Struct_Class.new(
|
134
|
+
@model,
|
135
|
+
row_index,
|
136
|
+
column_index,
|
137
|
+
column
|
138
|
+
)
|
139
|
+
end
|
140
|
+
|
141
|
+
def get_transformed_value(value, column, row_index, column_index)
|
142
|
+
transforms = column.value_transforms
|
143
|
+
return value if transforms.nil?
|
133
144
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
145
|
+
DuckType.check(transforms, :each, 'column.value_transforms')
|
146
|
+
|
147
|
+
transforms.each do |t|
|
148
|
+
next unless t
|
149
|
+
|
150
|
+
params = create_value_transforms_struct(
|
151
|
+
value,
|
152
|
+
row_index,
|
153
|
+
column_index,
|
154
|
+
column
|
155
|
+
)
|
156
|
+
|
157
|
+
value = t.call(params)
|
158
|
+
end
|
159
|
+
value
|
160
|
+
end
|
161
|
+
|
162
|
+
def create_value_transforms_struct(value, row_index, column_index, column)
|
163
|
+
@@Value_Transforms_Struct_Class = Struct.new(
|
164
|
+
:value,
|
165
|
+
:model,
|
166
|
+
:row_index,
|
167
|
+
:column_index,
|
168
|
+
:column
|
169
|
+
) unless defined?(@@Value_Transforms_Struct_Class)
|
170
|
+
|
171
|
+
@@Value_Transforms_Struct_Class.new(
|
172
|
+
value,
|
173
|
+
@model,
|
174
|
+
row_index,
|
175
|
+
column_index,
|
176
|
+
column
|
177
|
+
)
|
178
|
+
end
|
148
179
|
end
|
149
180
|
|
150
181
|
end
|
data/test/tc_data_view.rb
CHANGED
@@ -3,171 +3,158 @@ require this_dir << '/../lib/data_view'
|
|
3
3
|
require 'test/unit'
|
4
4
|
|
5
5
|
class DataViewTest < Test::Unit::TestCase
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
col = data_view.columns.find {|col| col[:field] == :field1}
|
31
|
-
col[:value_transforms] = nil
|
32
|
-
field = col[:field]
|
33
|
-
assert_equal(mock_model[0][field], data_view[0][:field1])
|
6
|
+
include DataView
|
7
|
+
public
|
8
|
+
|
9
|
+
def test_brackets
|
10
|
+
do_test_brackets(mock_model)
|
11
|
+
do_test_brackets(singular_mock_model)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_each
|
15
|
+
do_test_each(mock_model)
|
16
|
+
do_test_each(singular_mock_model)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_length
|
20
|
+
assert_equal(mock_model.length, get_data_view(mock_model).length)
|
21
|
+
assert_equal(1, get_data_view(singular_mock_model).length)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_value_transforms_accessor
|
25
|
+
data_view = get_data_view(mock_model)
|
26
|
+
col = data_view.columns.find {|col| col.field == :field1}
|
27
|
+
col.value_transforms.clear
|
28
|
+
field = col.field
|
29
|
+
assert_equal(mock_model[0][field], data_view[0].field1)
|
34
30
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
31
|
+
test_transform = lambda { |h|
|
32
|
+
'test ' << h.value.to_s
|
33
|
+
}
|
34
|
+
col.value_transforms << test_transform
|
39
35
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
36
|
+
arg = data_view.send(:create_value_transforms_struct, mock_model[0][field], 0, 0, col)
|
37
|
+
expected_transform_value = test_transform.call(arg)
|
38
|
+
assert_equal(expected_transform_value, data_view[0].field1)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def do_test_brackets(model)
|
44
|
+
data_view = get_data_view(model)
|
46
45
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
46
|
+
test_model = model.respond_to?(:each_index) ? model : [model]
|
47
|
+
test_model.each_with_index do |r, i|
|
48
|
+
columns.each_with_index do |col, j|
|
49
|
+
f = col.field
|
50
|
+
if f == :model_value_field
|
51
|
+
value_struct = create_value_proc_struct(data_view, i, j, col)
|
52
|
+
v = get_model_value(value_struct)
|
53
|
+
else
|
54
|
+
v = r[f]
|
55
|
+
end
|
56
|
+
assert_equal(expected_altered_value(v), data_view[i][f])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def do_test_each(model)
|
62
|
+
data_view = get_data_view(model)
|
63
|
+
data_view.each_with_index do |row, i|
|
64
|
+
expected_row = model.respond_to?(:each_index) ? model[i] : model
|
65
|
+
assert_equal(expected_row.length, row.to_a.length)
|
65
66
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
67
|
+
expected_row.each do |f, expected_value|
|
68
|
+
col_index = data_view.columns.each_with_index do |col, i|
|
69
|
+
break i if col.field == f
|
70
|
+
end
|
71
|
+
|
72
|
+
col = data_view.columns[col_index]
|
73
|
+
|
74
|
+
if f == :model_value_field
|
75
|
+
value_struct = create_value_proc_struct(data_view, i, col_index, col)
|
76
|
+
expected_value = get_model_value(value_struct)
|
77
|
+
end
|
78
|
+
assert_equal(expected_altered_value(expected_value), row[f])
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def create_value_proc_struct(dv, row_index, column_index, column)
|
84
|
+
dv.send(:create_value_proc_struct, row_index, column_index, column)
|
85
|
+
end
|
86
|
+
|
87
|
+
def expected_altered_value(v)
|
88
|
+
"altered " << v
|
89
|
+
end
|
90
|
+
|
91
|
+
def get_data_view(model)
|
92
|
+
DataView.new(model, columns)
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_model_value_method
|
96
|
+
method(:get_model_value)
|
97
|
+
end
|
98
|
+
|
99
|
+
def get_model_value(params)
|
100
|
+
test_model = params.model
|
101
|
+
test_row_index = params.row_index
|
102
|
+
test_column_index = params.column_index
|
103
|
+
test_column = params.column
|
104
|
+
|
105
|
+
unless mock_model == test_model
|
106
|
+
assert_equal(singular_mock_model, test_model[0])
|
107
|
+
end
|
108
|
+
assert((0..2).include?(test_row_index))
|
109
|
+
assert(columns.collect{|c| c.field}.include?(test_column.field))
|
110
|
+
|
111
|
+
fi = columns.collect{|c| c.field}.index(test_column.field)
|
112
|
+
assert_equal(fi, test_column_index)
|
113
|
+
|
114
|
+
test_row_index.to_s << ' : Custom ' << test_column[:field].to_s
|
115
|
+
end
|
116
|
+
|
117
|
+
def columns
|
118
|
+
columns = []
|
119
|
+
[:model_value_field, :field1, :field2, :field3].each do |f|
|
120
|
+
col = DataView.create_column
|
121
|
+
col.field = f
|
122
|
+
col.title = get_title(f)
|
123
|
+
col.value = get_model_value_method if f == :model_value_field
|
124
|
+
col.value_transforms << value_transform
|
125
|
+
columns << col
|
126
|
+
end
|
123
127
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
row[field] = i.to_s << ' : ' << field.to_s
|
157
|
-
end
|
158
|
-
row
|
159
|
-
end
|
160
|
-
def values
|
161
|
-
return @values if defined?(@values)
|
162
|
-
v = Hash.new
|
163
|
-
v[:model_value_field] = get_model_value_method
|
164
|
-
@values = v
|
165
|
-
end
|
166
|
-
def value_transform
|
167
|
-
return @value_transform if defined?(@value_transform)
|
168
|
-
@value_transform = lambda do |h|
|
169
|
-
'altered ' << h[:value].to_s
|
170
|
-
end
|
171
|
-
end
|
128
|
+
columns
|
129
|
+
end
|
130
|
+
|
131
|
+
def get_title(field)
|
132
|
+
"#{field.to_s} Title"
|
133
|
+
end
|
134
|
+
|
135
|
+
def singular_mock_model
|
136
|
+
return @Singular_Mock_Model if defined?(@Singular_Mock_Model)
|
137
|
+
@Singular_Mock_Model = get_mock_model_row(0)
|
138
|
+
end
|
139
|
+
|
140
|
+
def mock_model
|
141
|
+
[0..2].collect {|i| get_mock_model_row(i)}
|
142
|
+
end
|
143
|
+
|
144
|
+
def get_mock_model_row(i)
|
145
|
+
row = Hash.new
|
146
|
+
columns.each do |f|
|
147
|
+
field = f[:field]
|
148
|
+
row[field] = i.to_s << ' : ' << field.to_s
|
149
|
+
end
|
150
|
+
row
|
151
|
+
end
|
152
|
+
|
153
|
+
def values
|
154
|
+
{:model_value_field => get_model_value_method}
|
155
|
+
end
|
156
|
+
|
157
|
+
def value_transform
|
158
|
+
lambda { |h| 'altered ' << h[:value].to_s }
|
159
|
+
end
|
172
160
|
end
|
173
|
-
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
|
|
3
3
|
specification_version: 1
|
4
4
|
name: dataview
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2005-
|
6
|
+
version: 0.3.0
|
7
|
+
date: 2005-09-02 00:00:00 -07:00
|
8
8
|
summary: Data View is a library that creates a view of a data model. The view can transform the data of the data model without changing the data. Supports ActiveRecord models and other data models that have a similar interface.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -42,4 +42,14 @@ extra_rdoc_files:
|
|
42
42
|
executables: []
|
43
43
|
extensions: []
|
44
44
|
requirements: []
|
45
|
-
dependencies:
|
45
|
+
dependencies:
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: ducktypechecker
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
50
|
+
requirements:
|
51
|
+
-
|
52
|
+
- ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.1.0
|
55
|
+
version:
|