dataview 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/lib/data_view.rb +172 -141
  2. data/test/tc_data_view.rb +149 -162
  3. 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
- class Row
6
- public
7
- include Enumerable
8
- attr_accessor :data_view
9
- def initialize(data_view, values = {})
10
- @data_view = data_view
11
- @values = values
12
- end
13
- def [](key)
14
- return @values[get_key(key)]
15
- end
16
- def []=(key, value)
17
- @values[get_key(key)] = value
18
- end
19
- def each
20
- data_view.columns.each_with_index do |c, i|
21
- yield(c, @values[c])
22
- end
23
- end
24
- def length
25
- @values.length
26
- end
27
- private
28
- def get_key(key)
29
- return key if data_view.columns.include?(key)
30
- col = data_view.columns.find {|c| c[:field] == key}
31
- return col unless col == nil
32
- return data_view.columns[key.to_i] if key.respond_to?(:to_i)
33
- raise "The column #{key} does not exist in this row"
34
- end
35
- end
36
- public
37
- include Enumerable
38
- attr_reader :columns, :rows
39
- # Initializes the DataView.
40
- # ===Model
41
- # The DataView supports a model that contains an array of hashes. The keys in these hashes much be symbols of the field names. This is based off of the ActiveRecord interface.
42
- # ===Columns
43
- # An Array of Hashes that contain data about the columns and how the columns will be handled. The column order in the each method is the same as the array order.
44
- # Each Hash contains the following keys:
45
- # ====:field
46
- # (Required) The database field should be a Symbol or String of the field that is called.
47
- # ====:title
48
- # The title of the column. If one is not given, the field value is used.
49
- # ====:value
50
- # The value of the particular cell. If a value is not given, the value of the database field in the model is used. If a Symbol or String is given and the model contains a field with that name, then the model value is returned. If the Symbol or string is not a field in the model, the value parameter is returned.
51
- # If a Proc is given, the return value of the Proc is used. The Proc must accept a Hash of parameters and return the transformed value. The Hash contains the following keys:
52
- # * :sender - The DataView sending the call
53
- # * :model - The Model of the DataView
54
- # * :row_index - The Index of the current row.
55
- # * :column_index - The Index of the current column.
56
- # * :column - The column of the current value.
57
- # ====:value_transforms
58
- # An Array of Procs that act as event sinks to transform the value into the desired view. The transforms occur in the order of the Array. Each Proc must accept a Hash of parameters and return the transformed value. The Hash contains the following keys:
59
- # * :sender - The DataView sending the call
60
- # * :row_index - The Index of the current row.
61
- # * :column_index - The Index of the current column.
62
- # * :value - The current value.
63
- # * :column - The column of the current value.
64
- def initialize(model, columns = [])
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
- return value_param.call(
119
- {
120
- :sender => self,
121
- :model => @model,
122
- :row_index => row_index,
123
- :column_index => column_index,
124
- :column => col
125
- }
126
- ) if value_param.respond_to?(:call)
127
-
128
- return value_param
129
- end
130
- def get_transformed_value(value, column, row_index, column_index)
131
- transforms = column[:value_transforms]
132
- return value if transforms == nil
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
- transforms = [transforms] unless transforms.respond_to?(:each)
135
- transforms.each do |transform|
136
- next unless transform
137
- transform_params = {
138
- :sender => self,
139
- :row_index => row_index,
140
- :column_index => column_index,
141
- :value => value,
142
- :column => column
143
- }
144
- value = transform.call(transform_params)
145
- end
146
- value
147
- end
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
- include DataView
7
- public
8
- def test_brackets
9
- do_test_brackets(mock_model)
10
- do_test_brackets(singular_mock_model)
11
- end
12
- def test_each
13
- do_test_each(mock_model)
14
- do_test_each(singular_mock_model)
15
- end
16
- def test_each_title
17
- do_test_each_title(mock_model)
18
- do_test_each_title(singular_mock_model)
19
- end
20
- def test_each_title_with_index
21
- do_test_each_title_with_index(mock_model)
22
- do_test_each_title_with_index(singular_mock_model)
23
- end
24
- def test_length
25
- assert_equal(mock_model.length, get_data_view(mock_model).length)
26
- assert_equal(1, get_data_view(singular_mock_model).length)
27
- end
28
- def test_value_transforms_accessor
29
- data_view = get_data_view(mock_model)
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
- test_transform = lambda { |h|
36
- 'test ' << h[:value].to_s
37
- }
38
- col[:value_transforms] = test_transform
31
+ test_transform = lambda { |h|
32
+ 'test ' << h.value.to_s
33
+ }
34
+ col.value_transforms << test_transform
39
35
 
40
- expected_transform_value = test_transform.call({:value => mock_model[0][field]})
41
- assert_equal(expected_transform_value, data_view[0][:field1])
42
- end
43
- private
44
- def do_test_brackets(model)
45
- data_view = get_data_view(model)
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
- test_model = model.respond_to?(:each_index) ? model : [model]
48
- test_model.each_with_index do |r, i|
49
- columns.each_with_index do |col, j|
50
- f = col[:field]
51
- if f != :model_value_field
52
- v = r[f]
53
- else
54
- v = get_model_value({:sender => self, :model => model, :row_index => i, :column => col})
55
- end
56
- assert_equal(expected_altered_value(v), data_view[i][f])
57
- end
58
- end
59
- end
60
- def do_test_each(model)
61
- data_view = get_data_view(model)
62
- data_view.each_with_index do |row, i|
63
- expected_row = model.respond_to?(:each_index) ? model[i] : model
64
- assert_equal(expected_row.length, row.to_a.length)
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
- expected_row.each do |f, expected_value|
67
- col = data_view.columns.find{|col| col[:field] == f}
68
- if f == :model_value_field
69
- expected_value = get_model_value({:sender => self, :model => model, :row_index => i, :column => col})
70
- end
71
- assert_equal(expected_altered_value(expected_value), row[f])
72
- end
73
- end
74
- end
75
- def do_test_each_title(model)
76
- data_view = get_data_view(model)
77
- i = 0
78
- data_view.each_title do |f, t|
79
- assert_equal(f[:title], t)
80
- i = i + 1
81
- end
82
- end
83
- def do_test_each_title_with_index(model)
84
- data_view = get_data_view(model)
85
- ei = 0
86
- data_view.each_title_with_index do |f, t, i|
87
- assert_equal(f[:title], t)
88
- assert_equal(ei, i)
89
- ei = ei + 1
90
- end
91
- end
92
- def expected_altered_value(v)
93
- "altered " << v
94
- end
95
- def get_data_view(model)
96
- DataView.new(model, columns)
97
- end
98
- def get_model_value_method
99
- return @get_model_value_method if defined?(@get_model_value_method)
100
- @get_model_value_method = method(:get_model_value)
101
- end
102
- def get_model_value(params)
103
- test_sender = params[:sender]
104
- test_model = params[:model]
105
- test_row_index = params[:row_index]
106
- test_column_index = params[:column_index]
107
- test_column = params[:column]
108
-
109
- unless test_sender == self
110
- assert_equal(DataView, test_sender.class)
111
- unless mock_model == test_model
112
- assert_equal(singular_mock_model, test_model[0])
113
- end
114
- assert((0..2).include?(test_row_index))
115
- assert(columns.include?(test_column))
116
- fi = columns.index(test_column)
117
- assert_equal(fi, test_column_index)
118
- end
119
- test_row_index.to_s << ' : Custom ' << test_column[:field].to_s
120
- end
121
- def columns
122
- return @columns if defined?(@columns)
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
- columns = []
125
- [:model_value_field, :field1, :field2, :field3].each do |f|
126
- column = Hash.new
127
- column[:field] = f
128
- column[:title] = get_title(f)
129
- column[:value] = get_model_value_method if f == :model_value_field
130
- column[:value_transforms] = value_transform
131
- columns.push(column)
132
- end
133
-
134
- @columns = columns
135
- end
136
- def get_title(field)
137
- "#{field.to_s} Title"
138
- end
139
- def singular_mock_model
140
- return @singular_mock_model if defined?(@singular_mock_model)
141
- @singular_mock_model = get_mock_model_row(0)
142
- end
143
- def mock_model
144
- return @mock_model if defined?(@mock_model)
145
- m = Array.new
146
- (0..2).each do |i|
147
- row = get_mock_model_row(i)
148
- m << row
149
- end
150
- @mock_model = m
151
- end
152
- def get_mock_model_row(i)
153
- row = Hash.new
154
- columns.each do |f|
155
- field = f[:field]
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.2.0
7
- date: 2005-07-26 00:00:00 -07:00
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: