acts_as_table 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +1 -0
  3. data/LICENSE +30 -0
  4. data/README.md +256 -0
  5. data/Rakefile +13 -0
  6. data/app/models/acts_as_table/belongs_to.rb +81 -0
  7. data/app/models/acts_as_table/column_model.rb +89 -0
  8. data/app/models/acts_as_table/foreign_key.rb +299 -0
  9. data/app/models/acts_as_table/foreign_key_map.rb +121 -0
  10. data/app/models/acts_as_table/has_many.rb +90 -0
  11. data/app/models/acts_as_table/has_many_target.rb +40 -0
  12. data/app/models/acts_as_table/lense.rb +131 -0
  13. data/app/models/acts_as_table/primary_key.rb +108 -0
  14. data/app/models/acts_as_table/record.rb +109 -0
  15. data/app/models/acts_as_table/record_error.rb +50 -0
  16. data/app/models/acts_as_table/record_model.rb +194 -0
  17. data/app/models/acts_as_table/row_model.rb +285 -0
  18. data/app/models/acts_as_table/table.rb +41 -0
  19. data/app/models/acts_as_table/value.rb +80 -0
  20. data/app/models/concerns/acts_as_table/record_model_class_methods.rb +554 -0
  21. data/app/models/concerns/acts_as_table/value_provider.rb +223 -0
  22. data/app/models/concerns/acts_as_table/value_provider_association_methods.rb +57 -0
  23. data/config/locales/en.yml +8 -0
  24. data/db/migrate/1_acts_as_table_migration.rb +186 -0
  25. data/lib/acts_as_table.rb +288 -0
  26. data/lib/acts_as_table/adapter.rb +81 -0
  27. data/lib/acts_as_table/engine.rb +14 -0
  28. data/lib/acts_as_table/headers.rb +196 -0
  29. data/lib/acts_as_table/mapper.rb +464 -0
  30. data/lib/acts_as_table/path.rb +157 -0
  31. data/lib/acts_as_table/reader.rb +149 -0
  32. data/lib/acts_as_table/version.rb +4 -0
  33. data/lib/acts_as_table/writer.rb +116 -0
  34. 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