kuby-core 0.7.2 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -0
  3. data/Gemfile +1 -0
  4. data/README.md +11 -1
  5. data/bin/kuby +4 -0
  6. data/kuby-core.gemspec +9 -4
  7. data/lib/kuby.rb +58 -19
  8. data/lib/kuby/basic_logger.rb +13 -0
  9. data/lib/kuby/cli_base.rb +81 -8
  10. data/lib/kuby/commands.rb +244 -0
  11. data/lib/kuby/definition.rb +1 -11
  12. data/lib/kuby/dev_setup.rb +255 -0
  13. data/lib/kuby/docker.rb +1 -0
  14. data/lib/kuby/docker/bundler_phase.rb +3 -3
  15. data/lib/kuby/docker/cli.rb +13 -1
  16. data/lib/kuby/docker/dev_spec.rb +131 -0
  17. data/lib/kuby/docker/dockerfile.rb +16 -1
  18. data/lib/kuby/docker/layer.rb +4 -4
  19. data/lib/kuby/docker/layer_stack.rb +4 -0
  20. data/lib/kuby/docker/local_tags.rb +4 -0
  21. data/lib/kuby/docker/metadata.rb +18 -38
  22. data/lib/kuby/docker/package_phase.rb +2 -2
  23. data/lib/kuby/docker/setup_phase.rb +3 -2
  24. data/lib/kuby/docker/spec.rb +42 -16
  25. data/lib/kuby/docker/timestamp_tag.rb +6 -0
  26. data/lib/kuby/environment.rb +15 -2
  27. data/lib/kuby/kubernetes.rb +9 -11
  28. data/lib/kuby/kubernetes/deploy_task.rb +4 -1
  29. data/lib/kuby/kubernetes/deployer.rb +70 -15
  30. data/lib/kuby/kubernetes/{minikube_provider.rb → docker_desktop_provider.rb} +8 -4
  31. data/lib/kuby/kubernetes/provider.rb +12 -8
  32. data/lib/kuby/kubernetes/spec.rb +30 -29
  33. data/lib/kuby/plugin.rb +59 -0
  34. data/lib/kuby/plugin_registry.rb +27 -0
  35. data/lib/kuby/plugins.rb +6 -0
  36. data/lib/kuby/plugins/nginx_ingress.rb +71 -0
  37. data/lib/kuby/plugins/rails_app.rb +18 -0
  38. data/lib/kuby/plugins/rails_app/asset_copy_task.rb +117 -0
  39. data/lib/kuby/plugins/rails_app/assets.rb +347 -0
  40. data/lib/kuby/plugins/rails_app/database.rb +74 -0
  41. data/lib/kuby/{kubernetes/plugins → plugins}/rails_app/generators/kuby.rb +14 -16
  42. data/lib/kuby/plugins/rails_app/mysql.rb +152 -0
  43. data/lib/kuby/plugins/rails_app/plugin.rb +593 -0
  44. data/lib/kuby/plugins/rails_app/postgres.rb +143 -0
  45. data/lib/kuby/plugins/rails_app/rewrite_db_config.rb +11 -0
  46. data/lib/kuby/plugins/rails_app/sqlite.rb +32 -0
  47. data/lib/kuby/plugins/rails_app/tasks.rake +36 -0
  48. data/lib/kuby/rails_commands.rb +89 -0
  49. data/lib/kuby/railtie.rb +0 -4
  50. data/lib/kuby/tasks.rb +85 -31
  51. data/lib/kuby/version.rb +1 -1
  52. data/spec/docker/metadata_spec.rb +84 -0
  53. data/spec/docker/spec_spec.rb +266 -0
  54. data/spec/docker/timestamp_tag_spec.rb +54 -4
  55. data/spec/dummy/Gemfile +54 -0
  56. data/spec/dummy/Gemfile.lock +223 -0
  57. data/spec/dummy/README.md +24 -0
  58. data/spec/dummy/Rakefile +6 -0
  59. data/spec/dummy/app/assets/config/manifest.js +2 -0
  60. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  61. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  62. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  63. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  64. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  65. data/spec/dummy/app/javascript/channels/consumer.js +6 -0
  66. data/spec/dummy/app/javascript/channels/index.js +5 -0
  67. data/spec/dummy/app/javascript/packs/application.js +17 -0
  68. data/spec/dummy/app/jobs/application_job.rb +7 -0
  69. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  70. data/spec/dummy/app/models/application_record.rb +3 -0
  71. data/spec/dummy/app/views/layouts/application.html.erb +15 -0
  72. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  73. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  74. data/spec/dummy/bin/bundle +114 -0
  75. data/spec/dummy/bin/rails +9 -0
  76. data/spec/dummy/bin/rake +9 -0
  77. data/spec/dummy/bin/setup +36 -0
  78. data/spec/dummy/bin/spring +17 -0
  79. data/spec/dummy/bin/yarn +11 -0
  80. data/spec/dummy/config.ru +5 -0
  81. data/spec/dummy/config/application.rb +19 -0
  82. data/spec/dummy/config/boot.rb +4 -0
  83. data/spec/dummy/config/cable.yml +10 -0
  84. data/spec/dummy/config/credentials.yml.enc +1 -0
  85. data/spec/dummy/config/database.yml +25 -0
  86. data/spec/dummy/config/environment.rb +5 -0
  87. data/spec/dummy/config/environments/development.rb +62 -0
  88. data/spec/dummy/config/environments/production.rb +112 -0
  89. data/spec/dummy/config/environments/test.rb +49 -0
  90. data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
  91. data/spec/dummy/config/initializers/assets.rb +14 -0
  92. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  93. data/spec/dummy/config/initializers/content_security_policy.rb +30 -0
  94. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  95. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  96. data/spec/dummy/config/initializers/inflections.rb +16 -0
  97. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  98. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  99. data/spec/dummy/config/locales/en.yml +33 -0
  100. data/spec/dummy/config/master.key +1 -0
  101. data/spec/dummy/config/puma.rb +38 -0
  102. data/spec/dummy/config/routes.rb +3 -0
  103. data/spec/dummy/config/spring.rb +6 -0
  104. data/spec/dummy/config/storage.yml +34 -0
  105. data/spec/dummy/db/seeds.rb +7 -0
  106. data/spec/dummy/package.json +11 -0
  107. data/spec/dummy/public/404.html +67 -0
  108. data/spec/dummy/public/422.html +67 -0
  109. data/spec/dummy/public/500.html +66 -0
  110. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  111. data/spec/dummy/public/apple-touch-icon.png +0 -0
  112. data/spec/dummy/public/favicon.ico +0 -0
  113. data/spec/dummy/public/robots.txt +1 -0
  114. data/spec/dummy/test/application_system_test_case.rb +5 -0
  115. data/spec/dummy/test/channels/application_cable/connection_test.rb +11 -0
  116. data/spec/dummy/test/test_helper.rb +13 -0
  117. data/spec/dummy/tmp/cache/bootsnap-load-path-cache +0 -0
  118. data/spec/spec_helper.rb +77 -2
  119. data/spec/support/docker/fake_cli.rb +54 -0
  120. data/spec/support/docker/remote/fake_client.rb +16 -0
  121. data/spec/trailing_hash_spec.rb +23 -0
  122. metadata +139 -30
  123. data/lib/ext/krane/kubernetes_resource.rb +0 -16
  124. data/lib/kuby/kubernetes/plugin.rb +0 -55
  125. data/lib/kuby/kubernetes/plugins.rb +0 -8
  126. data/lib/kuby/kubernetes/plugins/nginx_ingress.rb +0 -73
  127. data/lib/kuby/kubernetes/plugins/rails_app.rb +0 -16
  128. data/lib/kuby/kubernetes/plugins/rails_app/database.rb +0 -79
  129. data/lib/kuby/kubernetes/plugins/rails_app/mysql.rb +0 -154
  130. data/lib/kuby/kubernetes/plugins/rails_app/plugin.rb +0 -379
  131. data/lib/kuby/kubernetes/plugins/rails_app/postgres.rb +0 -142
  132. data/lib/kuby/kubernetes/plugins/rails_app/rewrite_db_config.rb +0 -13
  133. data/lib/kuby/kubernetes/plugins/rails_app/sqlite.rb +0 -30
  134. data/lib/kuby/kubernetes/plugins/rails_app/tasks.rake +0 -28
  135. data/lib/kuby/tasks/kuby.rake +0 -70
@@ -1,3 +1,3 @@
1
1
  module Kuby
2
- VERSION = '0.7.2'
2
+ VERSION = '0.10.0'
3
3
  end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+ require 'timecop'
3
+
4
+ describe Kuby::Docker::Metadata do
5
+ let(:metadata) { definition.environment.docker.metadata }
6
+
7
+ describe '#image_url' do
8
+ subject { metadata.image_url }
9
+
10
+ it { is_expected.to eq(docker_image_url) }
11
+
12
+ context 'when no image URL is configured' do
13
+ let(:docker_image_url) { nil }
14
+ it { is_expected.to eq(definition.app_name) }
15
+ end
16
+ end
17
+
18
+ describe '#image_host' do
19
+ subject { metadata.image_host }
20
+
21
+ it { is_expected.to eq(described_class::DEFAULT_REGISTRY_HOST) }
22
+
23
+ context 'when the image URL contains an explicit host' do
24
+ let(:docker_image_url) { 'registry.foo.com/foo/testapp' }
25
+
26
+ it { is_expected.to eq('https://registry.foo.com') }
27
+ end
28
+
29
+ context 'when the image URL contains an explicit host with scheme' do
30
+ let(:docker_image_url) { 'http://registry.foo.com/foo/testapp' }
31
+
32
+ it { is_expected.to eq('http://registry.foo.com') }
33
+ end
34
+ end
35
+
36
+ describe '#image_repo' do
37
+ subject { metadata.image_repo }
38
+
39
+ it { is_expected.to eq('foo/testapp') }
40
+
41
+ context 'when the image URL contains an explicit host' do
42
+ let(:docker_image_url) { 'registry.foo.com/foo/testapp' }
43
+
44
+ it { is_expected.to eq('foo/testapp') }
45
+ end
46
+ end
47
+
48
+ describe '#image_hostname' do
49
+ subject { metadata.image_hostname }
50
+
51
+ it { is_expected.to eq('www.docker.com') }
52
+
53
+ context 'when the image URL contains an explicit host' do
54
+ let(:docker_image_url) { 'registry.foo.com/foo/testapp' }
55
+
56
+ it { is_expected.to eq('registry.foo.com') }
57
+ end
58
+ end
59
+
60
+ describe '#tags' do
61
+ subject { metadata.tags }
62
+
63
+ it 'specifies the current timestamp tag and the default tag' do
64
+ Timecop.freeze do
65
+ expect(subject).to eq([
66
+ Time.now.strftime('%Y%m%d%H%M%S'),
67
+ Kuby::Docker::Metadata::LATEST_TAG
68
+ ])
69
+ end
70
+ end
71
+ end
72
+
73
+ describe '#distro' do
74
+ subject { metadata.distro }
75
+
76
+ it { is_expected.to eq(Kuby::Docker::Metadata::DEFAULT_DISTRO) }
77
+
78
+ context 'with a distro set manually' do
79
+ before { metadata.distro = :alpine }
80
+
81
+ it { is_expected.to eq(:alpine) }
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,266 @@
1
+ require 'spec_helper'
2
+ require 'timecop'
3
+
4
+ describe Kuby::Docker::Spec do
5
+ let(:spec) { definition.environment.docker }
6
+
7
+ describe '#base_image' do
8
+ subject { spec.to_dockerfile.to_s }
9
+
10
+ it 'uses the default base image for Debian' do
11
+ expect(subject).to include("FROM ruby:#{RUBY_VERSION}\n")
12
+ end
13
+
14
+ context 'when the distro is set to Alpine' do
15
+ before { spec.distro(:alpine) }
16
+
17
+ it 'uses the Alpine base image' do
18
+ expect(subject).to include("FROM ruby:#{RUBY_VERSION}-alpine\n")
19
+ end
20
+ end
21
+
22
+ context 'when the base image is set to something custom' do
23
+ before { spec.base_image('foo/bar') }
24
+
25
+ it 'uses the base image as given' do
26
+ expect(subject).to include("FROM foo/bar\n")
27
+ end
28
+ end
29
+ end
30
+
31
+ describe '#working_dir' do
32
+ let(:default_working_dir) do
33
+ Kuby::Docker::SetupPhase::DEFAULT_WORKING_DIR
34
+ end
35
+
36
+ subject { spec.to_dockerfile.to_s }
37
+
38
+ it 'uses the default working dir' do
39
+ expect(subject).to(
40
+ include("WORKDIR #{default_working_dir}\n")
41
+ )
42
+ end
43
+
44
+ context 'when the working dir is set to something custom' do
45
+ before { spec.working_dir('/foo/bar') }
46
+
47
+ it 'uses the working dir as given' do
48
+ expect(subject).to include("WORKDIR /foo/bar\n")
49
+ end
50
+ end
51
+ end
52
+
53
+ describe '#rails_env' do
54
+ subject { spec.to_dockerfile.to_s }
55
+
56
+ it 'uses the name of the current Kuby environment' do
57
+ expect(subject).to include("ENV RAILS_ENV=#{spec.environment.name}\n")
58
+ expect(subject).to include("ENV KUBY_ENV=#{spec.environment.name}\n")
59
+ end
60
+
61
+ context 'when the environment is set to something custom' do
62
+ before { spec.rails_env('foobar') }
63
+
64
+ it 'uses the environment as given' do
65
+ expect(subject).to include("ENV RAILS_ENV=foobar\n")
66
+ end
67
+
68
+ it "doesn't change the Kuby env" do
69
+ expect(subject).to include("ENV KUBY_ENV=#{spec.environment.name}\n")
70
+ end
71
+ end
72
+ end
73
+
74
+ describe '#bundler_version' do
75
+ subject { spec.to_dockerfile.to_s }
76
+
77
+ it 'installs the current bundler version' do
78
+ expect(subject).to(
79
+ include("RUN gem install bundler -v #{Bundler::VERSION}\n")
80
+ )
81
+ end
82
+
83
+ context 'when the bundler version is set to something custom' do
84
+ before { spec.bundler_version('1.17.3') }
85
+
86
+ it 'installs the given version' do
87
+ expect(subject).to include("RUN gem install bundler -v 1.17.3\n")
88
+ end
89
+ end
90
+ end
91
+
92
+ describe '#gemfile' do
93
+ subject { spec.to_dockerfile.to_s }
94
+
95
+ it 'uses the default Gemfile' do
96
+ expect(subject).to include("COPY Gemfile .\n")
97
+ expect(subject).to include("COPY Gemfile.lock .\n")
98
+ expect(subject).to match(/RUN bundle install .* --gemfile Gemfile/)
99
+ end
100
+
101
+ context 'when the gemfile path is set to something custom' do
102
+ before { spec.gemfile('foo/bar/Gemfile') }
103
+
104
+ it 'uses the given gemfile' do
105
+ expect(subject).to include("COPY foo/bar/Gemfile .\n")
106
+ expect(subject).to include("COPY foo/bar/Gemfile.lock .\n")
107
+ expect(subject).to match(/RUN bundle install .* --gemfile foo\/bar\/Gemfile/)
108
+ end
109
+ end
110
+ end
111
+
112
+ describe '#package' do
113
+ subject { spec.to_dockerfile.to_s }
114
+
115
+ it 'installs the given package' do
116
+ # configured in spec_helper.rb
117
+ spec.package(:fake_package)
118
+
119
+ expect(subject).to match(/apt-get install .* fake_package/m)
120
+ end
121
+ end
122
+
123
+ describe '#files' do
124
+ subject { spec.to_dockerfile.to_s }
125
+
126
+ it 'copies the current directory contents by default' do
127
+ expect(subject).to include("COPY ./ .\n")
128
+ end
129
+
130
+ context 'when given a custom path to copy' do
131
+ before { spec.files('./foo/bar') }
132
+
133
+ it 'copies the given paths only' do
134
+ expect(subject).to include("COPY ./foo/bar .\n")
135
+ end
136
+ end
137
+ end
138
+
139
+ describe '#port' do
140
+ let(:default_port) do
141
+ Kuby::Docker::WebserverPhase::DEFAULT_PORT
142
+ end
143
+
144
+ subject { spec.to_dockerfile.to_s }
145
+
146
+ it 'exposes the default port' do
147
+ expect(subject).to include("EXPOSE #{default_port}\n")
148
+ end
149
+
150
+ context 'when given a custom port' do
151
+ before { spec.port(5555) }
152
+
153
+ it 'exposes the given port' do
154
+ expect(subject).to include("EXPOSE 5555\n")
155
+ end
156
+ end
157
+ end
158
+
159
+ describe '#tag' do
160
+ let(:tag) { make_ts_tag(Time.now) }
161
+
162
+ subject { spec.tag }
163
+
164
+ context 'with no local or remote tags' do
165
+ it 'raises an error' do
166
+ expect { subject }.to raise_error(Kuby::Docker::MissingTagError)
167
+ end
168
+ end
169
+
170
+ context 'with an available remote tag' do
171
+ before { docker_remote_client.tags << tag }
172
+
173
+ it { is_expected.to eq(tag) }
174
+ end
175
+
176
+ context 'with an available local tag' do
177
+ before do
178
+ docker_cli.build(
179
+ dockerfile: nil,
180
+ image_url: docker_image_url,
181
+ tags: [tag]
182
+ )
183
+ end
184
+
185
+ it { is_expected.to eq(tag) }
186
+ end
187
+
188
+ context 'with multiple remote tags' do
189
+ let(:time) { Time.now }
190
+
191
+ before do
192
+ docker_remote_client.tags +=
193
+ [time - 5, time + 10, time - 10, time + 15].map do |t|
194
+ make_ts_tag(t)
195
+ end
196
+ end
197
+
198
+ it { is_expected.to eq(make_ts_tag(time + 15)) }
199
+ end
200
+
201
+ context 'with multiple local and remote tags' do
202
+ let(:time) { Time.now }
203
+
204
+ before do
205
+ docker_remote_client.tags +=
206
+ [time - 5, time + 10, time - 10, time + 15].map do |t|
207
+ make_ts_tag(t)
208
+ end
209
+
210
+ docker_cli.build(
211
+ dockerfile: nil,
212
+ image_url: docker_image_url,
213
+ tags: [time - 3, time + 6, time - 6, time + 18].map do |t|
214
+ make_ts_tag(t)
215
+ end
216
+ )
217
+ end
218
+
219
+ it { is_expected.to eq(make_ts_tag(time + 18)) }
220
+ end
221
+ end
222
+
223
+ describe '#previous_tag' do
224
+ let(:time) { Time.now }
225
+ let(:current_tag) { make_ts_tag(time) }
226
+
227
+ before do
228
+ docker_remote_client.tags << current_tag
229
+ docker_cli.build(
230
+ dockerfile: nil,
231
+ image_url: docker_image_url,
232
+ tags: [current_tag]
233
+ )
234
+ end
235
+
236
+ subject { spec.previous_tag(current_tag) }
237
+
238
+ context 'with no previous local or remote tag' do
239
+ it 'raises an error' do
240
+ expect { subject }.to raise_error(Kuby::Docker::MissingTagError)
241
+ end
242
+ end
243
+
244
+ context 'with an available previous remote tag' do
245
+ let(:previous_tag) { make_ts_tag(time - 5) }
246
+
247
+ before { docker_remote_client.tags << previous_tag }
248
+
249
+ it { is_expected.to eq(previous_tag) }
250
+ end
251
+
252
+ context 'with an available previous local tag' do
253
+ let(:previous_tag) { make_ts_tag(time - 5) }
254
+
255
+ before do
256
+ docker_cli.build(
257
+ dockerfile: nil,
258
+ image_url: docker_image_url,
259
+ tags: [previous_tag]
260
+ )
261
+ end
262
+
263
+ it { is_expected.to eq(previous_tag) }
264
+ end
265
+ end
266
+ end
@@ -1,11 +1,61 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Kuby::Docker::TimestampTag do
4
- context ".try_parse" do
5
- it "returns a new timestamp tag" do
6
- tag = described_class.try_parse("20200810165134")
4
+ context '.try_parse' do
5
+ let(:tag_str) { '20200810165134' }
7
6
 
8
- expect(tag).to be_kind_of(described_class)
7
+ it 'creates a new timestamp tag' do
8
+ tag = described_class.try_parse(tag_str)
9
+ expect(tag).to be_a(described_class)
10
+ end
11
+
12
+ it 'correctly parses the timestamp contained in the tag' do
13
+ time = described_class.try_parse(tag_str).time
14
+ expect([time.year, time.month, time.day, time.hour, time.min, time.sec]).to(
15
+ eq([2020, 8, 10, 16, 51, 34])
16
+ )
17
+ end
18
+
19
+ context 'with an invalid tag' do
20
+ let(:tag_str) { 'abc123' }
21
+
22
+ it 'returns nil' do
23
+ expect(described_class.try_parse(tag_str)).to eq(nil)
24
+ end
25
+ end
26
+ end
27
+
28
+ context '#to_s' do
29
+ it 'serializes the tag as a timestamp' do
30
+ tag = described_class.new(Time.new(2020, 8, 10, 16, 51, 34))
31
+ expect(tag.to_s).to eq('20200810165134')
32
+ end
33
+ end
34
+
35
+ context 'comparison' do
36
+ it 'ensures tags can be compared by their timestamp values' do
37
+ seed_time = Time.now
38
+ times = [seed_time, seed_time + 5, seed_time + 10, seed_time + 15].shuffle
39
+ tags = times.map { |t| described_class.new(t) }
40
+ expect(tags.sort.map(&:time)).to eq(times.sort)
41
+ end
42
+ end
43
+
44
+ context 'equality' do
45
+ it 'ensures tags with equal times are considered equal' do
46
+ time = Time.now
47
+ tag1 = described_class.new(time)
48
+ tag2 = described_class.new(time)
49
+ expect(tag1).to eq(tag2)
50
+ expect(tag1.hash).to eq(tag2.hash)
51
+ end
52
+
53
+ it 'ensures tags with inequal times are not considered equal' do
54
+ time = Time.now
55
+ tag1 = described_class.new(time)
56
+ tag2 = described_class.new(time + 5)
57
+ expect(tag1).to_not eq(tag2)
58
+ expect(tag1.hash).to_not eq(tag2.hash)
9
59
  end
10
60
  end
11
61
  end
@@ -0,0 +1,54 @@
1
+ source 'https://rubygems.org'
2
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
+
4
+ ruby '2.5.8'
5
+
6
+ # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
7
+ gem 'rails', '~> 6.0.3', '>= 6.0.3.2'
8
+ # Use sqlite3 as the database for Active Record
9
+ gem 'sqlite3', '~> 1.4'
10
+ # Use Puma as the app server
11
+ gem 'puma', '~> 4.1'
12
+ # Use SCSS for stylesheets
13
+ gem 'sass-rails', '>= 6'
14
+ # Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
15
+ gem 'webpacker', '~> 4.0'
16
+ # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
17
+ gem 'turbolinks', '~> 5'
18
+ # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
19
+ gem 'jbuilder', '~> 2.7'
20
+ # Use Redis adapter to run Action Cable in production
21
+ # gem 'redis', '~> 4.0'
22
+ # Use Active Model has_secure_password
23
+ # gem 'bcrypt', '~> 3.1.7'
24
+
25
+ # Use Active Storage variant
26
+ # gem 'image_processing', '~> 1.2'
27
+
28
+ # Reduces boot times through caching; required in config/boot.rb
29
+ gem 'bootsnap', '>= 1.4.2', require: false
30
+
31
+ group :development, :test do
32
+ # Call 'byebug' anywhere in the code to stop execution and get a debugger console
33
+ gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
34
+ end
35
+
36
+ group :development do
37
+ # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
38
+ gem 'web-console', '>= 3.3.0'
39
+ gem 'listen', '~> 3.2'
40
+ # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
41
+ gem 'spring'
42
+ gem 'spring-watcher-listen', '~> 2.0.0'
43
+ end
44
+
45
+ group :test do
46
+ # Adds support for Capybara system testing and selenium driver
47
+ gem 'capybara', '>= 2.15'
48
+ gem 'selenium-webdriver'
49
+ # Easy installation and use of web drivers to run system tests with browsers
50
+ gem 'webdrivers'
51
+ end
52
+
53
+ # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
54
+ gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]