csv_row_model 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -12
- data/lib/csv_row_model/concerns/inherited_class_var.rb +39 -1
- data/lib/csv_row_model/concerns/invalid_options.rb +17 -0
- data/lib/csv_row_model/export/attributes.rb +52 -0
- data/lib/csv_row_model/export/dynamic_columns.rb +5 -0
- data/lib/csv_row_model/export/file.rb +16 -2
- data/lib/csv_row_model/export.rb +4 -25
- data/lib/csv_row_model/import/csv.rb +5 -8
- data/lib/csv_row_model/import/presenter.rb +17 -36
- data/lib/csv_row_model/import.rb +1 -1
- data/lib/csv_row_model/model/children.rb +3 -14
- data/lib/csv_row_model/model/columns.rb +13 -15
- data/lib/csv_row_model/model/dynamic_columns.rb +16 -23
- data/lib/csv_row_model/model.rb +0 -4
- data/lib/csv_row_model/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1659654bb54054a7ee93eacb161b4aeb1d6dcca7
|
4
|
+
data.tar.gz: b3f0d04925efa748ac1cbab7ca9445824d5b9f3d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a2576d69bb8c8ee6116a3df5041625fac583068ba9711db8caa37c2420553b0d03b2aa7fd2f84d3735b3a28a95b091398a9a5dd244b4cada34cbdab611bd7e7
|
7
|
+
data.tar.gz: c5cc99b9063d8529fdf6b4dc8dc507cf3df60165ca1c3e5347499f6af1baf5ee7e8a04d4d4b38cc0020240d6cb7331480a5080aa37bda2b2d337c179039b1620
|
data/README.md
CHANGED
@@ -289,7 +289,7 @@ class ProjectImportRowModel < ProjectRowModel
|
|
289
289
|
|
290
290
|
# this is shorthand for the psuedo_code:
|
291
291
|
# def project
|
292
|
-
# return if row_model.id.
|
292
|
+
# return if row_model.id.blank? || row_model.name.blank?
|
293
293
|
#
|
294
294
|
# # turn off memoziation with `memoize: false` option
|
295
295
|
# @project ||= __the_code_inside_the_block__
|
@@ -328,9 +328,9 @@ Also, the `attribute` defines a dynamic `#project` method that:
|
|
328
328
|
|
329
329
|
1. Memoizes by default, turn off with `memoize: false` option
|
330
330
|
2. All errors of `row_model` are propagated to the presenter when calling `presenter.valid?`
|
331
|
-
3. Handles dependencies
|
332
|
-
-
|
333
|
-
- `presenter.errors` for dependencies are cleaned. For the example above, if `row_model.id/name` are `invalid?`, then
|
331
|
+
3. Handles dependencies:
|
332
|
+
- When any of the dependencies are `blank?`, the attribute block is not called and the attribute returns `nil`.
|
333
|
+
- When any of the dependencies are `invalid?`, `presenter.errors` for dependencies are cleaned. For the example above, if `row_model.id/name` are `invalid?`, then
|
334
334
|
the `:project` key is removed from the errors, so: `presenter.errors.keys # => [:id, :name]`
|
335
335
|
|
336
336
|
## Import Validations
|
@@ -440,7 +440,8 @@ class DynamicColumnModel
|
|
440
440
|
|
441
441
|
column :first_name
|
442
442
|
column :last_name
|
443
|
-
|
443
|
+
# header is optional, below is the default_implementation
|
444
|
+
dynamic_column :skills, header: ->(skill_name) { skill_name }
|
444
445
|
end
|
445
446
|
```
|
446
447
|
|
@@ -465,13 +466,6 @@ class DynamicColumnExportModel < DynamicColumnModel
|
|
465
466
|
# below is an override, this is the default implementation: skill_name # => "skill1", then "skill2"
|
466
467
|
source_model.skills.include?(skill_name) ? "Yes" : "No"
|
467
468
|
end
|
468
|
-
|
469
|
-
class << self
|
470
|
-
# this is an override with the default implementation
|
471
|
-
def skill_header(skill_name)
|
472
|
-
skill_name
|
473
|
-
end
|
474
|
-
end
|
475
469
|
end
|
476
470
|
|
477
471
|
# the `skills` context is mapped to generate an array
|
@@ -1,8 +1,14 @@
|
|
1
|
+
require 'csv_row_model/concerns/invalid_options'
|
2
|
+
|
1
3
|
module CsvRowModel
|
2
4
|
module Concerns
|
3
5
|
module InheritedClassVar
|
4
6
|
extend ActiveSupport::Concern
|
5
7
|
|
8
|
+
included do
|
9
|
+
include InvalidOptions
|
10
|
+
end
|
11
|
+
|
6
12
|
class_methods do
|
7
13
|
# Clears the cache for a variable
|
8
14
|
# @param variable_name [Symbol] variable_name to cache against
|
@@ -12,9 +18,41 @@ module CsvRowModel
|
|
12
18
|
|
13
19
|
protected
|
14
20
|
|
21
|
+
# @param variable_name [Symbol] class variable name
|
22
|
+
def inherited_class_hash(variable_name, options={})
|
23
|
+
options = check_and_merge_options(options, dependencies: [])
|
24
|
+
|
25
|
+
options[:dependencies].each do |dependency_name|
|
26
|
+
define_singleton_method dependency_name do
|
27
|
+
class_cache(hidden_variable_name(dependency_name)) do
|
28
|
+
send("_#{dependency_name}")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
hidden_variable_name = hidden_variable_name(variable_name)
|
34
|
+
define_singleton_method variable_name do
|
35
|
+
inherited_class_var(hidden_variable_name, {}, :merge)
|
36
|
+
end
|
37
|
+
define_singleton_method "merge_#{variable_name}" do |merge_value|
|
38
|
+
value = instance_variable_get(hidden_variable_name) || instance_variable_set(hidden_variable_name, {})
|
39
|
+
|
40
|
+
deep_clear_class_cache(hidden_variable_name)
|
41
|
+
options[:dependencies].each {|dependency_name| deep_clear_class_cache(hidden_variable_name(dependency_name)) }
|
42
|
+
|
43
|
+
value.merge!(merge_value)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# @param variable_name [Symbol] class variable name based on
|
48
|
+
# @return [Symbol] the hidden variable name for class_cache
|
49
|
+
def hidden_variable_name(variable_name)
|
50
|
+
"@_#{variable_name}".to_sym
|
51
|
+
end
|
52
|
+
|
15
53
|
# @param included_module [Module] module to search for
|
16
54
|
# @return [Array<Module>] inherited_ancestors of included_module (including self)
|
17
|
-
def inherited_ancestors(included_module=
|
55
|
+
def inherited_ancestors(included_module=CsvRowModel::Concerns::InheritedClassVar)
|
18
56
|
included_model_index = ancestors.index(included_module)
|
19
57
|
included_model_index == 0 ? [included_module] : ancestors[0..(included_model_index - 1)]
|
20
58
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CsvRowModel
|
2
|
+
module Concerns
|
3
|
+
module InvalidOptions
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
class_methods do
|
7
|
+
protected
|
8
|
+
def check_and_merge_options(options, default_options)
|
9
|
+
invalid_options = options.keys - default_options.keys
|
10
|
+
raise ArgumentError.new("Invalid option(s): #{invalid_options}") if invalid_options.present?
|
11
|
+
|
12
|
+
options.reverse_merge(default_options)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module CsvRowModel
|
2
|
+
module Export
|
3
|
+
module Attributes
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
self.column_names.each { |*args| define_attribute_method(*args) }
|
8
|
+
end
|
9
|
+
|
10
|
+
# @return [Hash] a map of `column_name => self.class.format_cell()public_send(column_name))`
|
11
|
+
def formatted_attributes
|
12
|
+
formatted_attributes_from_column_names self.class.column_names
|
13
|
+
end
|
14
|
+
|
15
|
+
def formatted_attribute(column_name)
|
16
|
+
self.class.format_cell(
|
17
|
+
public_send(column_name),
|
18
|
+
column_name,
|
19
|
+
self.class.index(column_name)
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
def formatted_attributes_from_column_names(column_names)
|
25
|
+
array_to_block_hash(column_names) { |column_name| formatted_attribute(column_name) }
|
26
|
+
end
|
27
|
+
|
28
|
+
class_methods do
|
29
|
+
# See {Model#column}
|
30
|
+
def column(column_name, options={})
|
31
|
+
super
|
32
|
+
define_attribute_method(column_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Define default attribute method for a column
|
36
|
+
# @param column_name [Symbol] the cell's column_name
|
37
|
+
def define_attribute_method(column_name)
|
38
|
+
define_method(column_name) do
|
39
|
+
source_model.public_send(column_name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Safe to override. Method applied to each cell by default
|
44
|
+
#
|
45
|
+
# @param cell [Object] the cell's value
|
46
|
+
def format_cell(cell, column_name, column_index)
|
47
|
+
cell
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -16,10 +16,13 @@ module CsvRowModel
|
|
16
16
|
# Add a row_model to the
|
17
17
|
# @param [] source_model the source model of the export row model
|
18
18
|
# @param [Hash] context the extra context given to the instance of the row model
|
19
|
+
# @return [CsvRowModel::Export] the row model appended
|
19
20
|
def append_model(source_model, context={})
|
20
|
-
export_model_class.new(source_model, context.
|
21
|
+
row_model = export_model_class.new(source_model, context.reverse_merge(self.context))
|
22
|
+
row_model.to_rows.each do |row|
|
21
23
|
csv << row
|
22
24
|
end
|
25
|
+
row_model
|
23
26
|
end
|
24
27
|
alias_method :<<, :append_model
|
25
28
|
|
@@ -35,7 +38,7 @@ module CsvRowModel
|
|
35
38
|
CSV.open(file.path, "wb") do |csv|
|
36
39
|
@csv = csv
|
37
40
|
export_model_class.setup(csv, context, with_headers: with_headers)
|
38
|
-
yield self
|
41
|
+
yield Proxy.new(self)
|
39
42
|
end
|
40
43
|
ensure
|
41
44
|
@csv = nil
|
@@ -44,6 +47,17 @@ module CsvRowModel
|
|
44
47
|
def to_s
|
45
48
|
file.read
|
46
49
|
end
|
50
|
+
|
51
|
+
class Proxy
|
52
|
+
def initialize(file)
|
53
|
+
@file = file
|
54
|
+
end
|
55
|
+
|
56
|
+
def append_model(*args)
|
57
|
+
@file.append_model(*args)
|
58
|
+
end
|
59
|
+
alias_method :<<, :append_model
|
60
|
+
end
|
47
61
|
end
|
48
62
|
end
|
49
63
|
end
|
data/lib/csv_row_model/export.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'csv_row_model/export/dynamic_columns'
|
2
|
+
require 'csv_row_model/export/attributes'
|
2
3
|
|
3
4
|
module CsvRowModel
|
4
5
|
# Include this to with {Model} to have a RowModel for exporting to CSVs.
|
@@ -6,11 +7,10 @@ module CsvRowModel
|
|
6
7
|
extend ActiveSupport::Concern
|
7
8
|
|
8
9
|
included do
|
10
|
+
include Attributes
|
9
11
|
include DynamicColumns
|
10
|
-
attr_reader :source_model, :context
|
11
|
-
|
12
|
-
self.column_names.each { |*args| define_attribute_method(*args) }
|
13
12
|
|
13
|
+
attr_reader :source_model, :context
|
14
14
|
validates :source_model, presence: true
|
15
15
|
end
|
16
16
|
|
@@ -27,34 +27,13 @@ module CsvRowModel
|
|
27
27
|
|
28
28
|
# @return [Array] an array of public_send(column_name) of the CSV model
|
29
29
|
def to_row
|
30
|
-
|
30
|
+
formatted_attributes.values
|
31
31
|
end
|
32
32
|
|
33
33
|
class_methods do
|
34
34
|
def setup(csv, context={}, with_headers: true)
|
35
35
|
csv << headers(context) if with_headers
|
36
36
|
end
|
37
|
-
|
38
|
-
# See {Model#column}
|
39
|
-
def column(column_name, options={})
|
40
|
-
super
|
41
|
-
define_attribute_method(column_name)
|
42
|
-
end
|
43
|
-
|
44
|
-
# Define default attribute method for a column
|
45
|
-
# @param column_name [Symbol] the cell's column_name
|
46
|
-
def define_attribute_method(column_name)
|
47
|
-
define_method(column_name) do
|
48
|
-
self.class.format_cell(source_model.public_send(column_name), column_name, self.class.index(column_name))
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
# Safe to override. Method applied to each cell by default
|
53
|
-
#
|
54
|
-
# @param cell [Object] the cell's value
|
55
|
-
def format_cell(cell, column_name, column_index)
|
56
|
-
cell
|
57
|
-
end
|
58
37
|
end
|
59
38
|
end
|
60
39
|
end
|
@@ -102,16 +102,13 @@ module CsvRowModel
|
|
102
102
|
def _read_row(skipped_rows={}, index=@index, ruby_csv=@ruby_csv)
|
103
103
|
return unless valid?
|
104
104
|
|
105
|
-
|
106
|
-
|
105
|
+
row = ruby_csv.readline
|
106
|
+
raise "empty?" if row.try(:empty?)
|
107
107
|
|
108
|
-
|
109
|
-
|
110
|
-
index += 1 if index
|
108
|
+
index += 1 if index
|
111
109
|
|
112
|
-
|
113
|
-
|
114
|
-
end
|
110
|
+
yield row if block_given?
|
111
|
+
row
|
115
112
|
rescue Exception => e
|
116
113
|
index += 1 if index
|
117
114
|
yield [] if block_given?
|
@@ -5,6 +5,8 @@ module CsvRowModel
|
|
5
5
|
include Concerns::Inspect
|
6
6
|
include ActiveWarnings
|
7
7
|
|
8
|
+
inherited_class_hash :attributes, dependencies: %i[dependencies]
|
9
|
+
|
8
10
|
attr_reader :row_model
|
9
11
|
|
10
12
|
delegate :context, to: :row_model
|
@@ -64,15 +66,16 @@ module CsvRowModel
|
|
64
66
|
end
|
65
67
|
|
66
68
|
# @param [Array] Array of column_names to check
|
67
|
-
# @return [Boolean] if column_names are
|
68
|
-
def
|
69
|
-
|
69
|
+
# @return [Boolean] if column_names are present
|
70
|
+
def row_model_present?(*column_names)
|
71
|
+
column_names.each { |column_name| return false if row_model.public_send(column_name).blank? }
|
72
|
+
true
|
70
73
|
end
|
71
74
|
|
72
75
|
# @param [Symbol] attribute_name the attribute to check
|
73
76
|
# @return [Boolean] if the dependencies are valid
|
74
77
|
def valid_dependencies?(attribute_name)
|
75
|
-
|
78
|
+
row_model_present?(*self.class.options(attribute_name)[:dependencies])
|
76
79
|
end
|
77
80
|
|
78
81
|
# equal to: @method_name ||= yield
|
@@ -84,20 +87,11 @@ module CsvRowModel
|
|
84
87
|
end
|
85
88
|
|
86
89
|
class << self
|
87
|
-
def inherited_class_module
|
88
|
-
Presenter
|
89
|
-
end
|
90
|
-
|
91
90
|
# @return [Array<Symbol>] attribute names for the Presenter
|
92
91
|
def attribute_names
|
93
92
|
attributes.keys
|
94
93
|
end
|
95
94
|
|
96
|
-
# @return [Hash{Symbol => Array}] map of `attribute_name => [options, block]`
|
97
|
-
def attributes
|
98
|
-
inherited_class_var :@_presenter_attributes, {}, :merge
|
99
|
-
end
|
100
|
-
|
101
95
|
# @param [Symbol] attribute_name name of attribute to find option
|
102
96
|
# @return [Hash] options for the attribute_name
|
103
97
|
def options(attribute_name)
|
@@ -110,32 +104,23 @@ module CsvRowModel
|
|
110
104
|
attributes[attribute_name].last
|
111
105
|
end
|
112
106
|
|
107
|
+
protected
|
113
108
|
# @return [Hash{Symbol => Array}] map of `dependency => [array of presenter attributes dependent on dependency]`
|
114
|
-
def
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
dependencies[dependency] << attribute_name
|
121
|
-
end
|
109
|
+
def _dependencies
|
110
|
+
dependencies = {}
|
111
|
+
attribute_names.each do |attribute_name|
|
112
|
+
options(attribute_name)[:dependencies].each do |dependency|
|
113
|
+
dependencies[dependency] ||= []
|
114
|
+
dependencies[dependency] << attribute_name
|
122
115
|
end
|
123
|
-
dependencies
|
124
116
|
end
|
117
|
+
dependencies
|
125
118
|
end
|
126
119
|
|
127
|
-
protected
|
128
120
|
def inspect_methods
|
129
121
|
@inspect_methods ||= %i[row_model].freeze
|
130
122
|
end
|
131
123
|
|
132
|
-
def merge_attribute(attribute_hash)
|
133
|
-
@_presenter_attributes ||= {}
|
134
|
-
deep_clear_class_cache(:@_presenter_attributes)
|
135
|
-
deep_clear_class_cache(:@_presenter_dependencies)
|
136
|
-
@_presenter_attributes.merge! attribute_hash
|
137
|
-
end
|
138
|
-
|
139
124
|
# Adds column to the row model
|
140
125
|
#
|
141
126
|
# @param [Symbol] attribute_name name of attribute to add
|
@@ -144,13 +129,9 @@ module CsvRowModel
|
|
144
129
|
# @option options [Hash] :memoize whether to memoize the attribute (default: true)
|
145
130
|
# @option options [Hash] :dependencies the dependcies it has with the underlying row_model (default: [])
|
146
131
|
def attribute(attribute_name, options={}, &block)
|
147
|
-
|
148
|
-
invalid_options = options.keys - default_options.keys
|
149
|
-
raise ArgumentError.new("Invalid option(s): #{invalid_options}") if invalid_options.present?
|
150
|
-
|
151
|
-
options = options.reverse_merge(default_options)
|
132
|
+
options = check_and_merge_options(options, memoize: true, dependencies: [])
|
152
133
|
|
153
|
-
|
134
|
+
merge_attributes(attribute_name.to_sym => [options, block])
|
154
135
|
define_attribute_method(attribute_name)
|
155
136
|
end
|
156
137
|
|
data/lib/csv_row_model/import.rb
CHANGED
@@ -12,7 +12,7 @@ module CsvRowModel
|
|
12
12
|
include Attributes
|
13
13
|
include DynamicColumns
|
14
14
|
|
15
|
-
attr_reader :
|
15
|
+
attr_reader :source_header, :source_row, :context, :index, :previous
|
16
16
|
|
17
17
|
self.column_names.each { |*args| define_attribute_method(*args) }
|
18
18
|
|
@@ -2,6 +2,9 @@ module CsvRowModel
|
|
2
2
|
module Model
|
3
3
|
module Children
|
4
4
|
extend ActiveSupport::Concern
|
5
|
+
included do
|
6
|
+
inherited_class_hash :has_many_relationships
|
7
|
+
end
|
5
8
|
|
6
9
|
# @return [Boolean] returns true, if the instance is a child
|
7
10
|
def child?
|
@@ -40,20 +43,6 @@ module CsvRowModel
|
|
40
43
|
end
|
41
44
|
|
42
45
|
class_methods do
|
43
|
-
# Won't work for Export right now
|
44
|
-
#
|
45
|
-
# @return [Hash] map of `relation_name => CsvRowModel::Import or CsvRowModel::Export class`
|
46
|
-
def has_many_relationships
|
47
|
-
inherited_class_var :@_has_many_relationships, {}, :merge
|
48
|
-
end
|
49
|
-
|
50
|
-
protected
|
51
|
-
def merge_has_many_relationships(relation_hash)
|
52
|
-
@_has_many_relationships ||= {}
|
53
|
-
deep_clear_class_cache(:@_has_many_relationships)
|
54
|
-
@_has_many_relationships.merge! relation_hash
|
55
|
-
end
|
56
|
-
|
57
46
|
# Defines a relationship between a row model (only one relation per model for now).
|
58
47
|
#
|
59
48
|
# @param [Symbol] relation_name the name of the relation
|
@@ -2,6 +2,9 @@ module CsvRowModel
|
|
2
2
|
module Model
|
3
3
|
module Columns
|
4
4
|
extend ActiveSupport::Concern
|
5
|
+
included do
|
6
|
+
inherited_class_hash :columns
|
7
|
+
end
|
5
8
|
|
6
9
|
# @return [Hash] a map of `column_name => public_send(column_name)`
|
7
10
|
def attributes
|
@@ -17,10 +20,16 @@ module CsvRowModel
|
|
17
20
|
end
|
18
21
|
|
19
22
|
protected
|
23
|
+
|
20
24
|
def attributes_from_column_names(column_names)
|
21
|
-
column_names
|
22
|
-
|
23
|
-
|
25
|
+
array_to_block_hash(column_names) { |column_name| public_send(column_name) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def array_to_block_hash(array, &block)
|
29
|
+
array
|
30
|
+
.zip(
|
31
|
+
array.map { |column_name| block.call(column_name) }
|
32
|
+
).to_h
|
24
33
|
end
|
25
34
|
|
26
35
|
class_methods do
|
@@ -29,11 +38,6 @@ module CsvRowModel
|
|
29
38
|
columns.keys
|
30
39
|
end
|
31
40
|
|
32
|
-
# @return [Hash] column names mapped to their options
|
33
|
-
def columns
|
34
|
-
inherited_class_var(:@_columns, {}, :merge)
|
35
|
-
end
|
36
|
-
|
37
41
|
# @param [Symbol] column_name name of column to find option
|
38
42
|
# @return [Hash] options for the column_name
|
39
43
|
def options(column_name)
|
@@ -55,7 +59,7 @@ module CsvRowModel
|
|
55
59
|
# @param [Hash, OpenStruct] context name of column to check
|
56
60
|
# @return [Array] column headers for the row model
|
57
61
|
def headers(context={})
|
58
|
-
|
62
|
+
columns.map { |name, options| options[:header] || format_header(name) }
|
59
63
|
end
|
60
64
|
|
61
65
|
# Safe to override
|
@@ -67,12 +71,6 @@ module CsvRowModel
|
|
67
71
|
|
68
72
|
protected
|
69
73
|
|
70
|
-
def merge_columns(column_hash)
|
71
|
-
@_columns ||= {}
|
72
|
-
deep_clear_class_cache(:@_columns)
|
73
|
-
@_columns.merge!(column_hash)
|
74
|
-
end
|
75
|
-
|
76
74
|
VALID_OPTIONS_KEYS = %i[type parse validate_type default header header_matchs].freeze
|
77
75
|
|
78
76
|
# Adds column to the row model
|
@@ -3,6 +3,10 @@ module CsvRowModel
|
|
3
3
|
module DynamicColumns
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
|
+
included do
|
7
|
+
inherited_class_hash :dynamic_columns
|
8
|
+
end
|
9
|
+
|
6
10
|
# See Model::Columns#attributes
|
7
11
|
def attributes
|
8
12
|
super.merge(attributes_from_column_names(self.class.dynamic_column_names))
|
@@ -11,9 +15,14 @@ module CsvRowModel
|
|
11
15
|
class_methods do
|
12
16
|
# See Model::Columns::headers
|
13
17
|
def headers(context={})
|
14
|
-
|
18
|
+
super + dynamic_column_headers(context)
|
19
|
+
end
|
20
|
+
|
21
|
+
def dynamic_column_headers(context={})
|
22
|
+
dynamic_column_names.map do |column_name|
|
15
23
|
OpenStruct.new(context).public_send(column_name).each do |header_model|
|
16
|
-
|
24
|
+
header_proc = dynamic_column_options(column_name)[:header] || ->(header_model) { header_model }
|
25
|
+
instance_exec(header_model, &header_proc)
|
17
26
|
end
|
18
27
|
end.flatten
|
19
28
|
end
|
@@ -24,32 +33,22 @@ module CsvRowModel
|
|
24
33
|
offset ? columns.size + offset : nil
|
25
34
|
end
|
26
35
|
|
36
|
+
def dynamic_column_options(column_name)
|
37
|
+
dynamic_columns[column_name]
|
38
|
+
end
|
39
|
+
|
27
40
|
# @return [Array<Symbol>] column names for the row model
|
28
41
|
def dynamic_column_names
|
29
42
|
dynamic_columns.keys
|
30
43
|
end
|
31
44
|
|
32
|
-
# @return [Hash] column names mapped to their options
|
33
|
-
def dynamic_columns
|
34
|
-
inherited_class_var(:@_dynamic_columns, {}, :merge)
|
35
|
-
end
|
36
|
-
|
37
|
-
def header_method_name(column_name)
|
38
|
-
"#{column_name.to_s.singularize}_header"
|
39
|
-
end
|
40
45
|
def singular_dynamic_attribute_method_name(column_name)
|
41
46
|
column_name.to_s.singularize
|
42
47
|
end
|
43
48
|
|
44
49
|
protected
|
45
50
|
|
46
|
-
|
47
|
-
@_dynamic_columns ||= {}
|
48
|
-
deep_clear_class_cache(:@_dynamic_columns)
|
49
|
-
@_dynamic_columns.merge!(column_hash)
|
50
|
-
end
|
51
|
-
|
52
|
-
VALID_OPTIONS_KEYS = [].freeze
|
51
|
+
VALID_OPTIONS_KEYS = %i[header].freeze
|
53
52
|
|
54
53
|
# define a dynamic_column, must be after all normal columns
|
55
54
|
#
|
@@ -60,14 +59,8 @@ module CsvRowModel
|
|
60
59
|
extra_keys = options.keys - VALID_OPTIONS_KEYS
|
61
60
|
raise ArgumentError.new("invalid options #{extra_keys}") unless extra_keys.empty?
|
62
61
|
|
63
|
-
define_header_method(column_name)
|
64
|
-
|
65
62
|
merge_dynamic_columns(column_name.to_sym => options)
|
66
63
|
end
|
67
|
-
|
68
|
-
def define_header_method(column_name)
|
69
|
-
define_singleton_method(header_method_name(column_name)) { |header_model| header_model }
|
70
|
-
end
|
71
64
|
end
|
72
65
|
end
|
73
66
|
end
|
data/lib/csv_row_model/model.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: csv_row_model
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steve Chung
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-11-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -62,8 +62,10 @@ files:
|
|
62
62
|
- lib/csv_row_model.rb
|
63
63
|
- lib/csv_row_model/concerns/inherited_class_var.rb
|
64
64
|
- lib/csv_row_model/concerns/inspect.rb
|
65
|
+
- lib/csv_row_model/concerns/invalid_options.rb
|
65
66
|
- lib/csv_row_model/engine.rb
|
66
67
|
- lib/csv_row_model/export.rb
|
68
|
+
- lib/csv_row_model/export/attributes.rb
|
67
69
|
- lib/csv_row_model/export/dynamic_columns.rb
|
68
70
|
- lib/csv_row_model/export/file.rb
|
69
71
|
- lib/csv_row_model/export/file_model.rb
|
@@ -111,7 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
113
|
version: '0'
|
112
114
|
requirements: []
|
113
115
|
rubyforge_project:
|
114
|
-
rubygems_version: 2.4.
|
116
|
+
rubygems_version: 2.4.5
|
115
117
|
signing_key:
|
116
118
|
specification_version: 4
|
117
119
|
summary: Import and export your custom CSVs with a intuitive shared Ruby interface.
|