railsmdb 1.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,545 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'railsmdb/prioritizable'
4
+ require 'active_support/encrypted_configuration'
5
+ require 'os'
6
+ require 'railsmdb/version'
7
+ require 'railsmdb/crypt_shared/catalog'
8
+ require 'railsmdb/downloader'
9
+ require 'railsmdb/extractor'
10
+ require 'rails/generators/rails/app/app_generator'
11
+ require 'digest'
12
+ require 'mongoid'
13
+
14
+ module Railsmdb
15
+ module Generators
16
+ module Setup
17
+ module Concerns
18
+ # Tasks used for configuring a Rails app to use Mongoid, including
19
+ # adding support for encryption. This concern is shared between
20
+ # the app generator (e.g. `railsmdb new`) and the setup generator
21
+ # (e.g. `railsmdb setup`).
22
+ #
23
+ # @api private
24
+ module Setuppable
25
+ extend ActiveSupport::Concern
26
+
27
+ include Railsmdb::Prioritizable
28
+
29
+ GemfileEntry = Rails::Generators::AppBase::GemfileEntry
30
+
31
+ KEY_VAULT_CONFIG = <<~CONFIG
32
+ # This client is used to obtain the encryption keys from the key vault.
33
+ # For security reasons, this should be a different database instance than
34
+ # your primary application database.
35
+ key_vault:
36
+ uri: mongodb://localhost:27017
37
+
38
+ CONFIG
39
+
40
+ AUTO_ENCRYPTION_CONFIG = <<~CONFIG.freeze
41
+ # You can read about the auto encryption options here:
42
+ # https://www.mongodb.com/docs/ruby-driver/v#{Mongo::VERSION.split('.').first(2).join('.')}/reference/in-use-encryption/client-side-encryption/#auto-encryption-options
43
+ auto_encryption_options:
44
+ key_vault_client: 'key_vault'
45
+ key_vault_namespace: 'encryption.__keyVault'
46
+ kms_providers:
47
+ # Using a local master key is insecure and is not recommended if you plan
48
+ # to use client-side encryption in production.
49
+ #
50
+ # To learn how to set up a remote Key Management Service, see the tutorials
51
+ # at https://www.mongodb.com/docs/manual/core/csfle/tutorials/.
52
+ local:
53
+ key: '<%= Rails.application.credentials.mongodb_master_key %>'
54
+ extra_options:
55
+ crypt_shared_lib_path: %crypt-shared-path%
56
+
57
+ CONFIG
58
+
59
+ PRELOAD_MODELS_OPTION = <<~CONFIG
60
+ #
61
+ # Setting it to true is recommended for auto encryption to work
62
+ # properly in development.
63
+ preload_models: true
64
+ CONFIG
65
+
66
+ included do
67
+ # add the setup generator templates folder to the source path
68
+ source_paths.unshift File.join(__dir__, '..', 'templates')
69
+
70
+ # An option for enabling MongoDB encryption features in the new app.
71
+ class_option :encryption, type: :boolean,
72
+ aliases: '-E',
73
+ default: false,
74
+ desc: 'Add gems and configuration to enable MongoDB encryption features'
75
+
76
+ # Add an option for accepting the customer agreement related to
77
+ # MongoDB enterprise, allowing the acceptance prompt to be skipped.
78
+ class_option :accept_customer_agreement, type: :boolean,
79
+ default: false,
80
+ desc: 'Accept the MongoDB Customer Agreement'
81
+ end
82
+
83
+ # Save the current directory; this way, we can see
84
+ # if it is being run from the railsmdb project directory, in
85
+ # development, and set up the railsmdb gem dependency appropriately.
86
+ def save_initial_path
87
+ @initial_path = Dir.pwd
88
+ end
89
+
90
+ # Checks to see if the user agrees to the encryption terms and conditions
91
+ def confirm_legal_shenanigans
92
+ return unless options[:encryption]
93
+
94
+ @okay_to_support_encryption =
95
+ options[:accept_customer_agreement] ||
96
+ okay_with_legal_shenanigans?
97
+ end
98
+
99
+ # Fetches the MongoDB crypt_shared library and stores it in
100
+ # vendor/crypt_shared.
101
+ def fetch_crypt_shared
102
+ return unless @okay_to_support_encryption
103
+
104
+ log :fetch, 'current MongoDB catalog'
105
+ catalog = Railsmdb::CryptShared::Catalog.current
106
+ url, sha = catalog.optimal_download_url_for_this_host
107
+
108
+ if url
109
+ fetch_and_extract_crypt_shared_from_url(url, sha)
110
+ else
111
+ say_error 'Cannot find download URL for crypt_shared, for this host'
112
+ end
113
+ end
114
+
115
+ # Appends the mongoid gem entries to the Gemfile.
116
+ def add_mongoid_gem_entries
117
+ mongoid_gem_entries.each do |group, list|
118
+ append_to_file 'Gemfile' do
119
+ prefix = group ? "\ngroup :#{group} do\n" : "\n"
120
+ suffix = group ? "\nend\n" : "\n"
121
+ indent_size = group ? 2 : 0
122
+
123
+ prefix +
124
+ list.map { |entry| indent_entry(entry, indent_size) }
125
+ .join("\n\n") +
126
+ suffix
127
+ end
128
+ end
129
+ end
130
+
131
+ # Emit the mongoid.yml file to the new application folder. The
132
+ # mongoid.yml file is taken directly from the installed mongoid
133
+ # gem.
134
+ def mongoid_yml
135
+ file = Gem.find_files('rails/generators/mongoid/config/templates/mongoid.yml').first
136
+ database_name = app_name
137
+ template file, 'config/mongoid.yml', context: binding
138
+ end
139
+
140
+ # Appends a new local master key to the credentials file
141
+ def add_mongodb_local_master_key_to_credentials
142
+ return unless @okay_to_support_encryption
143
+
144
+ say_status :append, CREDENTIALS_FILE_PATH
145
+
146
+ credentials_file.change do |tmp_path|
147
+ File.open(tmp_path, 'a') do |io|
148
+ io.puts
149
+ io.puts '# Master key for MongoDB auto encryption'
150
+ # passing `96 / 2` because we need a 96-byte key, but
151
+ # SecureRandom.hex returns a hex-encoded string, which will
152
+ # be two bytes for requested byte.
153
+ io.puts "mongodb_master_key: '#{SecureRandom.hex(96 / 2)}'"
154
+ end
155
+ end
156
+ end
157
+
158
+ # If encryption is enabled, update the mongoid.yml with the necessary
159
+ # options for encryption.
160
+ def add_encryption_options_to_mongoid_yml
161
+ return unless @okay_to_support_encryption
162
+
163
+ mongoid_yml = File.join(Dir.pwd, 'config/mongoid.yml')
164
+ contents = File.read(mongoid_yml)
165
+
166
+ contents = insert_key_vault_config(contents)
167
+ contents = insert_auto_encryption_options(contents)
168
+ contents = insert_preload_models_option(contents)
169
+
170
+ say_status :update, 'config/mongoid.yml'
171
+ File.write(mongoid_yml, contents)
172
+ end
173
+
174
+ # Emit the mongoid.rb initializer. Unlike mongoid.yml, this is not
175
+ # taken from the mongoid gem, because mongoid versions prior to 9
176
+ # did not include an initializer template.
177
+ def mongoid_initializer
178
+ template '_config/initializers/mongoid.rb', 'config/initializers/mongoid.rb'
179
+ end
180
+
181
+ # Emit the bin/railsmdb script to the new app's bin folder. The
182
+ # existing bin/rails script is removed, and replaced by a link to
183
+ # bin/railsmdb.
184
+ def railsmdb
185
+ template '_bin/railsmdb', 'bin/railsmdb' do |content|
186
+ "#{shebang}\n" + content
187
+ end
188
+
189
+ chmod 'bin/railsmdb', 0o755, verbose: false
190
+
191
+ remove_file 'bin/rails', verbose: false
192
+ create_link 'bin/rails', File.expand_path('bin/railsmdb', destination_root), verbose: false
193
+ end
194
+
195
+ private
196
+
197
+ # Returns the Railsmdb project directory if run from inside a
198
+ # checkout of the railsmdb repository. Otherwise, returns nil.
199
+ #
200
+ # @return [ String | nil ] the railsmdb project directory, or nil
201
+ # if not run within a railsmdb checkout.
202
+ def railsmdb_project_directory
203
+ return @railsmdb_project_directory if defined?(@railsmdb_project_directory)
204
+
205
+ @railsmdb_project_directory ||= begin
206
+ path = @initial_path
207
+
208
+ while path != '/'
209
+ break if File.exist?(File.join(path, 'railsmdb.gemspec'))
210
+
211
+ path = File.dirname(path)
212
+ end
213
+
214
+ (path == '/') ? nil : path
215
+ end
216
+ end
217
+
218
+ # Adds indentation to the string representation of the given
219
+ # gem entry.
220
+ #
221
+ # @param [ Rails::Generators::AppBase::GemfileEntry ] entry The
222
+ # GemfileEntry instance to format.
223
+ # @param [ Integer ] indent_size the number of spaces to prepend
224
+ # to each line of the entry's string representation.
225
+ #
226
+ # @return [ String ] the string representation of the given entry
227
+ # with each line indented by the given number of spaces.
228
+ def indent_entry(entry, indent_size)
229
+ if indent_size < 1
230
+ entry.to_s
231
+ else
232
+ indent = ' ' * indent_size
233
+ entry.to_s.gsub(/^/, indent)
234
+ end
235
+ end
236
+
237
+ # The gem entries to be appended to the Gemfile, sorted by gem
238
+ # group.
239
+ #
240
+ # @return [ Hash ] a hash where the keys are the gem groups, and
241
+ # the values are lists of gem entries corresponding to those
242
+ # groups.
243
+ def mongoid_gem_entries
244
+ {
245
+ nil => [
246
+ mongoid_gem_entry,
247
+ railsmdb_gem_entry
248
+ ].tap { |list| maybe_add_encryption_gems(list) }
249
+ }
250
+ end
251
+
252
+ # The gem entry for the Mongoid gem. The version is set to whichever
253
+ # version is installed and active.
254
+ #
255
+ # @return [ Rails::Generators::AppBase::GemfileEntry ] the gem
256
+ # entry for Mongoid.
257
+ def mongoid_gem_entry
258
+ if @okay_to_support_encryption && ::Mongoid::VERSION < '9.0'
259
+ # FIXME: once Mongoid 9.0 is released, update this so that it
260
+ # uses that released version.
261
+ GemfileEntry.github \
262
+ 'mongoid',
263
+ 'mongodb/mongoid',
264
+ 'master',
265
+ 'Encryption requires an unreleased version of Mongoid'
266
+ else
267
+ GemfileEntry.version \
268
+ 'mongoid',
269
+ ::Mongoid::VERSION,
270
+ 'Use MongoDB for the database, with Mongoid as the ODM'
271
+ end
272
+ end
273
+
274
+ # The gem entry for the Railsmdb gem. If run from a railsmdb
275
+ # checkout, the gem will reference the path to that checkout.
276
+ # Otherwise, the version is set to whichever
277
+ # version is installed and active.
278
+ #
279
+ # @return [ Rails::Generators::AppBase::GemfileEntry ] the gem
280
+ # entry for Railsmdb.
281
+ def railsmdb_gem_entry
282
+ if railsmdb_project_directory.present?
283
+ GemfileEntry.path \
284
+ 'railsmdb',
285
+ railsmdb_project_directory,
286
+ 'The development version of railsmdb'
287
+ else
288
+ GemfileEntry.version \
289
+ 'railsmdb',
290
+ Railsmdb::Version::STRING,
291
+ 'The Rails CLI tool for MongoDB'
292
+ end
293
+ end
294
+
295
+ # Build the gem entry for the libmongocrypt-helper gem.
296
+ #
297
+ # @return [ Rails::Generators::AppBase::GemfileEntry ] the gem
298
+ # entry for libmongocrypt-helper gem.
299
+ def libmongocrypt_helper_gem_entry
300
+ GemfileEntry.version \
301
+ 'libmongocrypt-helper', '~> 1.8',
302
+ 'Encryption helper for MongoDB-based applications'
303
+ end
304
+
305
+ # Build the gem entry for the ffi gem.
306
+ #
307
+ # @return [ Rails::Generators::AppBase::GemfileEntry ] the gem
308
+ # entry for ffi gem.
309
+ def ffi_gem_entry
310
+ GemfileEntry.version \
311
+ 'ffi', nil,
312
+ 'Mongoid needs the ffi gem when encryption is enabled'
313
+ end
314
+
315
+ # If encryption is enabled, adds the necessary gems to the given list
316
+ # of gem entries, to prepare them to be added to the gemfile.
317
+ #
318
+ # @param [ Array<GemfileEntry> ] list The list of gemfile entries.
319
+ def maybe_add_encryption_gems(list)
320
+ return unless @okay_to_support_encryption
321
+
322
+ list.push libmongocrypt_helper_gem_entry
323
+ list.push ffi_gem_entry
324
+ end
325
+
326
+ # The location of the directory where the crypt_shared library will
327
+ # be saved to, relative to the app root.
328
+ CRYPT_SHARED_DIR = 'vendor/crypt_shared'
329
+
330
+ # Download and extract the crypt_shared library from the given url,
331
+ # and install it in vendor/crypt_shared.
332
+ #
333
+ # @param [ String ] url the url to the crypt_shared library archive
334
+ # @param [ String ] sha the sha hash for the file
335
+ def fetch_and_extract_crypt_shared_from_url(url, sha)
336
+ archive = fetch_crypt_shared_from_cache(url, sha) ||
337
+ fetch_crypt_shared_from_url(url, sha)
338
+
339
+ return unless archive
340
+
341
+ log :directory, CRYPT_SHARED_DIR
342
+ FileUtils.mkdir_p CRYPT_SHARED_DIR
343
+
344
+ extracted = extract_crypt_shared_from_file(archive)
345
+
346
+ log :error, 'No crypt_shared library could be found in the downloaded archive' unless extracted
347
+ end
348
+
349
+ # Computes the path to the cache for the file at the given URL.
350
+ #
351
+ # @param [ String ] url the url to consider
352
+ #
353
+ # @return [ String ] the path to the file's location on disk.
354
+ def cached_file_for(url)
355
+ uri = URI.parse(url)
356
+ File.join(Dir.tmpdir, File.basename(uri.path))
357
+ end
358
+
359
+ # Look in the cache location for a file downloaded from the given
360
+ # url. If it exists, make sure the SHA hash matches.
361
+ #
362
+ # @param [ String ] url the url to fetch the file from
363
+ # @param [ String ] sha the sha256 hash for the file
364
+ #
365
+ # @return [ String | nil ] the path to the file, if it exists, or nil
366
+ def fetch_crypt_shared_from_cache(url, sha)
367
+ path = cached_file_for(url)
368
+
369
+ return path if File.exist?(path) && Digest::SHA256.file(path).to_s == sha
370
+
371
+ nil
372
+ end
373
+
374
+ # Download the crypt_shared library archive from the given url and
375
+ # store it in the current directory.
376
+ #
377
+ # @param [ String ] url the url to fetch the file from
378
+ # @param [ String ] sha the sha256 hash for the file
379
+ #
380
+ # @return [ String ] the filename that the archive was saved to
381
+ def fetch_crypt_shared_from_url(url, sha)
382
+ log :fetch, url
383
+
384
+ cached_file_for(url).tap do |archive|
385
+ Railsmdb::Downloader.fetch(url, archive) { print '.' }
386
+ puts
387
+
388
+ unless File.exist?(archive) && Digest::SHA256.file(archive).to_s == sha
389
+ log :error, 'an uncorrupted crypt-shared library could not be downloaded'
390
+ return nil
391
+ end
392
+ end
393
+ end
394
+
395
+ # Extracts the crypt_shared library from the given archive file, and
396
+ # writes it to the CRYPT_SHARED_DIR.
397
+ #
398
+ # @param [ String ] archive the path to the archive file
399
+ #
400
+ # @return [ String | nil ] the name of the extracted file if successful,
401
+ # or nil if no file could be extracted.
402
+ def extract_crypt_shared_from_file(archive)
403
+ extractor = Railsmdb::Extractor.for(archive)
404
+ extractor.extract(%r{/mongo_crypt_v1\.(so|dylib|dll)}) do |name, data|
405
+ file = File.join(CRYPT_SHARED_DIR, File.basename(name))
406
+
407
+ log :create, file
408
+ File.open(file, 'w:BINARY') { |io| io.write(data) }
409
+ end
410
+ end
411
+
412
+ # Ask the user if they agree to the MongoDB Customer Agreement, which is
413
+ # required in order to download the crypt_shared library.
414
+ #
415
+ # @return [ true | false ] whether the user agrees or not
416
+ def okay_with_legal_shenanigans?
417
+ # primarily so we can interact with this programmatically in tests...
418
+ $stdout.sync = true
419
+
420
+ say "You've requested to begin a new Rails app with MongoDB encryption."
421
+ say
422
+
423
+ say "Using MongoDB's encryption features requires MongoDB Enterprise Edition,"
424
+ say 'which is for MongoDB customers only. Are you a MongoDB Atlas customer, or'
425
+ say 'are you currently a MongoDB Enterprise Advanced subscriber?'
426
+ say
427
+
428
+ case ask('"[yes], I am a MongoDB customer", or "[no], I am not" =>', limited_to: %w[ yes no ])
429
+ when 'yes'
430
+ say
431
+ say '* Use of these features constitutes acceptance of the Customer Agreement.'
432
+ say
433
+
434
+ true
435
+ when 'no'
436
+ say
437
+ say '* Encryption will not be enabled for your application.'
438
+ say
439
+
440
+ false
441
+ end
442
+ end
443
+
444
+ # Attempts to insert the key-vault configuration into the given
445
+ # string, which must be the contents of the generated mongoid.yml file.
446
+ #
447
+ # @param [String] contents the contents of mongoid.yml
448
+ #
449
+ # @return [ String ] the updated contents
450
+ def insert_key_vault_config(contents)
451
+ position = (contents =~ /^\s*# Defines the default client/)
452
+ unless position
453
+ say_error 'Default mongoid.yml format has changed; cannot update it with key-vault settings'
454
+ return contents
455
+ end
456
+
457
+ indent_size = contents[position..][/^\s*/].length
458
+ contents[position, 0] = KEY_VAULT_CONFIG.indent(indent_size).gsub(/%app%/, app_name)
459
+
460
+ contents
461
+ end
462
+
463
+ # Returns the path to the downloaded crypt-shared library.
464
+ #
465
+ # @return [ String ] path to the crypt_shared library.
466
+ def crypt_shared_path
467
+ ext = if OS.windows? || OS::Underlying.windows?
468
+ 'dll'
469
+ elsif OS.mac?
470
+ 'dylib'
471
+ else
472
+ 'so'
473
+ end
474
+
475
+ # excuse the final '# >'' at the end of the next line...this string
476
+ # confuses vscode's syntax highlighter, and that final comment is
477
+ # to shake it back to its senses...
478
+ %{"<%= Rails.root.join('vendor', 'crypt_shared', 'mongo_crypt_v1.#{ext}') %>"} # >
479
+ end
480
+
481
+ # Attempts to insert the auto-encryption configuration into the given
482
+ # string, which must be the contents of the generated mongoid.yml file.
483
+ #
484
+ # @param [String] contents the contents of mongoid.yml
485
+ #
486
+ # @return [ String ] the updated contents
487
+ def insert_auto_encryption_options(contents)
488
+ position = (contents =~ /\sdefault:.*?\s+options:\n/m)
489
+
490
+ unless position
491
+ say_error 'Default mongoid.yml format has changed; cannot update it with auto-encryption settings'
492
+ return contents
493
+ end
494
+
495
+ position += Regexp.last_match(0).length
496
+
497
+ indent_size = contents[position..][/^\s*/].length
498
+ contents[position, 0] = AUTO_ENCRYPTION_CONFIG
499
+ .indent(indent_size)
500
+ .gsub(/%crypt-shared-path%/, crypt_shared_path)
501
+
502
+ contents
503
+ end
504
+
505
+ # Attempts to enable the preload_models option in the given
506
+ # string, which must be the contents of the generated mongoid.yml file.
507
+ #
508
+ # @param [String] contents the contents of mongoid.yml
509
+ #
510
+ # @return [ String ] the updated contents
511
+ def insert_preload_models_option(contents)
512
+ position = (contents =~ /^\s+# preload_models: .*?\n/)
513
+
514
+ unless position
515
+ say_error 'Default mongoid.yml format has changed; cannot enable preload_models'
516
+ return contents
517
+ end
518
+
519
+ length = Regexp.last_match(0).length
520
+
521
+ indent_size = contents[position..][/^\s*/].length
522
+ contents[position, length] = PRELOAD_MODELS_OPTION.indent(indent_size)
523
+
524
+ contents
525
+ end
526
+
527
+ CREDENTIALS_FILE_PATH = 'config/credentials.yml.enc'
528
+
529
+ # Return the encrypted credentials file.
530
+ #
531
+ # @return [ ActiveSupport::EncryptedConfiguration ] the encrypted
532
+ # credentials file.
533
+ def credentials_file
534
+ ActiveSupport::EncryptedConfiguration.new(
535
+ config_path: CREDENTIALS_FILE_PATH,
536
+ key_path: 'config/master.key',
537
+ env_key: 'RAILS_MASTER_KEY',
538
+ raise_if_missing_key: true
539
+ )
540
+ end
541
+ end
542
+ end
543
+ end
544
+ end
545
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators/base'
4
+ require 'railsmdb/generators/setup/concerns/setuppable'
5
+
6
+ module Railsmdb
7
+ module Generators
8
+ # The implementation of the setup generator for Railsmdb, for
9
+ # configuring railsmdb to work in an existing Rails application.
10
+ class SetupGenerator < Rails::Generators::Base
11
+ include Railsmdb::Generators::Setup::Concerns::Setuppable
12
+
13
+ NEED_RAILS_APP_WARNING = <<~WARNING
14
+ The `railsmdb setup` command must be run from the root of an
15
+ existing Rails application. It will add railsmdb to the project,
16
+ prepare the application to use Mongoid for data access, and
17
+ replace the `bin/rails` script with `bin/railsmdb`.
18
+
19
+ Please try this command again from the root of an existing Rails
20
+ application.
21
+ WARNING
22
+
23
+ ALREADY_HAS_RAILSMDB = <<~WARNING
24
+ This Rails application is already configured to use railsmdb.
25
+ WARNING
26
+
27
+ WARN_ABOUT_UNDO = <<~WARNING
28
+ It is strongly recommended to invoke this in a separate branch,
29
+ where you can safely test the changes made by the script and
30
+ roll them back if they cause problems.
31
+ WARNING
32
+
33
+ add_shebang_option!
34
+
35
+ # Make sure the current directory is an appropriate place to
36
+ # run this generator. It must:
37
+ # - be the root directory of a Rails project
38
+ # - not already have been set up with railsmdb
39
+ #
40
+ # Additionally, this will encourage the user to run this generator
41
+ # in a branch, in order to safely see what it does to their app.
42
+ def ensure_proper_invocation
43
+ ensure_rails_app!
44
+ ensure_railsmdb_not_already_present!
45
+ warn_about_undo!
46
+ end
47
+
48
+ public_task :save_initial_path
49
+ public_task :confirm_legal_shenanigans
50
+ public_task :fetch_crypt_shared
51
+ public_task :add_mongoid_gem_entries
52
+ public_task :mongoid_yml
53
+ public_task :add_mongodb_local_master_key_to_credentials
54
+ public_task :add_encryption_options_to_mongoid_yml
55
+ public_task :mongoid_initializer
56
+ public_task :railsmdb
57
+
58
+ # Make sure the newly required gems are installed automatically.
59
+ def run_bundle_install
60
+ say_status :run, 'bundle install'
61
+ system 'bundle install'
62
+ end
63
+
64
+ private
65
+
66
+ # Infers the name of the app from the application's config/application.rb
67
+ # file.
68
+ #
69
+ # @return [ String ] the name of the application
70
+ def app_name
71
+ @app_name ||= File.read('config/application.rb').match(/module (\w+)/)[1].underscore
72
+ end
73
+
74
+ # Warns and exits if the current directory is not the root of a
75
+ # Rails project.
76
+ def ensure_rails_app!
77
+ return if rails_app?
78
+
79
+ warn NEED_RAILS_APP_WARNING
80
+ exit 1
81
+ end
82
+
83
+ # Warns and exits if railsmdb is already present in the current
84
+ # Rails project.
85
+ def ensure_railsmdb_not_already_present!
86
+ return unless railsmdb?
87
+
88
+ warn ALREADY_HAS_RAILSMDB
89
+ exit 1
90
+ end
91
+
92
+ # Encourages the user to run this in a branch so that the changes
93
+ # may be easily rolled back.
94
+ #
95
+ # If the user chooses not to proceed, this method will exit the
96
+ # program.
97
+ def warn_about_undo!
98
+ warn WARN_ABOUT_UNDO
99
+ say
100
+
101
+ exit 0 if ask('Do you wish to proceed in the current branch?', limited_to: %w[ yes no ]) == 'no'
102
+ end
103
+
104
+ # Returns true if the current directory appears to be a Rails app.
105
+ #
106
+ # @return [ true | false ] if the current directory is a Rails app
107
+ # or not.
108
+ def rails_app?
109
+ File.exist?('bin/rails') &&
110
+ File.exist?('app') &&
111
+ File.exist?('config')
112
+ end
113
+
114
+ # Returns true if railsmdb appears to already be present in the
115
+ # current Rails app.
116
+ #
117
+ # @return [ true | false ] if railsmdb is already present.
118
+ def railsmdb?
119
+ File.exist?('bin/railsmdb')
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,3 @@
1
+ # Welcome
2
+
3
+ Hey, you're running Rails with Mongoid!
@@ -0,0 +1,3 @@
1
+ APP_PATH = File.expand_path("../config/application", __dir__)
2
+ require_relative "../config/boot"
3
+ require "railsmdb/commands"