csv_row_model 0.2.1 → 0.3.0
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 +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.
|