wellcar 0.0.2 → 0.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d9629130bcd16fffcc9864daedb27e5f5795c660a33f92816a33851dac2bb98c
4
- data.tar.gz: d59cc9b695eeaff5af97a498c589b053ab454f6f99583e1a54e913bf19172d7c
3
+ metadata.gz: 90b0b4af7693396802151c5b3e91c743886fe85dc36c3b117531b6b579e8253b
4
+ data.tar.gz: 94a331a46e2629cc0dc8dc19b5242e0d5a7f50147224921f8514c02fde3d0f22
5
5
  SHA512:
6
- metadata.gz: e6da3f0f58f3c7ee6788ed5fd30bd767a8ac3c778a59e797bf174f5762db7eef75f742bdb9cd149e8327f66999f2291fefbfc8ddee139a94bc8849d218d2ce04
7
- data.tar.gz: f14698f09a94e51c8072f9812b7eef731fb19be0752d03895679b185e6e54a17715acbb1280651fff8273742deb02299495ba80b387999803e4cbf6e4345dde4
6
+ metadata.gz: 781ac4148cbaef8b289dfc3ffad80ce738919c3436597fb3e56e54d0d4e7ee04fa26365fee367a0fbf67bbafc8cd01f8ed795a9d7bb9b15575409e648187d7ca
7
+ data.tar.gz: c43cf9ff268e5019b9afa86079fd19e52fa2ee8ba0786d2b904f52f9a5b03699ed06b99da6355fc9fe9168ccf72e2c70c3139e57654e0f76d74731c811019241
@@ -10,278 +10,7 @@ rescue LoadError => e
10
10
  exit 64
11
11
  end
12
12
 
13
- class CommandBase
14
- attr_accessor :ruby_version,
15
- :app_name,
16
- :use_bundler_2,
17
- :github_account,
18
- :domain_name
19
13
 
20
- def core_docker_templates
21
- master_key = File.exist?("config/credentials/production.key") ?
22
- File.read("config/credentials/production.key") :
23
- nil
24
-
25
- [
26
- Wellcar::Templates::Dockerfile.new(ruby_version, app_name, use_bundler_2),
27
- Wellcar::Templates::DockerStack.new(app_name, github_account),
28
- Wellcar::Templates::EnvDevelopmentDatabase.new(app_name),
29
- Wellcar::Templates::EnvDevelopmentWeb.new,
30
- Wellcar::Templates::EnvProductionWeb.new(master_key),
31
- Wellcar::Templates::EnvProductionDatabase.new(app_name),
32
- Wellcar::Templates::Dockerignore.new,
33
- Wellcar::Templates::DockerEntrypoint.new,
34
- Wellcar::Templates::BuildProductionImage.new(app_name, github_account),
35
- Wellcar::Templates::ReverseProxyConf.new(domain_name)
36
- ]
37
- end
38
-
39
- def create_directories
40
- FileUtils.mkdir_p "config"
41
- FileUtils.mkdir_p "bin"
42
- FileUtils.mkdir_p ".env/development"
43
- FileUtils.mkdir_p ".env/production"
44
- end
45
-
46
- def write_files
47
- files_before_docker = Dir['*']
48
-
49
- system "curl -o bin/wait-for https://raw.githubusercontent.com/mrako/wait-for/master/wait-for"
50
-
51
- docker_files.each(&:write)
52
- puts "Wellcar created:\n#{Dir['*'] - files_before_docker}\n"
53
- end
54
-
55
- def create_wellcar_folder
56
- return if Dir.exist? ".wellcar"
57
-
58
- Dir.mkdir ".wellcar"
59
- FileUtils.touch ".wellcar/.keep"
60
- end
61
-
62
- def clean_docker
63
- # Make sure that no pre-existing containers are running. Although
64
- # very unlikely, it's worth being thorough. This makes the command
65
- # slow but for the time being this ensures the containers and images
66
- # are as expected.
67
- exit 8 unless system "docker-compose down --remove-orphans"
68
-
69
- removable_volumes = ["#{app_name}_gem_cache", "#{app_name}_node_modules"]
70
- volumes_to_remove = %x(docker volume ls --format='{{.Name}}')
71
- .split("\n") & removable_volumes
72
-
73
- return if volumes_to_remove.empty?
74
-
75
- puts "Removing the following volumes: #{volumes_to_remove.join(", ")}"
76
-
77
- system "docker volume rm #{volumes_to_remove.join(" ")}"
78
- end
79
-
80
- def build_development_environment
81
- exit 128 unless system "docker-compose build web webpack-dev-server database"
82
- end
83
- end
84
-
85
- class NewCommand < CommandBase
86
- def initialize(app_name, github_account, domain_name)
87
- self.app_name = app_name
88
- self.domain_name = domain_name
89
- self.github_account = github_account
90
- self.ruby_version = "2.6.6"
91
- self.use_bundler_2 = true
92
- end
93
-
94
- def docker_files
95
- @files ||= core_docker_templates.push(
96
- Wellcar::Templates::DockerfileInit.new(ruby_version, app_name),
97
- Wellcar::Templates::DockerCompose.new(app_name,
98
- github_account,
99
- Wellcar::Templates::DockerCompose::INIT_TEMPLATE)
100
- )
101
- end
102
-
103
- def try_full_nuke(nuke)
104
- if nuke && Dir.exists?(app_name)
105
- puts "*** Destroying the existing folder #{app_name} ***"
106
- FileUtils.rm_rf app_name
107
- elsif Dir.exists? app_name
108
- puts "Cannot overwrite existing folder #{Dir.pwd}/#{app_name}"
109
- exit 1
110
- end
111
- end
112
-
113
- def within_app_folder(&block)
114
- raise NotImplemented if block.nil?
115
-
116
- Dir.mkdir app_name
117
-
118
- Dir.chdir(app_name) do
119
- yield
120
- end
121
- end
122
-
123
- def perform_setup
124
- # Create app and install webpacker
125
- exit 16 unless system "docker-compose up --build new_rails"
126
- exit 32 unless system "docker-compose up --build bundle"
127
- exit 64 unless system "docker-compose up --build install_webpacker"
128
- puts "Rails app created; initializing for Docker\n"
129
- end
130
-
131
- def tidy_up_setup
132
- # remove dockerfile.init and docker-compose.yml with initialisation services
133
- %w(Dockerfile.init docker-compose.yml config/database.yml).each {|f| File.delete(f) if File.file?(f) }
134
- # Create docker-compose.yml file
135
- Wellcar::Templates::DockerCompose.new(app_name, github_account).write
136
- Wellcar::Templates::DatabaseYaml.new(app_name).write
137
- end
138
-
139
- def call(options, args)
140
- # Here we want to create a new rails app that is set up with docker.
141
- # We use `rails new` within a `docker run` command but skip a lot of set up so
142
- # that further setup steps can be taken later on.
143
- # What I don't know right now is how steps that are skipped at the start can be
144
- # initiated in an existing app.
145
- #
146
- # My list of pre-requisites for a Rails app are:
147
- # * RSpec
148
- # * Webpack + yarn
149
- # * Rubocop
150
- # * PostgreSQL
151
- # * Redis
152
- # * FactoryBot
153
- # * Capybara with a real webdriver
154
- # * Devise
155
-
156
- try_full_nuke options[:full_nuke]
157
-
158
- within_app_folder do
159
- create_wellcar_folder
160
- create_directories
161
- write_files
162
- clean_docker
163
- perform_setup
164
- tidy_up_setup
165
- build_development_environment
166
- end
167
-
168
- puts <<-FINISHED
169
- Your new Rails application "#{app_name}" has been created with Docker!
170
-
171
- Don't forget to commit everything to git.
172
-
173
- You can use wellcar to interact with the Docker containers and the Rails application as you develop your app.
174
-
175
- Run `wellcar` for help with commands.
176
- FINISHED
177
- end
178
- end
179
-
180
- class DockCommand < CommandBase
181
- def initialize(github_account, domain_name)
182
- self.github_account = github_account
183
- self.domain_name = domain_name
184
- end
185
-
186
- def interpret_app_name
187
- app_name = File.read("./config/application.rb")
188
- .split("\n")
189
- .select {|line| line =~ /^module / }
190
- .first
191
- &.split
192
- &.last
193
- &.downcase
194
-
195
- return app_name unless app_name.nil?
196
-
197
- puts "Cannot determine app name from config/application.rb. wellcar expects to find one module in this file named for the application."
198
- exit 5
199
- end
200
-
201
- def interpret_ruby_version
202
- File.file?(".ruby_version") ?
203
- File.read(".ruby_version").split.first :
204
- "2.6.6"
205
- end
206
-
207
- def interpret_bundler_2
208
- b2 = File.file?("Gemfile.lock") &&
209
- File.read("Gemfile.lock").split("BUNDLED WITH")&.last.strip >= "2.0.0"
210
- puts "Install bundler 2? #{b2}"
211
- b2
212
- end
213
-
214
- def determine_variables
215
- self.app_name = interpret_app_name
216
- self.ruby_version = interpret_ruby_version
217
- self.use_bundler_2 = interpret_bundler_2
218
- end
219
-
220
- def prepare_templates
221
- docker_files
222
- end
223
-
224
- def docker_files
225
- @files ||= core_docker_templates.push(Wellcar::Templates::DockerCompose.new(app_name, github_account))
226
- end
227
-
228
- def check_existing_files(force_new)
229
- files_exist = docker_files.select {|file| file.exist? }.any?
230
- if force_new && files_exist
231
- puts "--force-new set. Overwriting **all** existing docker files."
232
-
233
- if Dir.exist? ".env"
234
- label = Time.new.strftime("%Y%m%d%H%M%S")
235
- puts ".env will be backed up to .wellcar/env-#{label}"
236
- destination = ".wellcar/env-#{label}"
237
- FileUtils.copy_entry ".env", destination
238
- exit unless Dir.exist? destination
239
- end
240
- elsif files_exist
241
- puts "Found existing docker files. Please check your app source tree before trying to dockerise this app with wellcar again."
242
- exit 3
243
- end
244
- end
245
-
246
- def update_database_config
247
- Wellcar::Templates::DatabaseYaml.new(app_name).update
248
- puts <<-DB
249
- ... updated development settings for database.yml.
250
- Now using dockerised PostgreSQL server. Previous config saved as config/database.pre_docker.yml
251
- DB
252
- end
253
-
254
- # This does not currently work, as it is called before bundle is available. I'm debating how
255
- # to include this "gem tidying" aspect of adding Docker to an existing project.
256
- def tidy_gemfile
257
- %w(sqlite3 figaro dotenv).each {|gem| system "docker-compose run --rm bin/bundle remove #{gem}" }
258
- system "docker-compose run --rm bin/bundle add pg" unless system("docker-compose run --rm bin/bundle info pg")
259
- end
260
-
261
- def call(options, args)
262
- create_wellcar_folder
263
- determine_variables
264
- prepare_templates
265
- check_existing_files options[:"force-new"]
266
- create_directories
267
- write_files
268
- update_database_config
269
- clean_docker
270
- # This assumes bin/bundle is available, which will not be the case.
271
- # The gem set up done in this call should be part of a Dockerfile, but needs
272
- # a script to manage some conditional logic.
273
- # tidy_gemfile
274
- build_development_environment
275
-
276
- puts <<-DONE
277
- Your Rails app has been dockerised!
278
-
279
- You can use wellcar to interact with the Docker containers and the Rails application as you develop your app.
280
-
281
- Run `wellcar` for help with commands.
282
- DONE
283
- end
284
- end
285
14
 
286
15
  class App
287
16
  extend GLI::App
@@ -307,7 +36,7 @@ class App
307
36
  required: true,
308
37
  desc: "Sets the GitHub account used in building Docker image paths (which assumes images are hosted as GitHub Packages)"
309
38
  c.action do |global_options, options, args|
310
- NewCommand.new(args[0], options['github-account'], options[:domain]).call options, args
39
+ WellCar::Commmands::New.new(args[0], options['github-account'], options[:domain]).call options, args
311
40
  end
312
41
  end
313
42
 
@@ -325,7 +54,7 @@ class App
325
54
  required: true
326
55
 
327
56
  c.action do |global_options, options, args|
328
- DockCommand.new(options['github-account'], options[:domain]).call options, args
57
+ WellCar::Commmands::Dock.new(options['github-account'], options[:domain]).call options, args
329
58
  end
330
59
  end
331
60
 
@@ -1,4 +1,7 @@
1
1
  require 'wellcar/version.rb'
2
+ require 'wellcar/commands/base.rb'
3
+ require 'wellcar/commands/new.rb'
4
+ require 'wellcar/commands/dock.rb'
2
5
  require 'wellcar/templates/base.rb'
3
6
  require 'wellcar/templates/dockerfile.rb'
4
7
  require 'wellcar/templates/dockerfile_init.rb'
@@ -0,0 +1,75 @@
1
+ module Wellcar
2
+ module Commands
3
+ class Base
4
+ attr_accessor :ruby_version,
5
+ :app_name,
6
+ :use_bundler_2,
7
+ :github_account,
8
+ :domain_name
9
+
10
+ def core_docker_templates
11
+ master_key = File.exist?("config/credentials/production.key") ?
12
+ File.read("config/credentials/production.key") :
13
+ nil
14
+
15
+ [
16
+ Wellcar::Templates::Dockerfile.new(ruby_version, app_name, use_bundler_2),
17
+ Wellcar::Templates::DockerStack.new(app_name, github_account),
18
+ Wellcar::Templates::EnvDevelopmentDatabase.new(app_name),
19
+ Wellcar::Templates::EnvDevelopmentWeb.new,
20
+ Wellcar::Templates::EnvProductionWeb.new(master_key),
21
+ Wellcar::Templates::EnvProductionDatabase.new(app_name),
22
+ Wellcar::Templates::Dockerignore.new,
23
+ Wellcar::Templates::DockerEntrypoint.new,
24
+ Wellcar::Templates::BuildProductionImage.new(app_name, github_account),
25
+ Wellcar::Templates::ReverseProxyConf.new(domain_name)
26
+ ]
27
+ end
28
+
29
+ def create_directories
30
+ FileUtils.mkdir_p "config"
31
+ FileUtils.mkdir_p "bin"
32
+ FileUtils.mkdir_p ".env/development"
33
+ FileUtils.mkdir_p ".env/production"
34
+ end
35
+
36
+ def write_files
37
+ files_before_docker = Dir['*']
38
+
39
+ system "curl -o bin/wait-for https://raw.githubusercontent.com/mrako/wait-for/master/wait-for"
40
+
41
+ docker_files.each(&:write)
42
+ puts "Wellcar created:\n#{Dir['*'] - files_before_docker}\n"
43
+ end
44
+
45
+ def create_wellcar_folder
46
+ return if Dir.exist? ".wellcar"
47
+
48
+ Dir.mkdir ".wellcar"
49
+ FileUtils.touch ".wellcar/.keep"
50
+ end
51
+
52
+ def clean_docker
53
+ # Make sure that no pre-existing containers are running. Although
54
+ # very unlikely, it's worth being thorough. This makes the command
55
+ # slow but for the time being this ensures the containers and images
56
+ # are as expected.
57
+ exit 8 unless system "docker-compose down --remove-orphans"
58
+
59
+ removable_volumes = ["#{app_name}_gem_cache", "#{app_name}_node_modules"]
60
+ volumes_to_remove = %x(docker volume ls --format='{{.Name}}')
61
+ .split("\n") & removable_volumes
62
+
63
+ return if volumes_to_remove.empty?
64
+
65
+ puts "Removing the following volumes: #{volumes_to_remove.join(", ")}"
66
+
67
+ system "docker volume rm #{volumes_to_remove.join(" ")}"
68
+ end
69
+
70
+ def build_development_environment
71
+ exit 128 unless system "docker-compose build web webpack-dev-server database"
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,109 @@
1
+ module Wellcar
2
+ module Commands
3
+ class Dock < Base
4
+ def initialize(github_account, domain_name)
5
+ self.github_account = github_account
6
+ self.domain_name = domain_name
7
+ end
8
+
9
+ def interpret_app_name
10
+ app_name = File.read("./config/application.rb")
11
+ .split("\n")
12
+ .select {|line| line =~ /^module / }
13
+ .first
14
+ &.split
15
+ &.last
16
+ &.downcase
17
+
18
+ return app_name unless app_name.nil?
19
+
20
+ puts "Cannot determine app name from config/application.rb. wellcar expects to find one module in this file named for the application."
21
+ exit 5
22
+ end
23
+
24
+ def interpret_ruby_version
25
+ File.file?(".ruby_version") ?
26
+ File.read(".ruby_version").split.first :
27
+ "2.6.6"
28
+ end
29
+
30
+ def interpret_bundler_2
31
+ b2 = File.file?("Gemfile.lock") &&
32
+ File.read("Gemfile.lock").split("BUNDLED WITH")&.last.strip >= "2.0.0"
33
+ puts "Install bundler 2? #{b2}"
34
+ b2
35
+ end
36
+
37
+ def determine_variables
38
+ self.app_name = interpret_app_name
39
+ self.ruby_version = interpret_ruby_version
40
+ self.use_bundler_2 = interpret_bundler_2
41
+ end
42
+
43
+ def prepare_templates
44
+ docker_files
45
+ end
46
+
47
+ def docker_files
48
+ @files ||= core_docker_templates.push(Wellcar::Templates::DockerCompose.new(app_name, github_account))
49
+ end
50
+
51
+ def check_existing_files(force_new)
52
+ files_exist = docker_files.select {|file| file.exist? }.any?
53
+ if force_new && files_exist
54
+ puts "--force-new set. Overwriting **all** existing docker files."
55
+
56
+ if Dir.exist? ".env"
57
+ label = Time.new.strftime("%Y%m%d%H%M%S")
58
+ puts ".env will be backed up to .wellcar/env-#{label}"
59
+ destination = ".wellcar/env-#{label}"
60
+ FileUtils.copy_entry ".env", destination
61
+ exit unless Dir.exist? destination
62
+ end
63
+ elsif files_exist
64
+ puts "Found existing docker files. Please check your app source tree before trying to dockerise this app with wellcar again."
65
+ exit 3
66
+ end
67
+ end
68
+
69
+ def update_database_config
70
+ Wellcar::Templates::DatabaseYaml.new(app_name).update
71
+ puts <<-DB
72
+ ... updated development settings for database.yml.
73
+ Now using dockerised PostgreSQL server. Previous config saved as config/database.pre_docker.yml
74
+ DB
75
+ end
76
+
77
+ # This does not currently work, as it is called before bundle is available. I'm debating how
78
+ # to include this "gem tidying" aspect of adding Docker to an existing project.
79
+ def tidy_gemfile
80
+ %w(sqlite3 figaro dotenv).each {|gem| system "docker-compose run --rm bin/bundle remove #{gem}" }
81
+ system "docker-compose run --rm bin/bundle add pg" unless system("docker-compose run --rm bin/bundle info pg")
82
+ end
83
+
84
+ def call(options, args)
85
+ create_wellcar_folder
86
+ determine_variables
87
+ prepare_templates
88
+ check_existing_files options[:"force-new"]
89
+ create_directories
90
+ write_files
91
+ update_database_config
92
+ clean_docker
93
+ # This assumes bin/bundle is available, which will not be the case.
94
+ # The gem set up done in this call should be part of a Dockerfile, but needs
95
+ # a script to manage some conditional logic.
96
+ # tidy_gemfile
97
+ build_development_environment
98
+
99
+ puts <<-DONE
100
+ Your Rails app has been dockerised!
101
+
102
+ You can use wellcar to interact with the Docker containers and the Rails application as you develop your app.
103
+
104
+ Run `wellcar` for help with commands.
105
+ DONE
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,98 @@
1
+ module Wellcar
2
+ module Commands
3
+ class New < Base
4
+ def initialize(app_name, github_account, domain_name)
5
+ self.app_name = app_name
6
+ self.domain_name = domain_name
7
+ self.github_account = github_account
8
+ self.ruby_version = "2.6.6"
9
+ self.use_bundler_2 = true
10
+ end
11
+
12
+ def docker_files
13
+ @files ||= core_docker_templates.push(
14
+ Wellcar::Templates::DockerfileInit.new(ruby_version, app_name),
15
+ Wellcar::Templates::DockerCompose.new(app_name,
16
+ github_account,
17
+ Wellcar::Templates::DockerCompose::INIT_TEMPLATE)
18
+ )
19
+ end
20
+
21
+ def try_full_nuke(nuke)
22
+ if nuke && Dir.exists?(app_name)
23
+ puts "*** Destroying the existing folder #{app_name} ***"
24
+ FileUtils.rm_rf app_name
25
+ elsif Dir.exists? app_name
26
+ puts "Cannot overwrite existing folder #{Dir.pwd}/#{app_name}"
27
+ exit 1
28
+ end
29
+ end
30
+
31
+ def within_app_folder(&block)
32
+ raise NotImplemented if block.nil?
33
+
34
+ Dir.mkdir app_name
35
+
36
+ Dir.chdir(app_name) do
37
+ yield
38
+ end
39
+ end
40
+
41
+ def perform_setup
42
+ # Create app and install webpacker
43
+ exit 16 unless system "docker-compose up --build new_rails"
44
+ exit 32 unless system "docker-compose up --build bundle"
45
+ exit 64 unless system "docker-compose up --build install_webpacker"
46
+ puts "Rails app created; initializing for Docker\n"
47
+ end
48
+
49
+ def tidy_up_setup
50
+ # remove dockerfile.init and docker-compose.yml with initialisation services
51
+ %w(Dockerfile.init docker-compose.yml config/database.yml).each {|f| File.delete(f) if File.file?(f) }
52
+ # Create docker-compose.yml file
53
+ Wellcar::Templates::DockerCompose.new(app_name, github_account).write
54
+ Wellcar::Templates::DatabaseYaml.new(app_name).write
55
+ end
56
+
57
+ def call(options, args)
58
+ # Here we want to create a new rails app that is set up with docker.
59
+ # We use `rails new` within a `docker run` command but skip a lot of set up so
60
+ # that further setup steps can be taken later on.
61
+ # What I don't know right now is how steps that are skipped at the start can be
62
+ # initiated in an existing app.
63
+ #
64
+ # My list of pre-requisites for a Rails app are:
65
+ # * RSpec
66
+ # * Webpack + yarn
67
+ # * Rubocop
68
+ # * PostgreSQL
69
+ # * Redis
70
+ # * FactoryBot
71
+ # * Capybara with a real webdriver
72
+ # * Devise
73
+
74
+ try_full_nuke options[:full_nuke]
75
+
76
+ within_app_folder do
77
+ create_wellcar_folder
78
+ create_directories
79
+ write_files
80
+ clean_docker
81
+ perform_setup
82
+ tidy_up_setup
83
+ build_development_environment
84
+ end
85
+
86
+ puts <<-FINISHED
87
+ Your new Rails application "#{app_name}" has been created with Docker!
88
+
89
+ Don't forget to commit everything to git.
90
+
91
+ You can use wellcar to interact with the Docker containers and the Rails application as you develop your app.
92
+
93
+ Run `wellcar` for help with commands.
94
+ FINISHED
95
+ end
96
+ end
97
+ end
98
+ end
@@ -1,3 +1,3 @@
1
1
  module Wellcar
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.3'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wellcar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - NJ Pearman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-06 00:00:00.000000000 Z
11
+ date: 2020-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -85,6 +85,9 @@ files:
85
85
  - bin/wellcar
86
86
  - command_history_example.txt
87
87
  - lib/wellcar.rb
88
+ - lib/wellcar/commands/base.rb
89
+ - lib/wellcar/commands/dock.rb
90
+ - lib/wellcar/commands/new.rb
88
91
  - lib/wellcar/templates/base.rb
89
92
  - lib/wellcar/templates/build-production-image.yml.erb
90
93
  - lib/wellcar/templates/build_production_image.rb