acts_as_table 0.0.1
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 +7 -0
- data/.yardopts +1 -0
- data/LICENSE +30 -0
- data/README.md +256 -0
- data/Rakefile +13 -0
- data/app/models/acts_as_table/belongs_to.rb +81 -0
- data/app/models/acts_as_table/column_model.rb +89 -0
- data/app/models/acts_as_table/foreign_key.rb +299 -0
- data/app/models/acts_as_table/foreign_key_map.rb +121 -0
- data/app/models/acts_as_table/has_many.rb +90 -0
- data/app/models/acts_as_table/has_many_target.rb +40 -0
- data/app/models/acts_as_table/lense.rb +131 -0
- data/app/models/acts_as_table/primary_key.rb +108 -0
- data/app/models/acts_as_table/record.rb +109 -0
- data/app/models/acts_as_table/record_error.rb +50 -0
- data/app/models/acts_as_table/record_model.rb +194 -0
- data/app/models/acts_as_table/row_model.rb +285 -0
- data/app/models/acts_as_table/table.rb +41 -0
- data/app/models/acts_as_table/value.rb +80 -0
- data/app/models/concerns/acts_as_table/record_model_class_methods.rb +554 -0
- data/app/models/concerns/acts_as_table/value_provider.rb +223 -0
- data/app/models/concerns/acts_as_table/value_provider_association_methods.rb +57 -0
- data/config/locales/en.yml +8 -0
- data/db/migrate/1_acts_as_table_migration.rb +186 -0
- data/lib/acts_as_table.rb +288 -0
- data/lib/acts_as_table/adapter.rb +81 -0
- data/lib/acts_as_table/engine.rb +14 -0
- data/lib/acts_as_table/headers.rb +196 -0
- data/lib/acts_as_table/mapper.rb +464 -0
- data/lib/acts_as_table/path.rb +157 -0
- data/lib/acts_as_table/reader.rb +149 -0
- data/lib/acts_as_table/version.rb +4 -0
- data/lib/acts_as_table/writer.rb +116 -0
- metadata +181 -0
@@ -0,0 +1,288 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
require 'active_record'
|
4
|
+
require 'active_record/version'
|
5
|
+
require 'active_support/core_ext/module'
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'rails/engine'
|
9
|
+
require 'acts_as_table/engine'
|
10
|
+
rescue ::LoadError
|
11
|
+
# void
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'acts_as_table/version'
|
15
|
+
|
16
|
+
# ActsAsTable is a Ruby on Rails plugin for working with tabular data.
|
17
|
+
module ActsAsTable
|
18
|
+
extend ::ActiveSupport::Autoload
|
19
|
+
|
20
|
+
# ActsAsTable serialization.
|
21
|
+
autoload :Headers, 'acts_as_table/headers'
|
22
|
+
autoload :Reader, 'acts_as_table/reader'
|
23
|
+
autoload :Writer, 'acts_as_table/writer'
|
24
|
+
|
25
|
+
# ActsAsTable utilities.
|
26
|
+
autoload :Adapter, 'acts_as_table/adapter'
|
27
|
+
autoload :Mapper, 'acts_as_table/mapper'
|
28
|
+
autoload :Path, 'acts_as_table/path'
|
29
|
+
|
30
|
+
# Finds an ActsAsTable serialization format module based on a symbolic name.
|
31
|
+
#
|
32
|
+
# @param [Symbol] format
|
33
|
+
# @return [Module]
|
34
|
+
# @raise [ArgumentError] If the given symbolic name is invalid.
|
35
|
+
#
|
36
|
+
# @example Find the ActsAsTable serialization format module for CSV format.
|
37
|
+
# ActsAsTable.for(:csv) #=> ActsAsTable::CSV
|
38
|
+
#
|
39
|
+
# @example Implement an ActsAsTable serialization format module.
|
40
|
+
# require 'active_support/core_ext/module'
|
41
|
+
#
|
42
|
+
# module ActsAsTable
|
43
|
+
# # ActsAsTable serialization format module for "custom format."
|
44
|
+
# module CustomFormat
|
45
|
+
# extend ::ActiveSupport::Autoload
|
46
|
+
#
|
47
|
+
# autoload :Reader, 'acts_as_table/custom_format/reader'
|
48
|
+
# autoload :Writer, 'acts_as_table/custom_format/writer'
|
49
|
+
#
|
50
|
+
# # Returns the symbolic name for this ActsAsTable serialization format module.
|
51
|
+
# #
|
52
|
+
# # @return [Symbol]
|
53
|
+
# def format
|
54
|
+
# :custom_format
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# # Returns a new ActsAsTable reader object for this serialization format module.
|
58
|
+
# #
|
59
|
+
# # @param [Array<Object>] args
|
60
|
+
# # @yieldparam [ActsAsTable::CustomFormat::Reader] reader
|
61
|
+
# # @yieldreturn [void]
|
62
|
+
# # @return [ActsAsTable::CustomFormat::Reader]
|
63
|
+
# def reader(*args, &block)
|
64
|
+
# Reader.new(*args, &block)
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# # Returns a new ActsAsTable writer object for this serialization format module.
|
68
|
+
# #
|
69
|
+
# # @param [Array<Object>] args
|
70
|
+
# # @yieldparam [ActsAsTable::CustomFormat::Writer] writer
|
71
|
+
# # @yieldreturn [void]
|
72
|
+
# # @return [ActsAsTable::CustomFormat::Writer]
|
73
|
+
# def writer(*args, &block)
|
74
|
+
# Writer.new(*args, &block)
|
75
|
+
# end
|
76
|
+
# end
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
def self.for(format)
|
80
|
+
# @return [Hash<Symbol, Module>]
|
81
|
+
module_by_format = self.config.formats.collect { |const_name|
|
82
|
+
self.const_get(const_name, false)
|
83
|
+
}.inject({}) { |acc, m|
|
84
|
+
acc[m.format] ||= m
|
85
|
+
acc
|
86
|
+
}
|
87
|
+
|
88
|
+
unless module_by_format.key?(format)
|
89
|
+
raise ::ArgumentError.new("invalid format - expected: #{module_by_format.keys.inspect}, found: #{format.inspect}")
|
90
|
+
end
|
91
|
+
|
92
|
+
module_by_format[format]
|
93
|
+
end
|
94
|
+
|
95
|
+
# Uses the given ActsAsTable adapter object within the scope of the execution of the given block.
|
96
|
+
#
|
97
|
+
# If block given, yield with no arguments and return the result. Otherwise, return `nil`.
|
98
|
+
#
|
99
|
+
# @param [ActsAsTable::Adapter] new_adapter
|
100
|
+
# @yieldreturn [Object]
|
101
|
+
# @return [Object, nil]
|
102
|
+
def self.use(new_adapter, &block)
|
103
|
+
# @return [Object, nil]
|
104
|
+
result = nil
|
105
|
+
|
106
|
+
if block_given?
|
107
|
+
# @return [ActsAsTable::Adapter]
|
108
|
+
orig_adapter = self.config.adapter
|
109
|
+
|
110
|
+
begin
|
111
|
+
self.config.adapter = new_adapter
|
112
|
+
|
113
|
+
result = block.call
|
114
|
+
ensure
|
115
|
+
self.config.adapter = orig_adapter
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
result
|
120
|
+
end
|
121
|
+
|
122
|
+
autoload :VERSION
|
123
|
+
|
124
|
+
# ActsAsTable configuration object.
|
125
|
+
class Configuration
|
126
|
+
include ::Singleton
|
127
|
+
|
128
|
+
# @!attribute [rw] adapter
|
129
|
+
# Returns the ActsAsTable adapter object (default: `ActsAsTable::Adapter.new`).
|
130
|
+
#
|
131
|
+
# @return [ActsAsTable::Adapter]
|
132
|
+
attr_accessor :adapter
|
133
|
+
|
134
|
+
# @!attribute [rw] formats
|
135
|
+
# Returns the non-inherited constant names for available ActsAsTable serialization format modules (default: `[]`).
|
136
|
+
#
|
137
|
+
# @return [Array<Symbol>]
|
138
|
+
attr_accessor :formats
|
139
|
+
|
140
|
+
# @!attribute [rw] belongs_tos_table
|
141
|
+
# Returns the table name for the {ActsAsTable::BelongsTo} class (default: `:belongs_tos`).
|
142
|
+
#
|
143
|
+
# @return [Symbol]
|
144
|
+
# @!attribute [rw] column_models_table
|
145
|
+
# Returns the table name for the {ActsAsTable::ColumnModel} class (default: `:column_models`).
|
146
|
+
#
|
147
|
+
# @return [Symbol]
|
148
|
+
# @!attribute [rw] foreign_key_maps_table
|
149
|
+
# Returns the table name for the {ActsAsTable::ForeignKeyMap} class (default: `:foreign_key_maps`).
|
150
|
+
#
|
151
|
+
# @return [Symbol]
|
152
|
+
# @!attribute [rw] foreign_keys_table
|
153
|
+
# Returns the table name for the {ActsAsTable::ForeignKey} class (default: `:foreign_key`).
|
154
|
+
#
|
155
|
+
# @return [Symbol]
|
156
|
+
# @!attribute [rw] has_manies_table
|
157
|
+
# Returns the table name for the {ActsAsTable::HasMany} class (default: `:has_manies`).
|
158
|
+
#
|
159
|
+
# @return [Symbol]
|
160
|
+
# @!attribute [rw] has_many_targets_table
|
161
|
+
# Returns the table name for the {ActsAsTable::HasManyTarget} class (default: `:has_many_targets`).
|
162
|
+
#
|
163
|
+
# @return [Symbol]
|
164
|
+
# @!attribute [rw] lenses_table
|
165
|
+
# Returns the table name for the {ActsAsTable::Lense} class (default: `:lenses`).
|
166
|
+
#
|
167
|
+
# @return [Symbol]
|
168
|
+
# @!attribute [rw] primary_keys_table
|
169
|
+
# Returns the table name for the {ActsAsTable::PrimaryKey} class (default: `:primary_keys`).
|
170
|
+
#
|
171
|
+
# @return [Symbol]
|
172
|
+
# @!attribute [rw] record_errors_table
|
173
|
+
# Returns the table name for the {ActsAsTable::RecordError} class (default: `:record_errors`).
|
174
|
+
#
|
175
|
+
# @return [Symbol]
|
176
|
+
# @!attribute [rw] record_models_table
|
177
|
+
# Returns the table name for the {ActsAsTable::RecordModel} class (default: `:record_models`).
|
178
|
+
#
|
179
|
+
# @return [Symbol]
|
180
|
+
# @!attribute [rw] records_table
|
181
|
+
# Returns the table name for the {ActsAsTable::Record} class (default: `:records`).
|
182
|
+
#
|
183
|
+
# @return [Symbol]
|
184
|
+
# @!attribute [rw] row_models_table
|
185
|
+
# Returns the table name for the {ActsAsTable::RowModel} class (default: `:row_model`).
|
186
|
+
#
|
187
|
+
# @return [Symbol]
|
188
|
+
# @!attribute [rw] tables_table
|
189
|
+
# Returns the table name for the {ActsAsTable::Table} class (default: `:table`).
|
190
|
+
#
|
191
|
+
# @return [Symbol]
|
192
|
+
# @!attribute [rw] values_table
|
193
|
+
# Returns the table name for the {ActsAsTable::Value} class (default: `:value`).
|
194
|
+
#
|
195
|
+
# @return [Symbol]
|
196
|
+
%w(BelongsTo ColumnModel ForeignKey ForeignKeyMap HasMany HasManyTarget Lense PrimaryKey Record RecordError RecordModel RowModel Table Value).each do |class_name|
|
197
|
+
attr_accessor :"#{class_name.pluralize.underscore}_table"
|
198
|
+
end
|
199
|
+
|
200
|
+
# Returns a new ActsAsTable configuration object.
|
201
|
+
#
|
202
|
+
# @return [ActsAsTable::Configuration]
|
203
|
+
def initialize
|
204
|
+
@adapter = ActsAsTable::Adapter.new
|
205
|
+
|
206
|
+
@formats = []
|
207
|
+
|
208
|
+
@belongs_tos_table = :belongs_tos
|
209
|
+
@column_models_table = :column_models
|
210
|
+
@foreign_key_maps_table = :foreign_key_maps
|
211
|
+
@foreign_keys_table = :foreign_keys
|
212
|
+
@has_manies_table = :has_manies
|
213
|
+
@has_many_targets_table = :has_many_targets
|
214
|
+
@lenses_table = :lenses
|
215
|
+
@primary_keys_table = :primary_keys
|
216
|
+
@record_errors_table = :record_errors
|
217
|
+
@record_models_table = :record_models
|
218
|
+
@records_table = :records
|
219
|
+
@row_models_table = :row_models
|
220
|
+
@tables_table = :tables
|
221
|
+
@values_table = :values
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Returns the ActsAsTable configuration object.
|
226
|
+
#
|
227
|
+
# @return [ActsAsTable::Configuration]
|
228
|
+
def self.config
|
229
|
+
Configuration.instance
|
230
|
+
end
|
231
|
+
|
232
|
+
# Configure ActsAsTable.
|
233
|
+
#
|
234
|
+
# @yieldreturn [void]
|
235
|
+
# @return [void]
|
236
|
+
#
|
237
|
+
# @example Set the ActsAsTable adapter object.
|
238
|
+
# class CustomActsAsTableAdapter < ActsAsTable::Adapter
|
239
|
+
# # ...
|
240
|
+
# end
|
241
|
+
#
|
242
|
+
# ActsAsTable.configure do
|
243
|
+
# config.adapter = CustomActsAsTableAdapter.new
|
244
|
+
# end
|
245
|
+
#
|
246
|
+
# @example Register an ActsAsTable serialization format module.
|
247
|
+
# require 'acts_as_table_custom_format' # underscore
|
248
|
+
#
|
249
|
+
# ActsAsTable.configure do
|
250
|
+
# config.formats << :CustomFormat # constantize
|
251
|
+
# end
|
252
|
+
#
|
253
|
+
# @example Prefix the table names for the ActsAsTable model classes.
|
254
|
+
# ActsAsTable.configure do
|
255
|
+
# config.methods.select { |method_name| method_name.to_s.ends_with?("_table") }.each do |method_name|
|
256
|
+
# config.send(:"#{method_name}=", :"prefix_#{config.send(method_name)}")
|
257
|
+
# end
|
258
|
+
# end
|
259
|
+
#
|
260
|
+
def self.configure(&block)
|
261
|
+
if block_given?
|
262
|
+
self.instance_eval(&block)
|
263
|
+
end
|
264
|
+
|
265
|
+
return
|
266
|
+
end
|
267
|
+
|
268
|
+
# Delegates to ActsAsTable configuration object.
|
269
|
+
#
|
270
|
+
# @param [String] method_name
|
271
|
+
# @param [Array<Object>] args
|
272
|
+
# @yield [*args, &block]
|
273
|
+
# @yieldreturn [Object]
|
274
|
+
# @return [Object]
|
275
|
+
# @raise [NoMethodError]
|
276
|
+
def self.method_missing(method_name, *args, &block)
|
277
|
+
self.config.respond_to?(method_name, false) ? self.config.send(method_name, *args, &block) : super(method_name, *args, &block)
|
278
|
+
end
|
279
|
+
|
280
|
+
# Delegates to ActsAsTable configuration object.
|
281
|
+
#
|
282
|
+
# @param [String] method_name
|
283
|
+
# @param [Boolean] include_all
|
284
|
+
# @return [Boolean]
|
285
|
+
def self.respond_to?(method_name, include_all = false)
|
286
|
+
self.config.respond_to?(method_name, false) || super(method_name, include_all)
|
287
|
+
end
|
288
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module ActsAsTable
|
2
|
+
# ActsAsTable adapter object.
|
3
|
+
#
|
4
|
+
# @note The "adapter pattern" is a software design pattern that is used to make existing classes work with others without modifying their source code.
|
5
|
+
#
|
6
|
+
# @see ActsAsTable.use
|
7
|
+
# @see ActsAsTable::Configuration#adapter
|
8
|
+
# @see ActsAsTable::Configuration#adapter=
|
9
|
+
class Adapter
|
10
|
+
# Finds the first record with the given attributes, or creates a record with the attributes if one is not found.
|
11
|
+
#
|
12
|
+
# @param [ActsAsTable::RecordModel] record_model
|
13
|
+
# @param [#find_or_initialize_by] callee
|
14
|
+
# @param [Symbol] method_name
|
15
|
+
# @param [Array<Object>] args
|
16
|
+
# @yieldparam [ActiveRecord::Base] base
|
17
|
+
# @yieldreturn [void]
|
18
|
+
# @return [ActiveRecord::Base]
|
19
|
+
def find_or_initialize_by_for(record_model, callee, method_name = :find_or_initialize_by, *args, &block)
|
20
|
+
callee.send(method_name, *args, &block)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Initializes new record from relation while maintaining the current scope.
|
24
|
+
#
|
25
|
+
# @param [ActsAsTable::RecordModel] record_model
|
26
|
+
# @param [#new] callee
|
27
|
+
# @param [Symbol] method_name
|
28
|
+
# @param [Array<Object>] args
|
29
|
+
# @yieldparam [ActiveRecord::Base] base
|
30
|
+
# @yieldreturn [void]
|
31
|
+
# @return [ActiveRecord::Base]
|
32
|
+
def new_for(record_model, callee, method_name = :new, *args, &block)
|
33
|
+
callee.send(method_name, *args, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Get the value for the given record using the given options.
|
37
|
+
#
|
38
|
+
# @param [#get_value] value_provider
|
39
|
+
# @param [ActiveRecord::Base, nil] base
|
40
|
+
# @param [Hash<Symbol, Object>] options
|
41
|
+
# @option options [Boolean] :default
|
42
|
+
# @return [ActsAsTable::ValueProvider::WrappedValue]
|
43
|
+
def get_value_for(value_provider, base = nil, **options)
|
44
|
+
value_provider.get_value(base, **options)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Set the new value for the given record using the given options.
|
48
|
+
#
|
49
|
+
# @param [#set_value] value_provider
|
50
|
+
# @param [ActiveRecord::Base, nil] base
|
51
|
+
# @param [Object, nil] new_value
|
52
|
+
# @param [Hash<Symbol, Object>] options
|
53
|
+
# @option options [Boolean] :default
|
54
|
+
# @return [ActsAsTable::ValueProvider::WrappedValue]
|
55
|
+
def set_value_for(value_provider, base = nil, new_value = nil, **options)
|
56
|
+
value_provider.set_value(base, new_value, **options)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns a new ActsAsTable wrapped value object.
|
60
|
+
#
|
61
|
+
# @param [ActsAsTable::ValueProvider::InstanceMethods] value_provider
|
62
|
+
# @param [ActiveRecord::Base, nil] base
|
63
|
+
# @param [Object, nil] source_value
|
64
|
+
# @param [Object, nil] target_value
|
65
|
+
# @param [Hash<Symbol, Object>] options
|
66
|
+
# @option options [Boolean] :changed
|
67
|
+
# @option options [Boolean] :default
|
68
|
+
# @return [ActsAsTable::ValueProvider::WrappedValue]
|
69
|
+
def wrap_value_for(value_provider, base = nil, source_value = nil, target_value = nil, **options)
|
70
|
+
ActsAsTable::ValueProvider::WrappedValue.new(value_provider, base, source_value, target_value, **options)
|
71
|
+
end
|
72
|
+
|
73
|
+
def classify_for(value_provider, table_name)
|
74
|
+
table_name.classify
|
75
|
+
end
|
76
|
+
|
77
|
+
def tableize_for(value_provider, class_name)
|
78
|
+
class_name.tableize
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ActsAsTable
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace ActsAsTable
|
4
|
+
|
5
|
+
initializer 'acts_as_table.active_record' do |app|
|
6
|
+
::ActiveSupport.on_load(:active_record) do
|
7
|
+
::ActiveRecord::Base.class_eval do
|
8
|
+
include ActsAsTable::ValueProvider
|
9
|
+
include ActsAsTable::ValueProviderAssociationMethods
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
module ActsAsTable
|
2
|
+
# ActsAsTable headers.
|
3
|
+
module Headers
|
4
|
+
# ActsAsTable headers array object.
|
5
|
+
#
|
6
|
+
# @!attribute [r] column_models
|
7
|
+
# Returns the ActsAsTable column models for this ActsAsTable headers array object.
|
8
|
+
#
|
9
|
+
# @return [Enumerable<ActsAsTable::ColumnModel>]
|
10
|
+
class Array < ::Array
|
11
|
+
attr_reader :column_models
|
12
|
+
|
13
|
+
# Returns a new ActsAsTable headers array object.
|
14
|
+
#
|
15
|
+
# @param [Enumerable<ActsAsTable::ColumnModel>] column_models
|
16
|
+
# @return [ActsAsTable::Headers::Array]
|
17
|
+
def initialize(column_models)
|
18
|
+
@column_models = column_models.to_a
|
19
|
+
|
20
|
+
# @return [Array<Array<String>>]
|
21
|
+
header_names_without_padding = @column_models.collect { |column_model|
|
22
|
+
column_model.name.split(column_model.separator)
|
23
|
+
}
|
24
|
+
|
25
|
+
# @return [Integer]
|
26
|
+
max_header_names_without_padding_size = header_names_without_padding.collect(&:size).max || 0
|
27
|
+
|
28
|
+
# @return [Array<Array<String, nil>>]
|
29
|
+
@header_names_with_padding = header_names_without_padding.collect { |split_name|
|
30
|
+
split_name + ::Array.new(max_header_names_without_padding_size - split_name.size) { nil }
|
31
|
+
}.collect { |header_names|
|
32
|
+
header_names.freeze
|
33
|
+
}.freeze
|
34
|
+
|
35
|
+
# @return [Array<Array<String, nil>>]
|
36
|
+
headers = @header_names_with_padding.transpose.collect(&:freeze)
|
37
|
+
|
38
|
+
super(headers)
|
39
|
+
|
40
|
+
self.freeze
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns a 3-level array indexed by the header index, the header name index and the pair index.
|
44
|
+
#
|
45
|
+
# The elements in the 3rd level are pairs, where the first element is the header name and the second element is the ActsAsTable column models count for the header name.
|
46
|
+
#
|
47
|
+
# @note This method is intended to be used to render tables with merged cells in the header rows (where the ActsAsTable column models count is the column span).
|
48
|
+
#
|
49
|
+
# @return [Array<Array<Array<Object>>>]
|
50
|
+
#
|
51
|
+
# @example Render HTML "thead" element with merged "th" elements.
|
52
|
+
# <thead>
|
53
|
+
# <% @row_model.to_headers.with_column_models_count.each do |header| %>
|
54
|
+
# <tr>
|
55
|
+
# <% header.each do |pair| %>
|
56
|
+
# <%= content_tag(:th, pair[0], colspan: pair[1], scope: 'col') %>
|
57
|
+
# <% end %>
|
58
|
+
# </tr>
|
59
|
+
# <% end %>
|
60
|
+
# </thead>
|
61
|
+
#
|
62
|
+
def with_column_models_count
|
63
|
+
# Drop the last row (the corresponding instances of the {ActsAsTable::ColumnModel} class).
|
64
|
+
@with_column_models_count ||= Hash.for(@column_models, @header_names_with_padding).to_array[0..-2]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# ActsAsTable headers hash object.
|
69
|
+
#
|
70
|
+
# @!attribute [r] column_models_count
|
71
|
+
# Returns the ActsAsTable column models count for this ActsAsTable headers hash object.
|
72
|
+
#
|
73
|
+
# @return [Integer]
|
74
|
+
class Hash < ::Hash
|
75
|
+
# Returns a new ActsAsTable headers hash object for the given ActsAsTable column models and header names.
|
76
|
+
#
|
77
|
+
# @param [Enumerable<ActsAsTable::ColumnModel>] column_models
|
78
|
+
# @param [Array<Array<String, nil>>] header_names_with_padding
|
79
|
+
# @return [ActsAsTable::Headers::Hash]
|
80
|
+
def self.for(column_models, header_names_with_padding)
|
81
|
+
# @!method _block(orig_hash, counter)
|
82
|
+
# Returns the new ActsAsTable headers hash object with accumulated ActsAsTable column models count.
|
83
|
+
#
|
84
|
+
# @param [ActsAsTable::Headers::Hash] orig_hash
|
85
|
+
# @param [Integer] counter
|
86
|
+
# @return [ActsAsTable::Headers::Hash]
|
87
|
+
_block = ::Proc.new { |orig_hash, counter|
|
88
|
+
# @return [Hash<String, Object>]
|
89
|
+
new_hash = orig_hash.each_pair.inject({}) { |acc, pair|
|
90
|
+
key, value = *pair
|
91
|
+
|
92
|
+
case value
|
93
|
+
when ::Array
|
94
|
+
counter += value.size
|
95
|
+
|
96
|
+
# @return [Array<ActsAsTable::ColumnModel>]
|
97
|
+
acc[key] = value
|
98
|
+
when ::Hash
|
99
|
+
# @return [ActsAsTable::Headers::Hash]
|
100
|
+
new_hash_for_value = _block.call(value, 0)
|
101
|
+
|
102
|
+
counter += new_hash_for_value.column_models_count
|
103
|
+
|
104
|
+
# @return [ActsAsTable::Headers::Hash]
|
105
|
+
acc[key] = new_hash_for_value
|
106
|
+
end
|
107
|
+
|
108
|
+
acc
|
109
|
+
}
|
110
|
+
|
111
|
+
self.new(counter).merge(new_hash).freeze
|
112
|
+
}
|
113
|
+
|
114
|
+
# @return [Hash<String, Object>]
|
115
|
+
orig_hash = header_names_with_padding.each_with_index.inject({}) { |acc, pair|
|
116
|
+
header_names, index = *pair
|
117
|
+
|
118
|
+
# @return [Integer]
|
119
|
+
max_header_names_index = header_names.size - 1
|
120
|
+
|
121
|
+
# @return [Hash<String, Object>]
|
122
|
+
column_models_for_index = header_names.each_with_index.inject(acc) { |header_acc, pair|
|
123
|
+
header_name, header_name_index, = *pair
|
124
|
+
|
125
|
+
if header_name_index == max_header_names_index
|
126
|
+
header_acc[header_name] ||= []
|
127
|
+
header_acc[header_name] << column_models[index]
|
128
|
+
else
|
129
|
+
header_acc[header_name] ||= {}
|
130
|
+
end
|
131
|
+
|
132
|
+
header_acc[header_name]
|
133
|
+
}
|
134
|
+
|
135
|
+
column_models_for_index.freeze
|
136
|
+
|
137
|
+
acc
|
138
|
+
}
|
139
|
+
|
140
|
+
_block.call(orig_hash, 0)
|
141
|
+
end
|
142
|
+
|
143
|
+
attr_reader :column_models_count
|
144
|
+
|
145
|
+
# Returns a new ActsAsTable headers hash object.
|
146
|
+
#
|
147
|
+
# @param [Integer] column_models_count
|
148
|
+
# @param [Array<Object>] args
|
149
|
+
# @yieldparam [ActsAsTable::Headers::Hash] hash
|
150
|
+
# @yieldparam [String] key
|
151
|
+
# @yieldreturn [ActsAsTable::Headers::Hash, ActsAsTable::ColumnModel]
|
152
|
+
# @return [ActsAsTable::Headers::Hash]
|
153
|
+
def initialize(column_models_count, *args, &block)
|
154
|
+
super(*args, &block)
|
155
|
+
|
156
|
+
@column_models_count = column_models_count
|
157
|
+
end
|
158
|
+
|
159
|
+
# Returns this ActsAsTable headers hash object as an array.
|
160
|
+
#
|
161
|
+
# @return [Array<Array<Object>>]
|
162
|
+
def to_array
|
163
|
+
# @!method _block(acc, hash_with_column_models_count, depth)
|
164
|
+
# Returns the new ActsAsTable headers hash object with accumulated ActsAsTable column models count.
|
165
|
+
#
|
166
|
+
# @param [Array<Array<Object>>] acc
|
167
|
+
# @param [ActsAsTable::Headers::Hash] hash_with_column_models_count
|
168
|
+
# @param [Integer] depth
|
169
|
+
# @return [ActsAsTable::Headers::Hash]
|
170
|
+
_block = ::Proc.new { |acc, hash_with_column_models_count, depth|
|
171
|
+
hash_with_column_models_count.each do |key, hash_with_column_models_count_or_column_models|
|
172
|
+
case hash_with_column_models_count_or_column_models
|
173
|
+
when ::Array
|
174
|
+
acc[depth] ||= []
|
175
|
+
acc[depth] << [key, hash_with_column_models_count_or_column_models.size]
|
176
|
+
|
177
|
+
hash_with_column_models_count_or_column_models.each do |column_model|
|
178
|
+
acc[depth + 1] ||= []
|
179
|
+
acc[depth + 1] << [column_model]
|
180
|
+
end
|
181
|
+
when ::Hash
|
182
|
+
acc[depth] ||= []
|
183
|
+
acc[depth] << [key, hash_with_column_models_count_or_column_models.column_models_count]
|
184
|
+
|
185
|
+
_block.call(acc, hash_with_column_models_count_or_column_models, depth + 1)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
acc
|
190
|
+
}
|
191
|
+
|
192
|
+
_block.call([], self, 0)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|