jenkins_pipeline_builder 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -11
- data/README.md +19 -29
- data/commit_build.sh +1 -4
- data/lib/jenkins_pipeline_builder.rb +9 -13
- data/lib/jenkins_pipeline_builder/cli/describe.rb +14 -5
- data/lib/jenkins_pipeline_builder/cli/helper.rb +2 -0
- data/lib/jenkins_pipeline_builder/cli/list.rb +11 -4
- data/lib/jenkins_pipeline_builder/extensions.rb +151 -40
- data/lib/jenkins_pipeline_builder/{builders.rb → extensions/builders.rb} +22 -15
- data/lib/jenkins_pipeline_builder/{job_builder.rb → extensions/job_attributes.rb} +37 -27
- data/lib/jenkins_pipeline_builder/{publishers.rb → extensions/publishers.rb} +55 -20
- data/lib/jenkins_pipeline_builder/{triggers.rb → extensions/triggers.rb} +18 -8
- data/lib/jenkins_pipeline_builder/{wrappers.rb → extensions/wrappers.rb} +23 -36
- data/lib/jenkins_pipeline_builder/generator.rb +29 -62
- data/lib/jenkins_pipeline_builder/module_registry.rb +21 -9
- data/lib/jenkins_pipeline_builder/pull_request.rb +1 -1
- data/lib/jenkins_pipeline_builder/version.rb +1 -1
- data/pipeline/Jenkins-Pipeline-Builder.yaml +3 -7
- data/pipeline/project.yaml +10 -0
- data/spec/lib/jenkins_pipeline_builder/compiler_spec.rb +9 -1
- data/spec/lib/jenkins_pipeline_builder/extensions/builders_spec.rb +53 -0
- data/spec/lib/jenkins_pipeline_builder/extensions/publishers_spec.rb +140 -0
- data/spec/lib/jenkins_pipeline_builder/extensions_spec.rb +113 -31
- data/spec/lib/jenkins_pipeline_builder/generator_spec.rb +12 -4
- data/spec/lib/jenkins_pipeline_builder/module_registry_spec.rb +202 -44
- data/spec/lib/jenkins_pipeline_builder/spec_helper.rb +5 -5
- data/spec/requests/pipeline_spec.rb +20 -0
- metadata +16 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1b24c99f73cecb047e1f58cd328000f81ee3b31c
|
4
|
+
data.tar.gz: 43f6ce844efe1ed19940d3e8d0dfaab50b243d79
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82bce578c0a059d5cbee047fcd984bbe765174ed58e96601c721b41eb806583e4301959822a6bbe1e729dbdce2636b99d30d9ae00ec877171b1b9b490ecd59c0
|
7
|
+
data.tar.gz: 60bc6c5d94f07227b5750813455267f80c05f2dc7a677bfb972829acd6f32d17e7673eb1b211a3ac849bc085b8d8f6d0682e023a4081e5b74bc2f8fd32848ef5
|
data/.rubocop.yml
CHANGED
@@ -26,6 +26,7 @@ Style/LineLength:
|
|
26
26
|
- 'spec/lib/jenkins_pipeline_builder/compiler_spec.rb'
|
27
27
|
- 'spec/lib/jenkins_pipeline_builder/view_spec.rb'
|
28
28
|
- 'spec/lib/jenkins_pipeline_builder/pull_request_spec.rb'
|
29
|
+
- 'lib/jenkins_pipeline_builder/extensions/*'
|
29
30
|
|
30
31
|
# Configuration parameters: CountComments.
|
31
32
|
Style/MethodLength:
|
@@ -37,18 +38,9 @@ Style/Documentation:
|
|
37
38
|
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
38
39
|
Style/Next:
|
39
40
|
Exclude:
|
40
|
-
- 'lib/jenkins_pipeline_builder/
|
41
|
-
- 'lib/jenkins_pipeline_builder/triggers.rb'
|
42
|
-
- 'lib/jenkins_pipeline_builder/wrappers.rb'
|
43
|
-
- 'lib/jenkins_pipeline_builder/builders.rb'
|
44
|
-
- 'lib/jenkins_pipeline_builder/job_builder.rb'
|
41
|
+
- 'lib/jenkins_pipeline_builder/extensions/*'
|
45
42
|
|
46
43
|
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
47
44
|
Style/VariableName:
|
48
45
|
Exclude:
|
49
|
-
- 'lib/jenkins_pipeline_builder/
|
50
|
-
- 'lib/jenkins_pipeline_builder/triggers.rb'
|
51
|
-
- 'lib/jenkins_pipeline_builder/wrappers.rb'
|
52
|
-
- 'lib/jenkins_pipeline_builder/builders.rb'
|
53
|
-
- 'lib/jenkins_pipeline_builder/job_builder.rb'
|
54
|
-
#######
|
46
|
+
- 'lib/jenkins_pipeline_builder/extensions/*'
|
data/README.md
CHANGED
@@ -15,7 +15,7 @@ amazing progress done by the Open Stack community with their [jenkins-job-builde
|
|
15
15
|
The YAML structure very closely resembles the OpenStack Job Builder, but, in comparison to Python version, is 100%
|
16
16
|
pure Ruby and uses Jenkins API Client and has additional functionlity of building different types of Jenkins views.
|
17
17
|
|
18
|
-
#
|
18
|
+
# JenkinsPipelineBuilder
|
19
19
|
|
20
20
|
USAGE:
|
21
21
|
------
|
@@ -76,6 +76,8 @@ NOTE: you can run the pipeline in NOOP (debug-only) mode by addind -d parameter,
|
|
76
76
|
|
77
77
|
generate pipeline -d -c config/login.yml bootstrap ./pipeline
|
78
78
|
|
79
|
+
The command comes with fairly extensive help. For example you can list all of the registered extension types with `generate list` and a list of all extensions of a type with `generate list type`
|
80
|
+
|
79
81
|
DSL:
|
80
82
|
----
|
81
83
|
|
@@ -282,7 +284,7 @@ When running a project through this module, the project {{name}} is appended wit
|
|
282
284
|
job_type: pull_request_generator
|
283
285
|
git_url: 'https://www.github.com/'
|
284
286
|
git_repo: 'jenkins_pipeline_builder'
|
285
|
-
git_org: '
|
287
|
+
git_org: 'constantcontact'
|
286
288
|
jobs:
|
287
289
|
- '{{name}}-Job1':
|
288
290
|
publishers:
|
@@ -338,39 +340,27 @@ Extending the Pipeline Builder
|
|
338
340
|
|
339
341
|
Have a feature you want to test out before adding it to the source? Now you can create a quick "extension" to the pipeline builder to add new or overwrite existing functionality.
|
340
342
|
|
341
|
-
To add an extension, create an "extensions" directiroy inside of "pipeline" and create a file named "
|
342
|
-
|
343
|
-
When registering, you must use one of the following register methods, depending on what category your change falls into:
|
344
|
-
* register_job_attribute
|
345
|
-
* register_builder
|
346
|
-
* register_publisher
|
347
|
-
* register_wrapper
|
348
|
-
* register_trigger
|
343
|
+
To add an extension, create an "extensions" directiroy inside of "pipeline" and create a file named "my_extension.rb" (or any name). Then just `require 'jenkins_pipeline_builder/extensions'` and you can begin using the extension DSL. All of the plugins use this DSL and provide an excellent source of examples. You can find them in lib/jenkins_pipeline_builder/extensions.
|
349
344
|
|
350
345
|
For help figuring out what category your change is, examine the config.xml for a job that uses your feature. If it is a first child of the root "project" node, your change is a job_attribute. Otherwise it should be either a builder, publisher, wrapper, or trigger, depending what child node it is found in the XML tree.
|
351
346
|
|
352
347
|
Here is an example of extending the pipeline builder with a new publisher:
|
353
348
|
|
354
349
|
```ruby
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
JenkinsPipelineBuilder.extend do |registry|
|
370
|
-
registry.register_publisher :yaml_name, "Jenkins UI Name", "Description of this feature" do |params, xml|
|
371
|
-
xml.send("new_element") {
|
372
|
-
xml.property params[:value]
|
373
|
-
}
|
350
|
+
publisher do
|
351
|
+
name :yaml_name
|
352
|
+
plugin_id 123
|
353
|
+
min_version '0.4'
|
354
|
+
jenkins_name "Jenkins UI Name"
|
355
|
+
description "Description of this feature"
|
356
|
+
|
357
|
+
xml do |params, xml|
|
358
|
+
send("new_element") do
|
359
|
+
property params[:value]
|
360
|
+
if params[:thing]
|
361
|
+
thing 'true'
|
362
|
+
end
|
363
|
+
end
|
374
364
|
end
|
375
365
|
end
|
376
366
|
```
|
data/commit_build.sh
CHANGED
@@ -40,32 +40,28 @@ module JenkinsPipelineBuilder
|
|
40
40
|
|
41
41
|
def credentials=(creds)
|
42
42
|
@credentials = creds
|
43
|
-
@client =
|
44
|
-
|
45
|
-
generator.logger = @_client.logger
|
43
|
+
@client = JenkinsApi::Client.new(credentials)
|
44
|
+
generator.logger = @client.logger
|
46
45
|
@credentials
|
47
46
|
end
|
48
47
|
|
49
48
|
def logger
|
50
|
-
|
49
|
+
client.logger
|
51
50
|
end
|
52
51
|
|
53
52
|
def registry
|
54
53
|
generator.module_registry
|
55
54
|
end
|
56
|
-
|
57
|
-
def load_registry
|
58
|
-
load 'jenkins_pipeline_builder/builders.rb'
|
59
|
-
load 'jenkins_pipeline_builder/job_builder.rb'
|
60
|
-
load 'jenkins_pipeline_builder/wrappers.rb'
|
61
|
-
load 'jenkins_pipeline_builder/publishers.rb'
|
62
|
-
load 'jenkins_pipeline_builder/triggers.rb'
|
63
|
-
end
|
64
55
|
end
|
65
56
|
end
|
66
57
|
JenkinsPipelineBuilder.generator
|
67
58
|
require 'jenkins_pipeline_builder/extensions'
|
68
|
-
|
59
|
+
require 'jenkins_pipeline_builder/extensions/builders'
|
60
|
+
require 'jenkins_pipeline_builder/extensions/job_attributes'
|
61
|
+
require 'jenkins_pipeline_builder/extensions/wrappers'
|
62
|
+
require 'jenkins_pipeline_builder/extensions/publishers'
|
63
|
+
require 'jenkins_pipeline_builder/extensions/triggers'
|
64
|
+
|
69
65
|
require 'jenkins_pipeline_builder/cli/helper'
|
70
66
|
require 'jenkins_pipeline_builder/cli/view'
|
71
67
|
require 'jenkins_pipeline_builder/cli/pipeline'
|
@@ -22,15 +22,22 @@
|
|
22
22
|
|
23
23
|
module JenkinsPipelineBuilder
|
24
24
|
module CLI
|
25
|
-
JenkinsPipelineBuilder.registry.entries.keys
|
25
|
+
entries = JenkinsPipelineBuilder.registry.entries.keys
|
26
|
+
entries << :job_attributes
|
27
|
+
entries.each do |entry|
|
26
28
|
klass_name = entry.to_s.classify
|
27
29
|
# rubocop:disable Style/AccessModifierIndentation
|
28
30
|
klass = Class.new(Thor) do
|
29
31
|
|
30
|
-
|
31
|
-
|
32
|
+
if entry == :job_attributes
|
33
|
+
extensions = JenkinsPipelineBuilder.registry.registry[:job].select { |_, x| x.is_a? ExtensionSet }
|
34
|
+
else
|
35
|
+
extensions = JenkinsPipelineBuilder.registry.registry[:job][entry]
|
36
|
+
end
|
37
|
+
|
38
|
+
extensions.each do |key, extset|
|
32
39
|
# TODO: don't just take the first
|
33
|
-
ext =
|
40
|
+
ext = extset.extensions.first
|
34
41
|
desc key, "Details for #{ext.name}"
|
35
42
|
define_method(ext.name) do
|
36
43
|
display_module(ext)
|
@@ -47,7 +54,9 @@ module JenkinsPipelineBuilder
|
|
47
54
|
Module.const_set(klass_name, klass)
|
48
55
|
end
|
49
56
|
class Describe < Thor
|
50
|
-
JenkinsPipelineBuilder.registry.entries.
|
57
|
+
entries = JenkinsPipelineBuilder.registry.entries.keys
|
58
|
+
entries << :job_attributes
|
59
|
+
entries.each do |entry, _path|
|
51
60
|
klass_name = entry.to_s.classify
|
52
61
|
singular_model = entry.to_s.singularize
|
53
62
|
|
@@ -49,6 +49,8 @@ module JenkinsPipelineBuilder
|
|
49
49
|
creds = YAML.load_file(
|
50
50
|
File.expand_path("#{ENV['HOME']}/.jenkins_api_client/login.yml", __FILE__)
|
51
51
|
)
|
52
|
+
elsif options[:debug]
|
53
|
+
creds = { username: :foo, password: :bar, server_ip: :baz }
|
52
54
|
else
|
53
55
|
msg = 'Credentials are not set. Please pass them as parameters or'
|
54
56
|
msg << ' set them in the default credentials file'
|
@@ -26,15 +26,22 @@ module JenkinsPipelineBuilder
|
|
26
26
|
JenkinsPipelineBuilder.registry.entries.keys.each do |entry|
|
27
27
|
desc entry, "List all #{entry}"
|
28
28
|
define_method(entry) do
|
29
|
-
|
30
|
-
|
29
|
+
entries = JenkinsPipelineBuilder.registry.registry[:job][entry]
|
30
|
+
entries.each do |name, set|
|
31
|
+
ext = set.extensions.first
|
32
|
+
display_module(name, ext)
|
33
|
+
end
|
31
34
|
end
|
32
35
|
end
|
33
36
|
|
34
37
|
desc 'job_attributes', 'List all job attributes'
|
35
38
|
def job_attributes
|
36
|
-
|
37
|
-
|
39
|
+
entries = JenkinsPipelineBuilder.registry.registry[:job]
|
40
|
+
entries.each do |name, set|
|
41
|
+
next unless set.is_a? ExtensionSet
|
42
|
+
ext = set.extensions.first
|
43
|
+
display_module(name, ext)
|
44
|
+
end
|
38
45
|
end
|
39
46
|
|
40
47
|
private
|
@@ -23,74 +23,185 @@ require 'jenkins_pipeline_builder'
|
|
23
23
|
JenkinsPipelineBuilder.registry.entries.each do |type, path|
|
24
24
|
singular_type = type.to_s.singularize
|
25
25
|
define_method singular_type do |&block|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
26
|
+
set = JenkinsPipelineBuilder::ExtensionSet.new
|
27
|
+
set.instance_eval(&block)
|
28
|
+
set.blocks.each do |version, settings|
|
29
|
+
ext = JenkinsPipelineBuilder::Extension.new
|
30
|
+
ext.min_version version
|
31
|
+
ext.type singular_type
|
32
|
+
set.settings.merge(settings).each do |key, value|
|
33
|
+
ext.send key, value
|
34
|
+
end
|
35
|
+
ext.path path unless ext.path
|
36
|
+
set.extensions << ext
|
37
|
+
end
|
38
|
+
unless set.valid?
|
39
|
+
name = set.name || 'A plugin with no name provided'
|
32
40
|
puts "Encountered errors while registering #{name}"
|
33
|
-
puts
|
41
|
+
puts set.errors.map { |k, v| "#{k}: #{v}" }.join(', ')
|
34
42
|
return false
|
35
43
|
end
|
36
|
-
JenkinsPipelineBuilder.registry.register([:job, type],
|
37
|
-
|
44
|
+
JenkinsPipelineBuilder.registry.register([:job, type], set)
|
45
|
+
versions = set.extensions.map { |ext| ext.min_version }
|
46
|
+
puts "Successfully registered #{set.name} for versions #{versions}" if set.announced
|
38
47
|
end
|
39
48
|
end
|
40
49
|
|
41
50
|
def job_attribute(&block)
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
51
|
+
set = JenkinsPipelineBuilder::ExtensionSet.new
|
52
|
+
set.instance_eval(&block)
|
53
|
+
set.blocks.each do |version, settings|
|
54
|
+
ext = JenkinsPipelineBuilder::Extension.new
|
55
|
+
ext.min_version version
|
56
|
+
ext.type :job_attribute
|
57
|
+
set.settings.merge(settings).each do |key, value|
|
58
|
+
ext.send key, value
|
59
|
+
end
|
60
|
+
set.extensions << ext
|
61
|
+
end
|
62
|
+
unless set.valid?
|
63
|
+
name = set.name || 'A plugin with no name provided'
|
47
64
|
puts "Encountered errors while registering #{name}"
|
48
|
-
puts
|
65
|
+
puts set.errors.map { |k, v| "#{k}: #{v}" }.join(', ')
|
49
66
|
return false
|
50
67
|
end
|
51
|
-
JenkinsPipelineBuilder.registry.register([:job],
|
52
|
-
puts "Successfully registered #{
|
68
|
+
JenkinsPipelineBuilder.registry.register([:job], set)
|
69
|
+
puts "Successfully registered #{set.name} for versions #{set.min_version} and higher" if set.announced
|
70
|
+
end
|
71
|
+
|
72
|
+
module JenkinsPipelineBuilder
|
73
|
+
class ExtensionSet
|
74
|
+
SET_METHODS = [
|
75
|
+
:name,
|
76
|
+
:plugin_id,
|
77
|
+
:jenkins_name,
|
78
|
+
:description,
|
79
|
+
:announced,
|
80
|
+
:type
|
81
|
+
]
|
82
|
+
SET_METHODS.each do |method_name|
|
83
|
+
define_method method_name do |value = nil|
|
84
|
+
return settings[method_name] if value.nil?
|
85
|
+
settings[method_name] = value
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
attr_accessor :blocks, :extensions, :settings
|
90
|
+
|
91
|
+
def initialize
|
92
|
+
@blocks = {}
|
93
|
+
@settings = {}
|
94
|
+
@extensions = []
|
95
|
+
end
|
96
|
+
|
97
|
+
def installed_version=(version)
|
98
|
+
version = version.match(/\d+\.\d+/)
|
99
|
+
@version = Gem::Version.new version
|
100
|
+
end
|
101
|
+
|
102
|
+
def installed_version
|
103
|
+
return @version if @version
|
104
|
+
reg = JenkinsPipelineBuilder.registry
|
105
|
+
version = reg.versions[settings[:plugin_id]]
|
106
|
+
puts reg.versions.inspect if version.nil?
|
107
|
+
fail "Plugin #{settings[:name]} is not installed (plugin_id: #{settings[:plugin_id]})" if version.nil?
|
108
|
+
self.installed_version = version
|
109
|
+
@version
|
110
|
+
end
|
111
|
+
|
112
|
+
def extension
|
113
|
+
# TODO: Support multiple xml sections for the native to jenkins plugins
|
114
|
+
return extensions.first if settings[:plugin_id] == 'builtin'
|
115
|
+
|
116
|
+
versions = extensions.each_with_object({}) do |ext, hash|
|
117
|
+
hash[Gem::Version.new(ext.min_version)] = ext
|
118
|
+
end
|
119
|
+
versions.keys.sort!.reverse!.each do |version|
|
120
|
+
return versions[version] if version <= installed_version
|
121
|
+
end
|
122
|
+
|
123
|
+
fail "Can't find version of #{name} lte #{installed_version}, versions available are #{versions.keys.map(&:to_s)}"
|
124
|
+
end
|
125
|
+
|
126
|
+
def merge(other_set)
|
127
|
+
mismatch = []
|
128
|
+
SET_METHODS.each do |method_name|
|
129
|
+
val1 = settings[method_name]
|
130
|
+
val2 = other_set.settings[method_name]
|
131
|
+
mismatch << "The values for #{method_name} do not match '#{val1}' : '#{val2}'" unless val1 == val2
|
132
|
+
end
|
133
|
+
mismatch.each do |error|
|
134
|
+
puts error
|
135
|
+
end
|
136
|
+
fail 'Values did not match, cannot merge exception sets' if mismatch.any?
|
137
|
+
|
138
|
+
blocks.merge other_set.blocks
|
139
|
+
end
|
140
|
+
|
141
|
+
def xml(path: false, version: '0', &block)
|
142
|
+
unless block
|
143
|
+
fail "no block found for version #{version}" unless blocks.key version
|
144
|
+
return blocks[version][:block]
|
145
|
+
end
|
146
|
+
if blocks[version]
|
147
|
+
blocks[version].merge!(xml: block, path: path)
|
148
|
+
else
|
149
|
+
blocks[version] = { xml: block, path: path }
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
[:after, :before].each do |method_name|
|
154
|
+
define_method method_name do |version: '0', &block|
|
155
|
+
return instance_variable_get(method_name) unless block
|
156
|
+
blocks[version] = {} unless blocks[version]
|
157
|
+
blocks[version][method_name] = block
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def valid?
|
162
|
+
errors.empty?
|
163
|
+
end
|
164
|
+
|
165
|
+
def errors
|
166
|
+
errors = {}
|
167
|
+
errors['ExtensionSet'] = 'no extensions successfully registered' if extensions.empty?
|
168
|
+
extensions.each do |ext|
|
169
|
+
ver = ext.min_version || 'unknown'
|
170
|
+
errors["#{ext.name} version #{ver}"] = ext.errors unless ext.valid?
|
171
|
+
end
|
172
|
+
errors
|
173
|
+
end
|
174
|
+
end
|
53
175
|
end
|
54
176
|
|
55
177
|
module JenkinsPipelineBuilder
|
56
178
|
class Extension
|
57
|
-
|
179
|
+
EXT_METHODS = {
|
58
180
|
name: false,
|
59
181
|
plugin_id: false,
|
60
|
-
min_version: false,
|
61
182
|
jenkins_name: 'No jenkins display name set',
|
62
183
|
description: 'No description set',
|
63
|
-
path: false,
|
64
184
|
announced: true,
|
65
|
-
|
185
|
+
min_version: false,
|
186
|
+
path: false,
|
187
|
+
type: false,
|
188
|
+
before: false,
|
189
|
+
after: false,
|
190
|
+
xml: false
|
66
191
|
}
|
67
|
-
|
192
|
+
EXT_METHODS.keys.each do |method_name|
|
68
193
|
define_method method_name do |value = nil|
|
69
194
|
return instance_variable_get("@#{method_name}") if value.nil?
|
70
195
|
instance_variable_set("@#{method_name}", value)
|
71
196
|
end
|
72
197
|
end
|
73
198
|
|
74
|
-
def xml(path = false, &block)
|
75
|
-
@path = path if path
|
76
|
-
return @xml unless block
|
77
|
-
@xml = block
|
78
|
-
end
|
79
|
-
|
80
|
-
def after(&block)
|
81
|
-
return @after unless block
|
82
|
-
@after = block
|
83
|
-
end
|
84
|
-
|
85
|
-
def before(&block)
|
86
|
-
return @before unless block
|
87
|
-
@before = block
|
88
|
-
end
|
89
|
-
|
90
199
|
def initialize
|
91
|
-
|
200
|
+
EXT_METHODS.each do |key, value|
|
92
201
|
instance_variable_set("@#{key}", value) if value
|
93
202
|
end
|
203
|
+
before false
|
204
|
+
after false
|
94
205
|
end
|
95
206
|
|
96
207
|
def valid?
|
@@ -99,7 +210,7 @@ module JenkinsPipelineBuilder
|
|
99
210
|
|
100
211
|
def errors
|
101
212
|
errors = {}
|
102
|
-
|
213
|
+
EXT_METHODS.keys.each do |name|
|
103
214
|
errors[name] = 'Must be set' if send(name).nil?
|
104
215
|
end
|
105
216
|
errors
|