dapp 0.32.10 → 0.33.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/dapp +1 -23
- data/config/en/net_status.yml +7 -0
- data/lib/dapp.rb +3 -3
- data/lib/dapp/dapp.rb +13 -0
- data/lib/dapp/dapp/dappfile.rb +2 -2
- data/lib/dapp/dapp/deps/base.rb +5 -37
- data/lib/dapp/dapp/deps/common.rb +25 -0
- data/lib/dapp/dapp/deps/gitartifact.rb +2 -25
- data/lib/dapp/dapp/deps/toolchain.rb +1 -23
- data/lib/dapp/dapp/logging/base.rb +3 -3
- data/lib/dapp/dapp/ruby2go.rb +96 -0
- data/lib/dapp/dapp/sentry.rb +0 -1
- data/lib/dapp/dapp/shellout/base.rb +4 -2
- data/lib/dapp/dimg/build/stage/artifact_base.rb +7 -6
- data/lib/dapp/dimg/build/stage/base.rb +37 -7
- data/lib/dapp/dimg/build/stage/from.rb +1 -1
- data/lib/dapp/dimg/builder/ansible.rb +1 -209
- data/lib/dapp/dimg/builder/base.rb +0 -5
- data/lib/dapp/dimg/builder/none.rb +1 -34
- data/lib/dapp/dimg/builder/ruby2go.rb +51 -0
- data/lib/dapp/dimg/builder/shell.rb +1 -25
- data/lib/dapp/dimg/cli/command/dimg/bp.rb +10 -15
- data/lib/dapp/dimg/cli/command/dimg/build.rb +0 -5
- data/lib/dapp/dimg/config/directive/docker/base.rb +1 -1
- data/lib/dapp/dimg/dapp/command/build_context/export.rb +1 -1
- data/lib/dapp/dimg/dapp/command/build_context/import.rb +1 -1
- data/lib/dapp/dimg/dapp/command/cleanup_repo.rb +59 -90
- data/lib/dapp/dimg/dapp/command/common.rb +60 -74
- data/lib/dapp/dimg/dapp/command/mrproper.rb +2 -17
- data/lib/dapp/dimg/dapp/command/stages/cleanup_local.rb +9 -6
- data/lib/dapp/dimg/dimg.rb +26 -43
- data/lib/dapp/dimg/docker_registry/base/authorization.rb +1 -16
- data/lib/dapp/dimg/git_artifact.rb +142 -21
- data/lib/dapp/dimg/git_repo/base.rb +11 -0
- data/lib/dapp/dimg/git_repo/local.rb +14 -0
- data/lib/dapp/dimg/git_repo/remote.rb +25 -34
- data/lib/dapp/dimg/image/argument.rb +12 -58
- data/lib/dapp/dimg/image/stage.rb +202 -43
- data/lib/dapp/kube/kubernetes/client.rb +0 -6
- data/lib/dapp/kube/kubernetes/manager/deployment.rb +18 -30
- data/lib/dapp/version.rb +1 -1
- metadata +8 -8
- data/lib/dapp/dimg/builder/ansible/assets.rb +0 -349
- data/lib/dapp/dimg/exception/introspect_image.rb +0 -7
- data/lib/dapp/dimg/image/docker.rb +0 -144
@@ -13,6 +13,17 @@ module Dapp
|
|
13
13
|
@name = name
|
14
14
|
end
|
15
15
|
|
16
|
+
def get_ruby2go_state_hash
|
17
|
+
{
|
18
|
+
"Dapp" => dapp.get_ruby2go_state_hash,
|
19
|
+
"Name" => @name.to_s,
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def set_ruby2go_state_hash(state)
|
24
|
+
@name = state["Name"]
|
25
|
+
end
|
26
|
+
|
16
27
|
def exclude_paths
|
17
28
|
[]
|
18
29
|
end
|
@@ -9,6 +9,20 @@ module Dapp
|
|
9
9
|
self.path = path
|
10
10
|
end
|
11
11
|
|
12
|
+
def get_ruby2go_state_hash
|
13
|
+
super.tap {|res|
|
14
|
+
p = @path.to_s
|
15
|
+
p = File.dirname(@path) if File.basename(@path) == ".git"
|
16
|
+
res["Path"] = p
|
17
|
+
res["OrigPath"] = @path.to_s
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_ruby2go_state_hash(state)
|
22
|
+
super(state)
|
23
|
+
@path = state["OrigPath"]
|
24
|
+
end
|
25
|
+
|
12
26
|
def path=(path)
|
13
27
|
@path ||= Pathname(Rugged::Repository.new(path).path)
|
14
28
|
rescue Rugged::RepositoryError, Rugged::OSError => _e
|
@@ -8,7 +8,7 @@ module Dapp
|
|
8
8
|
|
9
9
|
class << self
|
10
10
|
def get_or_create(dapp, name, url:)
|
11
|
-
repositories[url] ||= new(dapp, name, url: url).tap(&:
|
11
|
+
repositories[url] ||= new(dapp, name, url: url).tap(&:clone_and_fetch)
|
12
12
|
end
|
13
13
|
|
14
14
|
def repositories
|
@@ -20,20 +20,29 @@ module Dapp
|
|
20
20
|
super(dapp, name)
|
21
21
|
|
22
22
|
@url = url
|
23
|
+
end
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
25
|
+
def ruby2go_method(method, args_hash={})
|
26
|
+
res = dapp.ruby2go_git_repo(args_hash.merge("RemoteGitRepo" => JSON.dump(get_ruby2go_state_hash), "method" => method))
|
27
|
+
|
28
|
+
raise res["error"] if res["error"]
|
29
|
+
|
30
|
+
self.set_ruby2go_state_hash(JSON.load(res["data"]["RemoteGitRepo"]))
|
31
|
+
|
32
|
+
res["data"]["result"]
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_ruby2go_state_hash
|
36
|
+
super.tap {|res|
|
37
|
+
res["Url"] = @url.to_s
|
38
|
+
res["ClonePath"] = dapp.build_path("remote_git_repo", CACHE_VERSION.to_s, dapp.consistent_uniq_slugify(name), remote_origin_url_protocol).to_s # FIXME
|
39
|
+
res["IsDryRun"] = dapp.dry_run?
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
def set_ruby2go_state_hash(state)
|
44
|
+
super(state)
|
45
|
+
@url = state["Url"]
|
37
46
|
end
|
38
47
|
|
39
48
|
def _with_lock(&blk)
|
@@ -54,26 +63,8 @@ module Dapp
|
|
54
63
|
Pathname(dapp.build_path("remote_git_repo", CACHE_VERSION.to_s, dapp.consistent_uniq_slugify(name), remote_origin_url_protocol).to_s)
|
55
64
|
end
|
56
65
|
|
57
|
-
def
|
58
|
-
|
59
|
-
cfg_path = path.join("config")
|
60
|
-
cfg = IniFile.load(cfg_path)
|
61
|
-
remote_origin_cfg = cfg['remote "origin"']
|
62
|
-
|
63
|
-
old_url = remote_origin_cfg["url"]
|
64
|
-
if old_url and old_url != url
|
65
|
-
remote_origin_cfg["url"] = url
|
66
|
-
cfg.write(filename: cfg_path)
|
67
|
-
end
|
68
|
-
|
69
|
-
dapp.log_secondary_process(dapp.t(code: 'process.git_artifact_fetch', data: { url: url }), short: true) do
|
70
|
-
begin
|
71
|
-
git.remotes.each { |remote| remote.fetch(credentials: _rugged_credentials) }
|
72
|
-
rescue Rugged::SshError, Rugged::NetworkError => e
|
73
|
-
raise Error::Rugged, code: :rugged_remote_error, data: { url: url, message: e.message.strip }
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end unless dapp.dry_run?
|
66
|
+
def clone_and_fetch
|
67
|
+
return ruby2go_method("CloneAndFetch")
|
77
68
|
end
|
78
69
|
|
79
70
|
def latest_branch_commit(branch)
|
@@ -11,11 +11,11 @@ module Dapp
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def add_change_env(**options)
|
14
|
-
|
14
|
+
(change_options[:env] ||= {}).merge!(options)
|
15
15
|
end
|
16
16
|
|
17
17
|
def add_change_label(**options)
|
18
|
-
|
18
|
+
(change_options[:label] ||= {}).merge!(options)
|
19
19
|
end
|
20
20
|
|
21
21
|
def add_change_cmd(value)
|
@@ -31,19 +31,19 @@ module Dapp
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def add_change_workdir(value)
|
34
|
-
|
34
|
+
change_options[:workdir] = value
|
35
35
|
end
|
36
36
|
|
37
37
|
def add_change_user(value)
|
38
|
-
|
38
|
+
change_options[:user] = value
|
39
39
|
end
|
40
40
|
|
41
41
|
def add_service_change_label(**options)
|
42
|
-
|
42
|
+
(service_change_options[:label] ||= {}).merge!(options)
|
43
43
|
end
|
44
44
|
|
45
|
-
def add_env(
|
46
|
-
|
45
|
+
def add_env(**options)
|
46
|
+
(self.options[:env] ||= {}).merge!(options)
|
47
47
|
end
|
48
48
|
|
49
49
|
def add_volume(value)
|
@@ -65,10 +65,12 @@ module Dapp
|
|
65
65
|
def prepare_instructions(options)
|
66
66
|
options.map do |key, vals|
|
67
67
|
case key
|
68
|
+
when :workdir, :user
|
69
|
+
[vals]
|
68
70
|
when :cmd, :entrypoint
|
69
71
|
vals = [''] if vals == [] && ::Dapp::Dapp.host_docker_minor_version >= Gem::Version.new('17.10')
|
70
72
|
[vals]
|
71
|
-
when :env, :label then vals
|
73
|
+
when :env, :label then options_to_args(vals)
|
72
74
|
else vals
|
73
75
|
end.map { |val| %(#{key.to_s.upcase} #{val}) }
|
74
76
|
end.flatten
|
@@ -77,7 +79,8 @@ module Dapp
|
|
77
79
|
protected
|
78
80
|
|
79
81
|
attr_reader :bash_commands, :service_bash_commands
|
80
|
-
attr_reader :change_options
|
82
|
+
attr_reader :change_options
|
83
|
+
attr_reader :service_change_options
|
81
84
|
attr_reader :options
|
82
85
|
|
83
86
|
def add_option(key, value)
|
@@ -96,58 +99,9 @@ module Dapp
|
|
96
99
|
hash[key] = (hash[key].nil? ? [value] : (hash[key] << value)).flatten
|
97
100
|
end
|
98
101
|
|
99
|
-
def from_change_options
|
100
|
-
return {} if from.nil?
|
101
|
-
[:entrypoint, :cmd].each_with_object({}) do |option, options|
|
102
|
-
options[option] = from.config_option(option.to_s.capitalize) || []
|
103
|
-
end.tap do |options|
|
104
|
-
workdir = from.config_option('WorkingDir')
|
105
|
-
options[:workdir] = Array((workdir || '').empty? ? '/' : workdir)
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
102
|
def options_to_args(options)
|
110
103
|
options.map { |key, value| "#{key}=#{value}" }
|
111
104
|
end
|
112
|
-
|
113
|
-
def prepared_options
|
114
|
-
all_options.map { |key, vals| Array(vals).map { |val| "--#{key}=#{val}" } }.flatten.join(' ')
|
115
|
-
end
|
116
|
-
|
117
|
-
def all_options
|
118
|
-
service_options.in_depth_merge(options)
|
119
|
-
end
|
120
|
-
|
121
|
-
def all_bash_commands
|
122
|
-
Array(bash_commands) + Array(service_bash_commands)
|
123
|
-
end
|
124
|
-
|
125
|
-
def service_options
|
126
|
-
{
|
127
|
-
workdir: '/',
|
128
|
-
entrypoint: dapp.bash_bin,
|
129
|
-
name: container_name,
|
130
|
-
user: '0:0',
|
131
|
-
:'volumes-from' => [dapp.base_container, dapp.toolchain_container]
|
132
|
-
}
|
133
|
-
end
|
134
|
-
|
135
|
-
def prepared_change
|
136
|
-
prepare_instructions(all_change_options).map { |instruction| %(-c '#{instruction}') }.join(' ')
|
137
|
-
end
|
138
|
-
|
139
|
-
def all_change_options
|
140
|
-
from_change_options.merge(change_options.merge(service_change_options) { |_, v1, v2| [v1, v2].flatten })
|
141
|
-
end
|
142
|
-
|
143
|
-
def prepared_bash_command
|
144
|
-
dapp.shellout_pack prepared_commands.join(' && ')
|
145
|
-
end
|
146
|
-
|
147
|
-
def prepared_commands
|
148
|
-
return [dapp.true_bin] if all_bash_commands.empty?
|
149
|
-
all_bash_commands
|
150
|
-
end
|
151
105
|
end
|
152
106
|
end # Image
|
153
107
|
end # Dimg
|
@@ -1,11 +1,59 @@
|
|
1
1
|
module Dapp
|
2
2
|
module Dimg
|
3
3
|
module Image
|
4
|
-
class Stage
|
4
|
+
class Stage
|
5
5
|
include Argument
|
6
6
|
|
7
|
+
attr_reader :from
|
8
|
+
attr_reader :name
|
9
|
+
attr_reader :dapp
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def image_by_name(name:, **kwargs)
|
13
|
+
images[name] ||= new(name: name, **kwargs)
|
14
|
+
end
|
15
|
+
|
16
|
+
def image_reset(name)
|
17
|
+
images.delete(name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def images
|
21
|
+
@images ||= {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def image_name_format
|
25
|
+
"#{DockerRegistry.repo_name_format}(:(?<tag>#{tag_format}))?"
|
26
|
+
end
|
27
|
+
|
28
|
+
def tag_format
|
29
|
+
'(?![-.])[a-zA-Z0-9_.-]{1,127}'
|
30
|
+
end
|
31
|
+
|
32
|
+
def image_name?(name)
|
33
|
+
!(/^#{image_name_format}$/ =~ name).nil?
|
34
|
+
end
|
35
|
+
|
36
|
+
def tag?(name)
|
37
|
+
!(/^#{tag_format}$/ =~ name).nil?
|
38
|
+
end
|
39
|
+
|
40
|
+
def save!(dapp, image_or_images, file_path, verbose: false, quiet: false)
|
41
|
+
ruby2go_command(dapp, command: :save, options: { images: Array(image_or_images), file_path: file_path })
|
42
|
+
end
|
43
|
+
|
44
|
+
def load!(dapp, file_path, verbose: false, quiet: false)
|
45
|
+
ruby2go_command(dapp, command: :load, options: { file_path: file_path })
|
46
|
+
end
|
47
|
+
|
48
|
+
def ruby2go_command(dapp, command:, **options)
|
49
|
+
dapp.ruby2go_image({ command: command }.merge(options)).tap do |res|
|
50
|
+
raise Error::Build, code: :ruby2go_image_command_failed_unexpected_error, data: { command: command, message: res["error"] } unless res["error"].nil?
|
51
|
+
break res['data']
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
7
56
|
def initialize(name:, dapp:, built_id: nil, from: nil)
|
8
|
-
@container_name = "dapp.build.#{SecureRandom.hex(4)}"
|
9
57
|
@built_id = built_id
|
10
58
|
|
11
59
|
@bash_commands = []
|
@@ -14,79 +62,190 @@ module Dapp
|
|
14
62
|
@change_options = {}
|
15
63
|
@service_change_options = {}
|
16
64
|
|
17
|
-
|
65
|
+
@from = from
|
66
|
+
@name = name
|
67
|
+
@dapp = dapp
|
68
|
+
end
|
69
|
+
|
70
|
+
def tagged?
|
71
|
+
not image_inspect.empty?
|
72
|
+
end
|
73
|
+
|
74
|
+
def built?
|
75
|
+
!built_id.nil?
|
18
76
|
end
|
19
77
|
|
20
78
|
def built_id
|
21
79
|
@built_id ||= id
|
22
80
|
end
|
23
81
|
|
82
|
+
def id
|
83
|
+
image_inspect["Id"]
|
84
|
+
end
|
85
|
+
|
86
|
+
def labels
|
87
|
+
built_image_inspect!.fetch('Config', {}).fetch('Labels', {})
|
88
|
+
end
|
89
|
+
|
90
|
+
def created_at
|
91
|
+
built_image_inspect!["Created"]
|
92
|
+
end
|
93
|
+
|
94
|
+
def size
|
95
|
+
Float(built_image_inspect!["Size"])
|
96
|
+
end
|
97
|
+
|
98
|
+
def built_image_inspect!
|
99
|
+
built_image_inspect
|
100
|
+
end
|
101
|
+
|
102
|
+
def built_image_inspect
|
103
|
+
@built_image_inspect || image_inspect
|
104
|
+
end
|
105
|
+
|
106
|
+
def reset_image_inspect
|
107
|
+
@image_inspect = nil
|
108
|
+
end
|
109
|
+
|
110
|
+
def image_inspect
|
111
|
+
ruby2go_command(:inspect, extended_image_option: false) if @image_inspect.nil?
|
112
|
+
@image_inspect
|
113
|
+
end
|
114
|
+
|
115
|
+
def pull!
|
116
|
+
dapp.log_secondary_process(dapp.t(code: 'process.image_pull', data: { name: name })) do
|
117
|
+
ruby2go_command(:pull)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def push!
|
122
|
+
dapp.log_secondary_process(dapp.t(code: 'process.image_push', data: { name: name })) do
|
123
|
+
ruby2go_command(:push)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
24
127
|
def build!
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
128
|
+
res = self.dapp.ruby2go_image(**ruby2go_image_build_options)
|
129
|
+
if res["error"].nil?
|
130
|
+
set_ruby2go_state_hash(JSON.load(res['data']['image']))
|
131
|
+
elsif res["error"].start_with? "container run failed"
|
132
|
+
raise Error::Build, code: :ruby2go_image_command_failed, data: { command: "build" }
|
133
|
+
else
|
134
|
+
raise Error::Build, code: :ruby2go_image_command_failed_unexpected_error, data: { command: "build", message: res["error"] }
|
135
|
+
end
|
29
136
|
end
|
30
137
|
|
31
|
-
def
|
32
|
-
|
138
|
+
def ruby2go_image_build_options
|
139
|
+
{
|
140
|
+
image: ruby2go_image_option,
|
141
|
+
command: :build,
|
142
|
+
options: {
|
143
|
+
introspection: {
|
144
|
+
before: dapp.introspect_before_error?,
|
145
|
+
after: dapp.introspect_error?
|
146
|
+
}
|
147
|
+
}
|
148
|
+
}
|
149
|
+
end
|
150
|
+
|
151
|
+
def introspect!
|
152
|
+
ruby2go_command(:introspect)
|
33
153
|
end
|
34
154
|
|
35
155
|
def export!(name)
|
36
|
-
|
37
|
-
image.push!
|
38
|
-
image.untag!
|
39
|
-
end
|
156
|
+
ruby2go_command(:export, options: { name: name })
|
40
157
|
end
|
41
158
|
|
42
159
|
def tag!(name)
|
43
|
-
|
44
|
-
self.class.tag!(id: image.built_id, tag: image.name)
|
45
|
-
end
|
160
|
+
ruby2go_command(:tag, options: { name: name })
|
46
161
|
end
|
47
162
|
|
48
163
|
def import!(name)
|
49
|
-
|
50
|
-
image.pull!
|
51
|
-
@built_id = image.built_id
|
52
|
-
save_in_cache!
|
53
|
-
image.untag!
|
54
|
-
end
|
164
|
+
ruby2go_command(:import, options: { name: name })
|
55
165
|
end
|
56
166
|
|
57
167
|
def save_in_cache!
|
58
|
-
|
59
|
-
self.class.tag!(id: built_id, tag: name)
|
168
|
+
ruby2go_command(:save_in_cache)
|
60
169
|
end
|
61
170
|
|
62
|
-
def
|
63
|
-
|
171
|
+
def untag!
|
172
|
+
ruby2go_command(:untag)
|
64
173
|
end
|
65
174
|
|
66
|
-
|
175
|
+
def ruby2go_command(command, extended_image_option: true, options: {})
|
176
|
+
command_options = ruby2go_command_options(command, extended_image_option: extended_image_option).in_depth_merge(options: options)
|
177
|
+
self.class.ruby2go_command(dapp, **command_options).tap do |data|
|
178
|
+
set_ruby2go_state_hash(JSON.load(data['image']))
|
179
|
+
end
|
180
|
+
end
|
67
181
|
|
68
|
-
|
182
|
+
def ruby2go_command_options(command, extended_image_option: true)
|
183
|
+
image = begin
|
184
|
+
if extended_image_option
|
185
|
+
ruby2go_image_option
|
186
|
+
else
|
187
|
+
JSON.dump({name: name})
|
188
|
+
end
|
189
|
+
end
|
69
190
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
191
|
+
{
|
192
|
+
image: image,
|
193
|
+
command: command,
|
194
|
+
}
|
195
|
+
end
|
75
196
|
|
76
|
-
|
77
|
-
|
78
|
-
raise Exception::IntrospectImage, data: { built_id: built_id,
|
79
|
-
options: prepared_options,
|
80
|
-
rmi: dapp.introspect_error?,
|
81
|
-
error: error }
|
197
|
+
def ruby2go_image_option
|
198
|
+
JSON.dump(get_ruby2go_state_hash)
|
82
199
|
end
|
83
200
|
|
84
|
-
def
|
85
|
-
|
201
|
+
def get_ruby2go_state_hash
|
202
|
+
[
|
203
|
+
:name,
|
204
|
+
:from,
|
205
|
+
:built_id,
|
206
|
+
:built_image_inspect,
|
207
|
+
:image_inspect,
|
208
|
+
:bash_commands,
|
209
|
+
:service_bash_commands,
|
210
|
+
:options,
|
211
|
+
:change_options,
|
212
|
+
:service_change_options,
|
213
|
+
].map do |name|
|
214
|
+
if name == :from
|
215
|
+
[name, from.get_ruby2go_state_hash] unless from.nil?
|
216
|
+
elsif name == :built_image_inspect && built_image_inspect.empty?
|
217
|
+
elsif name == :image_inspect && image_inspect.empty?
|
218
|
+
else
|
219
|
+
[name, send(name)]
|
220
|
+
end
|
221
|
+
end
|
222
|
+
.compact
|
223
|
+
.to_h
|
86
224
|
end
|
87
225
|
|
88
|
-
def
|
89
|
-
|
226
|
+
def set_ruby2go_state_hash(state_hash)
|
227
|
+
state_hash.each do |name, value|
|
228
|
+
variable = "@#{name}".to_sym
|
229
|
+
|
230
|
+
case name
|
231
|
+
when "from"
|
232
|
+
from.set_ruby2go_state_hash(value) unless from.nil? || value.nil?
|
233
|
+
when "built_id"
|
234
|
+
if value.to_s.empty?
|
235
|
+
@built_id = nil
|
236
|
+
else
|
237
|
+
@built_id = value
|
238
|
+
end
|
239
|
+
when "image_inspect"
|
240
|
+
instance_variable_set(variable, (value || {}))
|
241
|
+
when "options", "change_options", "service_change_options"
|
242
|
+
instance_variable_set(variable, (value || {}).reject { |_, v| v.nil? || v.empty? }.symbolize_keys)
|
243
|
+
when "bash_commands", "service_bash_commands"
|
244
|
+
instance_variable_set(variable, value || [])
|
245
|
+
else
|
246
|
+
instance_variable_set(variable, value)
|
247
|
+
end
|
248
|
+
end
|
90
249
|
end
|
91
250
|
end # Stage
|
92
251
|
end # Image
|