dataview 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|