eco-helpers 2.0.26 → 2.0.27
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/CHANGELOG.md +19 -1
- data/lib/eco/api/common/people/default_parsers/csv_parser.rb +47 -11
- data/lib/eco/api/common/people/person_entry.rb +7 -6
- data/lib/eco/api/common/people/person_entry_attribute_mapper.rb +55 -16
- data/lib/eco/api/usecases/default_cases/to_csv_case.rb +1 -37
- data/lib/eco/api/usecases/default_cases/to_csv_detailed_case.rb +42 -0
- data/lib/eco/cli/config/default/options.rb +6 -1
- data/lib/eco/cli/config/filters.rb +1 -1
- data/lib/eco/cli/config/options_set.rb +1 -1
- data/lib/eco/cli/config/use_cases.rb +1 -1
- data/lib/eco/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 144eb5a5b4c0069c07c1af3bac6c23c43b975643f45b8b7d8e5edd05daa8defd
|
4
|
+
data.tar.gz: 6f2c5e8f1744380cb543d11087b3d5bbab822697e876c21c8a4ca1984ea5f3a9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8bb382dbc126f15308cdd51f3ec8d520a154450080388e329243102e0fa2c4d9dcbba395ec91f59ea645ea734f222a5af8fe25a1000a39b2474aba2c89a725dd
|
7
|
+
data.tar.gz: 11874a475a744e673e8741777079d54081c0b6be855a609cf7ed6e8a032f29c6e074cefe7e8a94d57944d70be4b8bebbcb5d6a7ff81e25d6e2798ce4259779b1
|
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,25 @@
|
|
1
1
|
# Change Log
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
|
4
|
-
## [2.0.
|
4
|
+
## [2.0.27] - 2021-06-xx
|
5
|
+
|
6
|
+
### Added
|
7
|
+
|
8
|
+
### Changed
|
9
|
+
- Parent class `Eco::API::UseCases::DefaultCases::ToCsvCase` shouldn't know header maps necessary for `Eco::API::UseCases::DefaultCases::ToCsvDetailedCase`
|
10
|
+
* Moved related header maps to `ToCsvDetailedCase`
|
11
|
+
|
12
|
+
### Fixed
|
13
|
+
- `--help` should work fine now
|
14
|
+
- Attribute parsers that depended on indirect parser attributes were **not** included
|
15
|
+
- **changed** a couple of classes
|
16
|
+
- `Eco::API::Common::People::PersonEntryAttributeMapper`, where methods should receive `data` to re-scope if the data is available (most specifically `#internal_attrs`)
|
17
|
+
- `Eco::API::Common::People::PersonEntry`, where when setting the final values on `Person`, it should include any internal attribute that is present in the `final_entry` (the result of all the parsing process)
|
18
|
+
- `Eco::API::Common::People::DefaultParsers::CSVParser`
|
19
|
+
- indirect attributes that depended on other indirect attributes would show as missing even if they were active
|
20
|
+
- i.e. `name` depends on `first-name` & `surname`, **AND** `details-name` depends on `name`
|
21
|
+
|
22
|
+
## [2.0.26] - 2021-06-25
|
5
23
|
|
6
24
|
### Added
|
7
25
|
- `Eco::API::MicroCases#set_supervisor`, tries to keep in sync the `#subordinates` **count** of previous and new supervisor
|
@@ -70,20 +70,14 @@ class Eco::API::Common::People::DefaultParsers::CSVParser < Eco::API::Common::Lo
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def missing_headers(headers)
|
73
|
-
|
74
|
-
hext = headers - hint
|
75
|
-
int_head = hint + hext.map {|e| fields_mapper.to_internal(e)}.compact
|
76
|
-
known_as_int = known_headers.select do |e|
|
77
|
-
i = fields_mapper.to_internal(e)
|
78
|
-
int_head.include?(i)
|
79
|
-
end
|
73
|
+
int_head = internal_present_or_active(headers)
|
80
74
|
ext = headers.select do |e|
|
81
75
|
i = fields_mapper.to_internal(e)
|
82
76
|
int_head.include?(i)
|
83
77
|
end
|
84
|
-
ext_present =
|
78
|
+
ext_present = known_headers_present(int_head) | ext
|
85
79
|
ext_miss = known_headers - ext_present
|
86
|
-
|
80
|
+
|
87
81
|
ext_miss.each_with_object({}) do |ext, missing|
|
88
82
|
next unless int = fields_mapper.to_internal(ext)
|
89
83
|
if all_internal_attrs.include?(int)
|
@@ -91,7 +85,10 @@ class Eco::API::Common::People::DefaultParsers::CSVParser < Eco::API::Common::Lo
|
|
91
85
|
missing[:direct] << ext
|
92
86
|
end
|
93
87
|
related_attrs_requirements = required_attrs.values.select do |req|
|
94
|
-
req.dependant?(int)
|
88
|
+
dep = req.dependant?(int)
|
89
|
+
affects = dep && !int_head.include?(int)
|
90
|
+
in_header = int_head.include?(req.attr)
|
91
|
+
affects || (dep && !in_header)
|
95
92
|
end
|
96
93
|
next if related_attrs_requirements.empty?
|
97
94
|
missing[:indirect] ||= {}
|
@@ -106,6 +103,41 @@ class Eco::API::Common::People::DefaultParsers::CSVParser < Eco::API::Common::Lo
|
|
106
103
|
end
|
107
104
|
end
|
108
105
|
|
106
|
+
def known_headers_present(headers_internal)
|
107
|
+
@known_internal ||= known_headers.select do |ext|
|
108
|
+
int = fields_mapper.to_internal(ext)
|
109
|
+
headers_internal.include?(int)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def internal_present_or_active(headers, inactive_requirements = {})
|
114
|
+
hint = headers & all_internal_attrs
|
115
|
+
hext = headers - hint
|
116
|
+
int_present = hint + hext.map {|e| fields_mapper.to_internal(e)}.compact
|
117
|
+
|
118
|
+
update_inactive = Proc.new do
|
119
|
+
inactive_requirements.dup.each do |attr, req|
|
120
|
+
if req.active?(*int_present)
|
121
|
+
inactive_requirements.delete(attr)
|
122
|
+
int_present << attr
|
123
|
+
update_inactive.call
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
required_attrs.values.each do |req|
|
129
|
+
next if int_present.include?(req)
|
130
|
+
if req.active?(*int_present)
|
131
|
+
inactive_requirements.delete(req.attr)
|
132
|
+
int_present << req.attr
|
133
|
+
update_inactive.call
|
134
|
+
else
|
135
|
+
inactive_requirements[req.attr] = req
|
136
|
+
end
|
137
|
+
end
|
138
|
+
int_present
|
139
|
+
end
|
140
|
+
|
109
141
|
def known_headers
|
110
142
|
@known_headers ||= fields_mapper.list(:external).compact
|
111
143
|
end
|
@@ -121,7 +153,11 @@ class Eco::API::Common::People::DefaultParsers::CSVParser < Eco::API::Common::Lo
|
|
121
153
|
end
|
122
154
|
|
123
155
|
def all_internal_attrs
|
124
|
-
|
156
|
+
@all_internal_attrs ||= [].tap do |int_attrs|
|
157
|
+
known_int_attrs = person_parser.all_attrs(include_defined_parsers: true)
|
158
|
+
known_int_attrs |= fields_mapper.list(:internal).compact
|
159
|
+
int_attrs.concat(known_int_attrs)
|
160
|
+
end
|
125
161
|
end
|
126
162
|
|
127
163
|
def person_parser
|
@@ -188,7 +188,7 @@ module Eco
|
|
188
188
|
# @param person [Ecoportal::API::V1::Person] the person we want to set the core values to.
|
189
189
|
# @param exclude [String, Array<String>] core attributes that should not be set/changed to the person.
|
190
190
|
def set_core(person, exclude: nil)
|
191
|
-
scoped_attrs = @emap.core_attrs - into_a(exclude)
|
191
|
+
scoped_attrs = @emap.core_attrs(@final_entry) - into_a(exclude)
|
192
192
|
@final_entry.slice(*scoped_attrs).each do |attr, value|
|
193
193
|
begin
|
194
194
|
set_part(person, attr, value)
|
@@ -210,7 +210,7 @@ module Eco
|
|
210
210
|
# @param exclude [String, Array<String>] account properties that should not be set/changed to the person.
|
211
211
|
def set_account(person, exclude: nil)
|
212
212
|
person.account = {} if !person.account
|
213
|
-
scoped_attrs = @emap.account_attrs - into_a(exclude)
|
213
|
+
scoped_attrs = @emap.account_attrs(@final_entry) - into_a(exclude)
|
214
214
|
@final_entry.slice(*scoped_attrs).each do |attr, value|
|
215
215
|
set_part(person.account, attr, value)
|
216
216
|
end
|
@@ -224,7 +224,7 @@ module Eco
|
|
224
224
|
# @param exclude [String, Array<String>] schema field attributes that should not be set/changed to the person.
|
225
225
|
def set_details(person, exclude: nil)
|
226
226
|
person.add_details(@person_parser.schema) if !person.details || !person.details.schema_id
|
227
|
-
scoped_attrs = @emap.details_attrs - into_a(exclude)
|
227
|
+
scoped_attrs = @emap.details_attrs(@final_entry) - into_a(exclude)
|
228
228
|
@final_entry.slice(*scoped_attrs).each do |attr, value|
|
229
229
|
set_part(person.details, attr, value)
|
230
230
|
end
|
@@ -326,12 +326,13 @@ module Eco
|
|
326
326
|
# @param internal_entry [Hash] the entry with the **internal** _attribute_ names and values but the **external** types.
|
327
327
|
# @return [Hash] the `parsed entry` with the **internal** final attributes names, values and types.
|
328
328
|
def _final_parsing(internal_entry)
|
329
|
-
|
330
|
-
core_account_hash
|
329
|
+
core_account_attrs = @emap.account_attrs(internal_entry) + @emap.core_attrs(internal_entry)
|
330
|
+
core_account_hash = internal_entry.slice(*core_account_attrs).each_with_object({}) do |(attr, value), hash|
|
331
331
|
hash[attr] = _parse_type(attr, value)
|
332
332
|
end
|
333
333
|
|
334
|
-
|
334
|
+
details_attrs = @emap.details_attrs(internal_entry)
|
335
|
+
details_hash = internal_entry.slice(*details_attrs).each_with_object({}) do |(attr, value), hash|
|
335
336
|
hash[attr] = _parse_type(attr, value, schema: @person_parser.schema)
|
336
337
|
end
|
337
338
|
|
@@ -3,18 +3,11 @@ module Eco
|
|
3
3
|
module Common
|
4
4
|
module People
|
5
5
|
|
6
|
-
# @attr_reader core_attrs [Array<String>] core attributes that are present in the person entry.
|
7
|
-
# @attr_reader details_attrs [Array<String>] schema details attributes that are present in the person entry.
|
8
|
-
# @attr_reader account_attrs [Array<String>] account attributes that are present in the person entry.
|
9
|
-
# @attr_reader all_model_attrs [Array<String>] all the attrs that are present in the person entry.
|
10
|
-
# @attr_reader internal_attrs [Array<String>] all the internally named attributes that the person entry has.
|
11
|
-
# @attr_reader aliased_attrs [Array<String>] only those internal attributes present in the person entry that have an internal/external name mapping.
|
12
6
|
# @attr_reader direct_attrs [Array<String>] only those internal attributes present in the person entry that do **not** have an internal/external name mapping.
|
13
7
|
class PersonEntryAttributeMapper
|
14
8
|
@@cached_warnings = {}
|
15
9
|
|
16
|
-
attr_reader :
|
17
|
-
attr_reader :internal_attrs, :aliased_attrs, :direct_attrs
|
10
|
+
attr_reader :aliased_attrs, :direct_attrs
|
18
11
|
|
19
12
|
# Helper class tied to `PersonEntry` that allows to track which attributes of a person entry are present
|
20
13
|
# and how they should be mapped between internal and external names if applicable.
|
@@ -38,17 +31,64 @@ module Eco
|
|
38
31
|
|
39
32
|
if parsing?
|
40
33
|
@external_entry = data
|
41
|
-
init_attr_trackers
|
42
34
|
else # SERIALIZING
|
43
35
|
@person = data
|
44
|
-
@internal_attrs = @person_parser.all_model_attrs
|
45
|
-
@aliased_attrs = @attr_map.list(:internal)
|
46
36
|
end
|
37
|
+
end
|
47
38
|
|
48
|
-
|
49
|
-
|
50
|
-
@
|
51
|
-
|
39
|
+
# @return [Array<String>] only those internal attributes present in the person entry that have an internal/external name mapping.
|
40
|
+
def aliased_attrs
|
41
|
+
return @aliased_attrs unless !@aliased_attrs
|
42
|
+
if parsing?
|
43
|
+
init_attr_trackers
|
44
|
+
else
|
45
|
+
@aliased_attrs = @attr_map.list(:internal)
|
46
|
+
end
|
47
|
+
@aliased_attrs
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [Array<String>] all the internally named attributes that the person entry has.
|
51
|
+
def internal_attrs(data = nil)
|
52
|
+
return @internal_attrs unless data || !@internal_attrs
|
53
|
+
if parsing?
|
54
|
+
init_attr_trackers unless @internal_attrs
|
55
|
+
if data
|
56
|
+
return data.keys & @person_parser.all_model_attrs
|
57
|
+
end
|
58
|
+
else
|
59
|
+
@internal_attrs = @person_parser.all_model_attrs
|
60
|
+
end
|
61
|
+
@internal_attrs
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
# @return [Array<String>] all the attrs that are present in the person entry.
|
66
|
+
def all_model_attrs(data = nil)
|
67
|
+
core_attrs(data) | account_attrs(data) | details_attrs(data)
|
68
|
+
end
|
69
|
+
|
70
|
+
# @return [Array<String>] core attributes that are present in the person entry.
|
71
|
+
def core_attrs(data = nil)
|
72
|
+
return @core_attrs unless data || !@core_attrs
|
73
|
+
@person_parser.target_attrs_core(internal_attrs(data)).tap do |core_attrs|
|
74
|
+
@core_attrs ||= core_attrs
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [Array<String>] schema details attributes that are present in the person entry.
|
79
|
+
def details_attrs(data = nil)
|
80
|
+
return @details_attrs unless data || !@core_attrs
|
81
|
+
@person_parser.target_attrs_details(internal_attrs(data)).tap do |details_attrs|
|
82
|
+
@details_attrs ||= details_attrs
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# @return [Array<String>] account attributes that are present in the person entry.
|
87
|
+
def account_attrs(data = nil)
|
88
|
+
return @account_attrs unless data || !@account_attrs
|
89
|
+
@person_parser.target_attrs_account(internal_attrs(data)).tap do |account_attrs|
|
90
|
+
@account_attrs ||= account_attrs
|
91
|
+
end
|
52
92
|
end
|
53
93
|
|
54
94
|
# To know if currently the object is in parse or serialize mode.
|
@@ -151,7 +191,6 @@ module Eco
|
|
151
191
|
def_unlinked = @person_parser.undefined_model_attrs.select { |attr| !to_external(attr) }
|
152
192
|
# (def) those with parser or alias:
|
153
193
|
def_linked = def_all_attrs - def_unlinked
|
154
|
-
|
155
194
|
# (data) data attributes (actual attributes of the entry)
|
156
195
|
data_attrs = attributes(@external_entry)
|
157
196
|
# (data) attributes of the data that come directly as internal attribute names
|
@@ -125,43 +125,7 @@ class Eco::API::UseCases::DefaultCases::ToCsvCase < Eco::API::Common::Loaders::U
|
|
125
125
|
"id" => "ecoPortal ID",
|
126
126
|
"external_id" => "Reference ID (ext_id)",
|
127
127
|
"login_provider_ids" => "Login Methods",
|
128
|
-
"landing_page_id" => "Landing Page ID"
|
129
|
-
"show_sidebar" => "(pref) Sidebar Open?",
|
130
|
-
"show_shortcuts" => "(pref) Link to Registers?",
|
131
|
-
"show_coming_soon" => "(pref) Coming Soon List?",
|
132
|
-
"show_recently_visited_forms" => "(pref) Recently Visited Forms List?",
|
133
|
-
"show_tasks" => "(pref) Tasks List?",
|
134
|
-
"show_task_bubbles" => "(pref) Task Count Bubbles",
|
135
|
-
"kiosk_enabled" => "Kiosk User?",
|
136
|
-
"freemium" => "Freemium User?",
|
137
|
-
"files" => "(able) on Files",
|
138
|
-
"reports" => "(able) on Report Structures",
|
139
|
-
"data" => "(able) on Data (hours, datasets)",
|
140
|
-
"organization" => "(able) on Organization Config",
|
141
|
-
"pages" => "(able) on Page/Entries",
|
142
|
-
"page_editor" => "(able) page Editor Level",
|
143
|
-
"registers" => "(able) on Registers",
|
144
|
-
"tasks" => "(able) on Tasks",
|
145
|
-
"person_core" => "(able) on People",
|
146
|
-
"person_core_create" => "(able) Create People?",
|
147
|
-
"person_core_edit" => "(able) Edit People?",
|
148
|
-
"person_details" => "(able) on People Schema Details",
|
149
|
-
"person_account" => "(able) on Users",
|
150
|
-
"person_abilities" => "(able) on Users' Abilities",
|
151
|
-
"custom_files" => "(min) on Files",
|
152
|
-
"custom_reports" => "(min) on Report Structures",
|
153
|
-
"custom_data" => "(min) on Data (hours, datasets)",
|
154
|
-
"custom_organization" => "(min) on Organization Config",
|
155
|
-
"custom_pages" => "(min) on Page/Entries",
|
156
|
-
"custom_page_editor" => "(min) page Editor Level",
|
157
|
-
"custom_registers" => "(min) on Registers",
|
158
|
-
"custom_tasks" => "(min) on Tasks",
|
159
|
-
"custom_person_core" => "(min) on People",
|
160
|
-
"custom_person_core_create" => "(min) Create People?",
|
161
|
-
"custom_person_core_edit" => "(min) Edit People?",
|
162
|
-
"custom_person_details" => "(min) on People Schema Details",
|
163
|
-
"custom_person_account" => "(min) on Users",
|
164
|
-
"custom_person_abilities" => "(min) on Users' Abilities"
|
128
|
+
"landing_page_id" => "Landing Page ID"
|
165
129
|
}
|
166
130
|
end
|
167
131
|
|
@@ -75,4 +75,46 @@ class Eco::API::UseCases::DefaultCases::ToCsvDetailedCase < Eco::API::UseCases::
|
|
75
75
|
]
|
76
76
|
end
|
77
77
|
|
78
|
+
def nice_header_maps
|
79
|
+
@nice_header_maps ||= super.merge({
|
80
|
+
"landing_page_id" => "Landing Page ID",
|
81
|
+
"show_sidebar" => "(pref) Sidebar Open?",
|
82
|
+
"show_shortcuts" => "(pref) Link to Registers?",
|
83
|
+
"show_coming_soon" => "(pref) Coming Soon List?",
|
84
|
+
"show_recently_visited_forms" => "(pref) Recently Visited Forms List?",
|
85
|
+
"show_tasks" => "(pref) Tasks List?",
|
86
|
+
"show_task_bubbles" => "(pref) Task Count Bubbles",
|
87
|
+
"kiosk_enabled" => "Kiosk User?",
|
88
|
+
"freemium" => "Freemium User?",
|
89
|
+
"files" => "(able) on Files",
|
90
|
+
"reports" => "(able) on Report Structures",
|
91
|
+
"data" => "(able) on Data (hours, datasets)",
|
92
|
+
"organization" => "(able) on Organization Config",
|
93
|
+
"pages" => "(able) on Page/Entries",
|
94
|
+
"page_editor" => "(able) page Editor Level",
|
95
|
+
"registers" => "(able) on Registers",
|
96
|
+
"tasks" => "(able) on Tasks",
|
97
|
+
"person_core" => "(able) on People",
|
98
|
+
"person_core_create" => "(able) Create People?",
|
99
|
+
"person_core_edit" => "(able) Edit People?",
|
100
|
+
"person_details" => "(able) on People Schema Details",
|
101
|
+
"person_account" => "(able) on Users",
|
102
|
+
"person_abilities" => "(able) on Users' Abilities",
|
103
|
+
"custom_files" => "(min) on Files",
|
104
|
+
"custom_reports" => "(min) on Report Structures",
|
105
|
+
"custom_data" => "(min) on Data (hours, datasets)",
|
106
|
+
"custom_organization" => "(min) on Organization Config",
|
107
|
+
"custom_pages" => "(min) on Page/Entries",
|
108
|
+
"custom_page_editor" => "(min) page Editor Level",
|
109
|
+
"custom_registers" => "(min) on Registers",
|
110
|
+
"custom_tasks" => "(min) on Tasks",
|
111
|
+
"custom_person_core" => "(min) on People",
|
112
|
+
"custom_person_core_create" => "(min) Create People?",
|
113
|
+
"custom_person_core_edit" => "(min) Edit People?",
|
114
|
+
"custom_person_details" => "(min) on People Schema Details",
|
115
|
+
"custom_person_account" => "(min) on Users",
|
116
|
+
"custom_person_abilities" => "(min) on Users' Abilities"
|
117
|
+
})
|
118
|
+
end
|
119
|
+
|
78
120
|
end
|
@@ -2,7 +2,12 @@ ASSETS.cli.config do |cnf|
|
|
2
2
|
cnf.options_set do |options_set, options|
|
3
3
|
options_set.add("--help", "Offers a HELP") do |options, sesssion|
|
4
4
|
conf = ASSETS.cli.config
|
5
|
-
active = Proc.new
|
5
|
+
active = Proc.new do |opt|
|
6
|
+
if there = SCR.get_arg(opt)
|
7
|
+
refine = SCR.get_arg(opt, with_param: true)
|
8
|
+
end
|
9
|
+
refine || there
|
10
|
+
end
|
6
11
|
|
7
12
|
if hpf = active.call("-filters")
|
8
13
|
puts conf.people_filters.help(refine: hpf)
|
@@ -18,7 +18,7 @@ module Eco
|
|
18
18
|
[msg].yield_self do |lines|
|
19
19
|
max_len = keys_max_len(@filters.keys)
|
20
20
|
@filters.keys.sort.select do |key|
|
21
|
-
refine.is_a?(String)
|
21
|
+
!refine.is_a?(String) || key.include?(refine)
|
22
22
|
end.each do |key|
|
23
23
|
lines << help_line(key, @description[key], max_len)
|
24
24
|
end
|
@@ -25,7 +25,7 @@ module Eco
|
|
25
25
|
str_indent = is_general ? "" : " " * indent
|
26
26
|
lines << help_line(namespace, "", max_len) unless is_general
|
27
27
|
options_set(namespace).select do |arg, option|
|
28
|
-
refine.is_a?(String)
|
28
|
+
!refine.is_a?(String) || option.name.include?(refine)
|
29
29
|
end.each do |arg, option|
|
30
30
|
lines << help_line(" " * indent + "#{option.name}", option.description, max_len)
|
31
31
|
end
|
@@ -34,7 +34,7 @@ module Eco
|
|
34
34
|
["The following are the available use cases#{refinement}:"].yield_self do |lines|
|
35
35
|
max_len = keys_max_len(@linked_cases.keys)
|
36
36
|
@linked_cases.keys.sort.select do |key|
|
37
|
-
refine.is_a?(String)
|
37
|
+
!refine.is_a?(String) || key.include?(refine)
|
38
38
|
end.each do |option_case|
|
39
39
|
lines << help_line(option_case, @linked_cases[option_case].description, max_len)
|
40
40
|
end
|
data/lib/eco/version.rb
CHANGED