sorbet-rails 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +77 -0
  3. data/Gemfile +6 -0
  4. data/Gemfile.lock +176 -0
  5. data/README.md +68 -45
  6. data/Rakefile +6 -0
  7. data/SECURITY.md +5 -0
  8. data/lib/sorbet-rails/model_rbi_formatter.rb +1 -1
  9. data/lib/sorbet-rails/tasks/rails_rbi.rake +60 -0
  10. data/rbis/activerecord.rbi +133 -0
  11. data/sorbet-rails.gemspec +31 -0
  12. data/spec/model_rbi_formatter_spec.rb +54 -0
  13. data/spec/rails_helper.rb +14 -0
  14. data/spec/routes_rbi_formatter_spec.rb +21 -0
  15. data/spec/spec_helper.rb +8 -0
  16. data/spec/support/rails_app/.ruby-version +1 -0
  17. data/spec/support/rails_app/Gemfile +45 -0
  18. data/spec/support/rails_app/Gemfile.lock +139 -0
  19. data/spec/support/rails_app/README.md +24 -0
  20. data/spec/support/rails_app/Rakefile +6 -0
  21. data/spec/support/rails_app/app/channels/application_cable/channel.rb +4 -0
  22. data/spec/support/rails_app/app/channels/application_cable/connection.rb +4 -0
  23. data/spec/support/rails_app/app/controllers/application_controller.rb +2 -0
  24. data/spec/support/rails_app/app/controllers/concerns/.keep +0 -0
  25. data/spec/support/rails_app/app/controllers/test_controller.rb +5 -0
  26. data/spec/support/rails_app/app/jobs/application_job.rb +2 -0
  27. data/spec/support/rails_app/app/mailers/application_mailer.rb +4 -0
  28. data/spec/support/rails_app/app/models/application_record.rb +3 -0
  29. data/spec/support/rails_app/app/models/concerns/.keep +0 -0
  30. data/spec/support/rails_app/app/models/spell_book.rb +6 -0
  31. data/spec/support/rails_app/app/models/wand.rb +15 -0
  32. data/spec/support/rails_app/app/models/wizard.rb +14 -0
  33. data/spec/support/rails_app/app/views/layouts/mailer.html.erb +13 -0
  34. data/spec/support/rails_app/app/views/layouts/mailer.text.erb +1 -0
  35. data/spec/support/rails_app/bin/bundle +3 -0
  36. data/spec/support/rails_app/bin/rails +9 -0
  37. data/spec/support/rails_app/bin/rake +9 -0
  38. data/spec/support/rails_app/bin/setup +33 -0
  39. data/spec/support/rails_app/bin/spring +17 -0
  40. data/spec/support/rails_app/bin/update +28 -0
  41. data/spec/support/rails_app/config.ru +5 -0
  42. data/spec/support/rails_app/config/application.rb +35 -0
  43. data/spec/support/rails_app/config/boot.rb +3 -0
  44. data/spec/support/rails_app/config/cable.yml +10 -0
  45. data/spec/support/rails_app/config/credentials.yml.enc +1 -0
  46. data/spec/support/rails_app/config/database.yml +25 -0
  47. data/spec/support/rails_app/config/environment.rb +5 -0
  48. data/spec/support/rails_app/config/environments/development.rb +54 -0
  49. data/spec/support/rails_app/config/environments/production.rb +85 -0
  50. data/spec/support/rails_app/config/environments/test.rb +46 -0
  51. data/spec/support/rails_app/config/initializers/application_controller_renderer.rb +8 -0
  52. data/spec/support/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  53. data/spec/support/rails_app/config/initializers/cors.rb +16 -0
  54. data/spec/support/rails_app/config/initializers/filter_parameter_logging.rb +4 -0
  55. data/spec/support/rails_app/config/initializers/inflections.rb +16 -0
  56. data/spec/support/rails_app/config/initializers/mime_types.rb +4 -0
  57. data/spec/support/rails_app/config/initializers/wrap_parameters.rb +14 -0
  58. data/spec/support/rails_app/config/locales/en.yml +33 -0
  59. data/spec/support/rails_app/config/puma.rb +34 -0
  60. data/spec/support/rails_app/config/routes.rb +5 -0
  61. data/spec/support/rails_app/config/spring.rb +6 -0
  62. data/spec/support/rails_app/config/storage.yml +34 -0
  63. data/spec/support/rails_app/db/migrate/20190620001234_create_wizards.rb +12 -0
  64. data/spec/support/rails_app/db/migrate/20190620003037_create_wands.rb +11 -0
  65. data/spec/support/rails_app/db/migrate/20190620003739_create_spell_books.rb +8 -0
  66. data/spec/support/rails_app/db/schema.rb +39 -0
  67. data/spec/support/rails_app/db/seeds.rb +7 -0
  68. data/spec/support/rails_app/lib/tasks/.keep +0 -0
  69. data/spec/support/rails_app/log/.keep +0 -0
  70. data/spec/support/rails_app/public/robots.txt +1 -0
  71. data/spec/support/rails_app/storage/.keep +0 -0
  72. data/spec/support/rails_app/test/controllers/.keep +0 -0
  73. data/spec/support/rails_app/test/fixtures/.keep +0 -0
  74. data/spec/support/rails_app/test/fixtures/files/.keep +0 -0
  75. data/spec/support/rails_app/test/integration/.keep +0 -0
  76. data/spec/support/rails_app/test/mailers/.keep +0 -0
  77. data/spec/support/rails_app/test/models/.keep +0 -0
  78. data/spec/support/rails_app/test/test_helper.rb +10 -0
  79. data/spec/support/rails_app/tmp/.keep +0 -0
  80. data/spec/support/rails_app/vendor/.keep +0 -0
  81. data/spec/test_data/expected_routes.rbi +43 -0
  82. data/spec/test_data/expected_wizard_no_book.rbi +0 -0
  83. data/spec/test_data/models/expected_wand.rbi +174 -0
  84. data/spec/test_data/models/expected_wizard.rbi +189 -0
  85. data/spec/test_data/models/expected_wizard_wo_spellbook.rbi +189 -0
  86. metadata +338 -3
@@ -286,7 +286,7 @@ class ModelRbiFormatter
286
286
 
287
287
  def type_for_column_def(column_def)
288
288
  strict_type = RUBY_TO_SORBET_TYPE_MAPPING[column_def.type] || 'T.untyped'
289
- if column_def.array?
289
+ if column_def.respond_to?(:array?) && column_def.array?
290
290
  strict_type = "T::Array[#{strict_type}]"
291
291
  end
292
292
  column_def.null ? "T.nilable(#{strict_type})" : strict_type
@@ -0,0 +1,60 @@
1
+ require("sorbet-rails/model_rbi_formatter")
2
+ require("sorbet-rails/routes_rbi_formatter")
3
+
4
+ namespace :rails_rbi do
5
+
6
+ desc "Generate rbi for rails routes"
7
+ task :routes, [:root_dir] => :environment do |t, args|
8
+ all_routes = Rails.application.routes.routes
9
+ require "action_dispatch/routing/inspector"
10
+ inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes)
11
+ root_dir = args[:root_dir] || Rails.root
12
+ file_path = Rails.root.join("sorbet", "rails-rbi", "routes.rbi")
13
+ FileUtils.mkdir_p(File.dirname(file_path))
14
+ File.write(file_path, inspector.format(RoutesRbiFormatter.new))
15
+ end
16
+
17
+ desc "Generate rbi for rails models. Pass models name to regenrate rbi for only the given models."
18
+ task models: :environment do |t, args|
19
+ # need to eager load to see all models
20
+ Rails.application.eager_load!
21
+
22
+ blacklisted_models = []
23
+ blacklisted_models << ActiveRecord::SchemaMigration if defined?(ActiveRecord::SchemaMigration)
24
+
25
+ all_models = ActiveRecord::Base.descendants - blacklisted_models
26
+
27
+ models_to_generate = args.extras.size > 0 ?
28
+ args.extras.map { |m| Object.const_get(m) } :
29
+ all_models
30
+
31
+ generated_rbis = generate_rbis_for_models(models_to_generate, all_models)
32
+ generated_rbis.each do |model_name, contents|
33
+ file_path = Rails.root.join("sorbet", "rails-rbi", "models", "#{model_name.underscore}.rbi")
34
+ FileUtils.mkdir_p(File.dirname(file_path))
35
+ File.write(file_path, contents)
36
+ end
37
+ end
38
+
39
+ def generate_rbis_for_models(model_classes, available_classes)
40
+ available_class_names = Set.new(available_classes.map { |c| c.name })
41
+ formatted = model_classes.map do |model_class|
42
+ begin
43
+ formatter = ModelRbiFormatter.new(model_class, available_class_names)
44
+ [model_class.name, formatter.generate_rbi]
45
+ rescue StandardError => ex
46
+ puts "---"
47
+ puts "Error when handling model #{model_class.name}: #{ex}"
48
+ nil
49
+ end
50
+ end
51
+ Hash[formatted.compact] # remove models with errors
52
+ end
53
+
54
+ def blacklisted_models
55
+ blacklisted_models = []
56
+ blacklisted_models << ActiveRecord::SchemaMigration if defined?(ActiveRecord::SchemaMigration)
57
+ blacklisted_models << ApplicationRecord if defined?(ApplicationRecord)
58
+ blacklisted_models
59
+ end
60
+ end
@@ -0,0 +1,133 @@
1
+ # typed: strong
2
+ class ActiveRecord::Relation
3
+ extend T::Sig
4
+ extend T::Generic
5
+ include Enumerable
6
+ include ActiveRecord::Querying::Typed
7
+
8
+ Elem = type_member
9
+
10
+ sig { params(block: T.proc.params(e: Elem).void).void }
11
+ def each(&block); end
12
+
13
+ sig { params(level: T.nilable(Integer)).returns(T::Array[Elem]) }
14
+ def flatten(level); end
15
+
16
+ sig { returns(T::Array[Elem]) }
17
+ def to_a; end
18
+
19
+ # This is part of both FinderMethods & Enumerable module.
20
+ # We need to define here to override the typedef in Enumerable module.
21
+ # TODO normally this method could return Elem or Array[Elem]
22
+ # however, we think it's better to limit the interface to returning Elem only
23
+ # Use `find_n` as a replacement if you want to get an array from `find`
24
+ sig { params(args: T.untyped).returns(Elem).soft }
25
+ def find(*args); end
26
+ end
27
+
28
+ module ActiveRecord::Querying::Typed
29
+ extend T::Sig
30
+ extend T::Generic
31
+ Elem = type_member
32
+
33
+ # -- Booleans
34
+ sig { params(args: T.untyped, block: T.nilable(T.proc.void)).returns(T::Boolean) }
35
+ def exists?(*args, &block); end
36
+ sig { params(args: T.untyped, block: T.nilable(T.proc.void)).returns(T::Boolean) }
37
+ def any?(*args, &block); end
38
+ sig { params(args: T.untyped, block: T.nilable(T.proc.void)).returns(T::Boolean) }
39
+ def many?(*args, &block); end
40
+ sig { params(args: T.untyped, block: T.nilable(T.proc.void)).returns(T::Boolean) }
41
+ def none?(*args, &block); end
42
+ sig { params(args: T.untyped, block: T.nilable(T.proc.void)).returns(T::Boolean) }
43
+ def one?(*args, &block); end
44
+
45
+ # -- finder methods
46
+ sig { returns(Elem) }
47
+ def first!; end
48
+ sig { returns(Elem) }
49
+ def second!; end
50
+ sig { returns(T.nilable(Elem)) }
51
+ def second; end
52
+ sig { returns(Elem) }
53
+ def third!; end
54
+ sig { returns(T.nilable(Elem)) }
55
+ def third; end
56
+ sig { returns(Elem) }
57
+ def fourth!; end
58
+ sig { returns(T.nilable(Elem)) }
59
+ def fourth; end
60
+ sig { returns(Elem) }
61
+ def fifth!; end
62
+ sig { returns(T.nilable(Elem)) }
63
+ def fifth; end
64
+
65
+ sig { returns(Elem) }
66
+ def last!; end
67
+ sig { returns(Elem) }
68
+ def third_to_last!; end
69
+ sig { returns(T.nilable(Elem)) }
70
+ def third_to_last; end
71
+ sig { returns(Elem) }
72
+ def second_to_last!; end
73
+ sig { returns(T.nilable(Elem)) }
74
+ def second_to_last; end
75
+
76
+ # These methods would return 1 single element when there is no limit, and return an
77
+ # array of element when there is a limit passed in.
78
+ # The more common use case is to find 1 object. We use that as the default sig
79
+ # and add custom first_n, last_n method for the cases that return an array
80
+ sig { params(limit: T.nilable(Integer)).returns(T.nilable(Elem)).soft }
81
+ def first(limit = nil); end
82
+ sig { params(limit: T.nilable(Integer)).returns(T.nilable(Elem)).soft }
83
+ def last(limit = nil); end
84
+ # Similar to first & last, normally this method could return Elem or Array[Elem]
85
+ # But we enforce that `find` return Elem by default and provide find_n as an alternative
86
+ sig { params(args: T.any(Integer, String)).returns(Elem).soft }
87
+ def find(*args); end
88
+ # These are custom finder methods to provide strong type for secondary use case of first, last & find
89
+ # See sorbet_rails/custom_finder_methods
90
+ sig { params(limit: Integer).returns(T::Array[Elem]) }
91
+ def first_n(limit); end
92
+ sig { params(limit: Integer).returns(T::Array[Elem]) }
93
+ def last_n(limit); end
94
+ sig { params(args: T::Array[T.any(Integer, String)]).returns(T::Array[Elem]) }
95
+ def find_n(*args); end
96
+
97
+ sig { params(args: T.untyped, block: T.nilable(T.proc.void)).returns(T.nilable(Elem)) }
98
+ def find_by(*args, &block); end
99
+
100
+ sig { params(args: T.untyped, block: T.nilable(T.proc.void)).returns(Elem) }
101
+ def find_by!(*args, &block); end
102
+
103
+ sig { params(id: Integer).returns(T.nilable(Elem)) }
104
+ def find_by_id(id); end
105
+
106
+ sig { params(id: Integer).returns(Elem) }
107
+ def find_by_id!(id); end
108
+
109
+ # -- Calculation
110
+ # There can be a complication of using count with .group
111
+ # https://api.rubyonrails.org/classes/ActiveRecord/Calculations.html#method-i-count
112
+ # sig { params(args: T.untyped, block: T.nilable(T.proc.void)).returns(Integer) }
113
+ # def count(*args, &block); end
114
+ end
115
+
116
+ module ActiveRecord::QueryMethods
117
+ # TODO: where.not is a special case that we replace it with a `where_not` method
118
+ # `where` when not given parameters will return a `ActiveRecord::QueryMethods::WhereChain`
119
+ # instance that has a method `not` on it
120
+ sig { params(opts: T.untyped, rest: T.untyped).returns(T.self_type) }
121
+ def not(opts = nil, *rest); end
122
+ end
123
+
124
+ class ActiveRecord::Base
125
+ extend ActiveRecord::Querying::Typed
126
+ extend T::Generic
127
+ Elem = type_template(fixed: ActiveRecord::Base)
128
+ end
129
+
130
+ class ApplicationRecord
131
+ extend T::Generic
132
+ Elem = type_template(fixed: ApplicationRecord)
133
+ end
@@ -0,0 +1,31 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{sorbet-rails}
3
+ s.version = "0.1.3"
4
+ s.date = %q{2019-04-18}
5
+ s.summary = %q{Set of tools to make Sorbet work with Rails seamlessly.}
6
+ s.authors = ["Chan Zuckerberg Initiative"]
7
+ s.email = "opensource@chanzuckerberg.com"
8
+ s.homepage = "https://github.com/chanzuckerberg/sorbet-rails"
9
+ s.license = 'MIT'
10
+ s.require_paths = ["lib"]
11
+ s.files = Dir.chdir(File.expand_path('..', __FILE__)) do
12
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
13
+ end
14
+ s.test_files = Dir.chdir(File.expand_path('..', __FILE__)) do
15
+ `git ls-files -z`.split("\x0").select { |f| f.match(%r{^(test|spec|features)/}) }
16
+ end
17
+
18
+ # Development
19
+ s.add_development_dependency 'rspec', '~> 3.8', '>= 3.8'
20
+ # Debugging
21
+ s.add_development_dependency 'awesome_print', '~>1.8.0', '>=1.8.0'
22
+ s.add_development_dependency 'byebug', '~>11.0.1', '>=11.0.1'
23
+
24
+ # for testing a gem with a rails app (controller specs)
25
+ s.add_development_dependency 'bundler', '~>1.3.0', '>=1.3.0'
26
+ s.add_development_dependency 'rails', '~>5.2.3', '>=5.2.3'
27
+ s.add_development_dependency 'rspec-rails', '~>3.8.2', '>=3.8.2'
28
+ s.add_development_dependency 'sqlite3', '~>1.4.1', '>=1.4.1'
29
+ s.add_development_dependency 'puma', '~>3.12.1', '>=3.12.1'
30
+ s.add_development_dependency 'listen', '~>3.1.5', '>=3.1.5'
31
+ end
@@ -0,0 +1,54 @@
1
+ require 'rails_helper'
2
+ require 'sorbet-rails/model_rbi_formatter'
3
+
4
+ RSpec.describe ModelRbiFormatter do
5
+
6
+ def expect_match_file(content, file_path, save_to_file: false)
7
+ # TODO make this a custom matcher that can be shared in multiple files
8
+ if save_to_file
9
+ FileUtils.mkdir_p(File.dirname(file_path))
10
+ File.write(file_path, content)
11
+ end
12
+ expected_value = File.read(file_path)
13
+ expect(content).to eql(expected_value)
14
+ end
15
+
16
+ it 'loads all the models in the app' do
17
+ Rails.application.eager_load!
18
+ all_models = ActiveRecord::Base.descendants.sort_by(&:name)
19
+ expect(all_models).to eql([
20
+ ActiveRecord::SchemaMigration,
21
+ ApplicationRecord,
22
+ SpellBook,
23
+ Wand,
24
+ Wizard,
25
+ ])
26
+ end
27
+
28
+ it 'generates correct rbi file for Wizard' do
29
+ formatter = ModelRbiFormatter.new(Wizard, ['Wizard', 'Wand', 'SpellBook'])
30
+ expect_match_file(
31
+ formatter.generate_rbi,
32
+ 'spec/test_data/models/expected_wizard.rbi',
33
+ )
34
+ end
35
+
36
+ it 'generates strict belongs_to and do not generate overridden methods' do
37
+ formatter = ModelRbiFormatter.new(Wand, ['Wizard', 'Wand', 'SpellBook'])
38
+ expect_match_file(
39
+ formatter.generate_rbi,
40
+ 'spec/test_data/models/expected_wand.rbi',
41
+ save_to_file: true,
42
+ )
43
+ end
44
+
45
+ context 'there is a hidden model' do
46
+ it 'fallbacks to use ActiveRecord::Relation' do
47
+ formatter = ModelRbiFormatter.new(Wizard, ['Wizard', 'Wand'])
48
+ expect_match_file(
49
+ formatter.generate_rbi,
50
+ 'spec/test_data/models/expected_wizard_wo_spellbook.rbi',
51
+ )
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ ENV['RAILS_ENV'] = 'test'
4
+
5
+ require 'rails/all'
6
+ require 'rake'
7
+ require 'rspec/rails'
8
+ require 'sorbet-rails/railtie'
9
+
10
+ load 'sorbet-rails/tasks/rails_rbi.rake'
11
+
12
+ require 'support/rails_app/config/environment'
13
+
14
+ ActiveRecord::Migration.maintain_test_schema!
@@ -0,0 +1,21 @@
1
+ require 'rails_helper'
2
+ require 'sorbet-rails/routes_rbi_formatter'
3
+
4
+ RSpec.describe RoutesRbiFormatter do
5
+ it 'sets up Rails Application' do
6
+ expect(Rails.application.routes).to_not be_empty
7
+ end
8
+
9
+ it 'generates _path and _url of a named route' do
10
+ all_routes = Rails.application.routes.routes
11
+ require "action_dispatch/routing/inspector"
12
+ inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes)
13
+ formatted = inspector.format(RoutesRbiFormatter.new)
14
+ # Write the res to a file
15
+ # file_path = '/tmp/expected_routes.rbi'
16
+ # FileUtils.mkdir_p(File.dirname(file_path))
17
+ # File.write(file_path, res)
18
+ expected_value = File.read('spec/test_data/expected_routes.rbi')
19
+ expect(formatted).to eql(expected_value)
20
+ end
21
+ end
@@ -0,0 +1,8 @@
1
+ require "byebug"
2
+ require "simplecov"
3
+
4
+ SimpleCov.start
5
+
6
+ RSpec.configure do |config|
7
+ config.expect_with(:rspec) { |c| c.syntax = :expect }
8
+ end
@@ -0,0 +1,45 @@
1
+ source 'https://rubygems.org'
2
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
+
4
+ ruby '2.4.3'
5
+
6
+ # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
7
+ gem 'rails', '~> 5.2.3'
8
+ # Use sqlite3 as the database for Active Record
9
+ gem 'sqlite3'
10
+ # Use Puma as the app server
11
+ gem 'puma', '~> 3.11'
12
+ # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
13
+ # gem 'jbuilder', '~> 2.5'
14
+ # Use Redis adapter to run Action Cable in production
15
+ # gem 'redis', '~> 4.0'
16
+ # Use ActiveModel has_secure_password
17
+ # gem 'bcrypt', '~> 3.1.7'
18
+
19
+ # Use ActiveStorage variant
20
+ # gem 'mini_magick', '~> 4.8'
21
+
22
+ # Use Capistrano for deployment
23
+ # gem 'capistrano-rails', group: :development
24
+
25
+ # Reduces boot times through caching; required in config/boot.rb
26
+ # gem 'bootsnap', '>= 1.1.0', require: false
27
+
28
+ # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
29
+ # gem 'rack-cors'
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
+ gem 'listen', '>= 3.0.5', '< 3.2'
38
+ # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
39
+ # gem 'spring'
40
+ # gem 'spring-watcher-listen', '~> 2.0.0'
41
+ end
42
+
43
+
44
+ # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
45
+ gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
@@ -0,0 +1,139 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ actioncable (5.2.3)
5
+ actionpack (= 5.2.3)
6
+ nio4r (~> 2.0)
7
+ websocket-driver (>= 0.6.1)
8
+ actionmailer (5.2.3)
9
+ actionpack (= 5.2.3)
10
+ actionview (= 5.2.3)
11
+ activejob (= 5.2.3)
12
+ mail (~> 2.5, >= 2.5.4)
13
+ rails-dom-testing (~> 2.0)
14
+ actionpack (5.2.3)
15
+ actionview (= 5.2.3)
16
+ activesupport (= 5.2.3)
17
+ rack (~> 2.0)
18
+ rack-test (>= 0.6.3)
19
+ rails-dom-testing (~> 2.0)
20
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
21
+ actionview (5.2.3)
22
+ activesupport (= 5.2.3)
23
+ builder (~> 3.1)
24
+ erubi (~> 1.4)
25
+ rails-dom-testing (~> 2.0)
26
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
27
+ activejob (5.2.3)
28
+ activesupport (= 5.2.3)
29
+ globalid (>= 0.3.6)
30
+ activemodel (5.2.3)
31
+ activesupport (= 5.2.3)
32
+ activerecord (5.2.3)
33
+ activemodel (= 5.2.3)
34
+ activesupport (= 5.2.3)
35
+ arel (>= 9.0)
36
+ activestorage (5.2.3)
37
+ actionpack (= 5.2.3)
38
+ activerecord (= 5.2.3)
39
+ marcel (~> 0.3.1)
40
+ activesupport (5.2.3)
41
+ concurrent-ruby (~> 1.0, >= 1.0.2)
42
+ i18n (>= 0.7, < 2)
43
+ minitest (~> 5.1)
44
+ tzinfo (~> 1.1)
45
+ arel (9.0.0)
46
+ builder (3.2.3)
47
+ byebug (11.0.1)
48
+ concurrent-ruby (1.1.5)
49
+ crass (1.0.4)
50
+ erubi (1.8.0)
51
+ ffi (1.11.1)
52
+ globalid (0.4.2)
53
+ activesupport (>= 4.2.0)
54
+ i18n (1.6.0)
55
+ concurrent-ruby (~> 1.0)
56
+ listen (3.1.5)
57
+ rb-fsevent (~> 0.9, >= 0.9.4)
58
+ rb-inotify (~> 0.9, >= 0.9.7)
59
+ ruby_dep (~> 1.2)
60
+ loofah (2.2.3)
61
+ crass (~> 1.0.2)
62
+ nokogiri (>= 1.5.9)
63
+ mail (2.7.1)
64
+ mini_mime (>= 0.1.1)
65
+ marcel (0.3.3)
66
+ mimemagic (~> 0.3.2)
67
+ method_source (0.9.2)
68
+ mimemagic (0.3.3)
69
+ mini_mime (1.0.1)
70
+ mini_portile2 (2.4.0)
71
+ minitest (5.11.3)
72
+ nio4r (2.3.1)
73
+ nokogiri (1.10.3)
74
+ mini_portile2 (~> 2.4.0)
75
+ puma (3.12.1)
76
+ rack (2.0.7)
77
+ rack-test (1.1.0)
78
+ rack (>= 1.0, < 3)
79
+ rails (5.2.3)
80
+ actioncable (= 5.2.3)
81
+ actionmailer (= 5.2.3)
82
+ actionpack (= 5.2.3)
83
+ actionview (= 5.2.3)
84
+ activejob (= 5.2.3)
85
+ activemodel (= 5.2.3)
86
+ activerecord (= 5.2.3)
87
+ activestorage (= 5.2.3)
88
+ activesupport (= 5.2.3)
89
+ bundler (>= 1.3.0)
90
+ railties (= 5.2.3)
91
+ sprockets-rails (>= 2.0.0)
92
+ rails-dom-testing (2.0.3)
93
+ activesupport (>= 4.2.0)
94
+ nokogiri (>= 1.6)
95
+ rails-html-sanitizer (1.0.4)
96
+ loofah (~> 2.2, >= 2.2.2)
97
+ railties (5.2.3)
98
+ actionpack (= 5.2.3)
99
+ activesupport (= 5.2.3)
100
+ method_source
101
+ rake (>= 0.8.7)
102
+ thor (>= 0.19.0, < 2.0)
103
+ rake (12.3.2)
104
+ rb-fsevent (0.10.3)
105
+ rb-inotify (0.10.0)
106
+ ffi (~> 1.0)
107
+ ruby_dep (1.5.0)
108
+ sprockets (3.7.2)
109
+ concurrent-ruby (~> 1.0)
110
+ rack (> 1, < 3)
111
+ sprockets-rails (3.2.1)
112
+ actionpack (>= 4.0)
113
+ activesupport (>= 4.0)
114
+ sprockets (>= 3.0.0)
115
+ sqlite3 (1.4.1)
116
+ thor (0.20.3)
117
+ thread_safe (0.3.6)
118
+ tzinfo (1.2.5)
119
+ thread_safe (~> 0.1)
120
+ websocket-driver (0.7.1)
121
+ websocket-extensions (>= 0.1.0)
122
+ websocket-extensions (0.1.4)
123
+
124
+ PLATFORMS
125
+ ruby
126
+
127
+ DEPENDENCIES
128
+ byebug
129
+ listen (>= 3.0.5, < 3.2)
130
+ puma (~> 3.11)
131
+ rails (~> 5.2.3)
132
+ sqlite3
133
+ tzinfo-data
134
+
135
+ RUBY VERSION
136
+ ruby 2.4.3p205
137
+
138
+ BUNDLED WITH
139
+ 2.0.1