active_record_doctor 1.4.1 → 1.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +140 -10
  3. data/lib/active_record_doctor.rb +15 -1
  4. data/lib/active_record_doctor/printers/io_printer.rb +63 -7
  5. data/lib/active_record_doctor/railtie.rb +1 -1
  6. data/lib/active_record_doctor/tasks.rb +6 -0
  7. data/lib/active_record_doctor/tasks/base.rb +86 -0
  8. data/lib/active_record_doctor/tasks/extraneous_indexes.rb +5 -26
  9. data/lib/active_record_doctor/tasks/incorrect_boolean_presence_validation.rb +37 -0
  10. data/lib/active_record_doctor/tasks/missing_foreign_keys.rb +7 -28
  11. data/lib/active_record_doctor/tasks/missing_non_null_constraint.rb +56 -0
  12. data/lib/active_record_doctor/tasks/missing_presence_validation.rb +75 -0
  13. data/lib/active_record_doctor/tasks/missing_unique_indexes.rb +57 -0
  14. data/lib/active_record_doctor/tasks/undefined_table_references.rb +22 -21
  15. data/lib/active_record_doctor/tasks/unindexed_deleted_at.rb +23 -0
  16. data/lib/active_record_doctor/tasks/unindexed_foreign_keys.rb +7 -28
  17. data/lib/active_record_doctor/version.rb +1 -1
  18. data/lib/tasks/active_record_doctor.rake +33 -0
  19. data/test/active_record_doctor/printers/io_printer_test.rb +15 -7
  20. data/test/active_record_doctor/tasks/extraneous_indexes_test.rb +69 -19
  21. data/test/active_record_doctor/tasks/incorrect_boolean_presence_validation_test.rb +38 -0
  22. data/test/active_record_doctor/tasks/missing_foreign_keys_test.rb +17 -13
  23. data/test/active_record_doctor/tasks/missing_non_null_constraint_test.rb +113 -0
  24. data/test/active_record_doctor/tasks/missing_presence_validation_test.rb +115 -0
  25. data/test/active_record_doctor/tasks/missing_unique_indexes_test.rb +126 -0
  26. data/test/active_record_doctor/tasks/undefined_table_references_test.rb +39 -11
  27. data/test/active_record_doctor/tasks/unindexed_deleted_at_test.rb +59 -0
  28. data/test/active_record_doctor/tasks/unindexed_foreign_keys_test.rb +17 -13
  29. data/test/setup.rb +97 -0
  30. metadata +58 -117
  31. data/Rakefile +0 -28
  32. data/lib/active_record_doctor/compatibility.rb +0 -11
  33. data/lib/tasks/active_record_doctor_tasks.rake +0 -22
  34. data/test/dummy/README.rdoc +0 -28
  35. data/test/dummy/Rakefile +0 -6
  36. data/test/dummy/app/assets/javascripts/application.js +0 -13
  37. data/test/dummy/app/assets/stylesheets/application.css +0 -15
  38. data/test/dummy/app/controllers/application_controller.rb +0 -5
  39. data/test/dummy/app/helpers/application_helper.rb +0 -2
  40. data/test/dummy/app/models/application_record.rb +0 -3
  41. data/test/dummy/app/models/comment.rb +0 -3
  42. data/test/dummy/app/models/contract.rb +0 -3
  43. data/test/dummy/app/models/employer.rb +0 -2
  44. data/test/dummy/app/models/profile.rb +0 -2
  45. data/test/dummy/app/models/user.rb +0 -3
  46. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  47. data/test/dummy/bin/bundle +0 -3
  48. data/test/dummy/bin/rails +0 -4
  49. data/test/dummy/bin/rake +0 -4
  50. data/test/dummy/bin/setup +0 -29
  51. data/test/dummy/config.ru +0 -4
  52. data/test/dummy/config/application.rb +0 -23
  53. data/test/dummy/config/boot.rb +0 -5
  54. data/test/dummy/config/database.yml +0 -19
  55. data/test/dummy/config/database.yml.travis +0 -5
  56. data/test/dummy/config/environment.rb +0 -5
  57. data/test/dummy/config/environments/development.rb +0 -41
  58. data/test/dummy/config/environments/production.rb +0 -79
  59. data/test/dummy/config/environments/test.rb +0 -47
  60. data/test/dummy/config/initializers/assets.rb +0 -11
  61. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  62. data/test/dummy/config/initializers/cookies_serializer.rb +0 -3
  63. data/test/dummy/config/initializers/filter_parameter_logging.rb +0 -4
  64. data/test/dummy/config/initializers/inflections.rb +0 -16
  65. data/test/dummy/config/initializers/mime_types.rb +0 -4
  66. data/test/dummy/config/initializers/session_store.rb +0 -3
  67. data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
  68. data/test/dummy/config/locales/en.yml +0 -23
  69. data/test/dummy/config/routes.rb +0 -56
  70. data/test/dummy/config/secrets.yml +0 -22
  71. data/test/dummy/db/migrate/20160213101213_create_employers.rb +0 -13
  72. data/test/dummy/db/migrate/20160213101221_create_users.rb +0 -23
  73. data/test/dummy/db/migrate/20160213101232_create_profiles.rb +0 -12
  74. data/test/dummy/db/migrate/20160604081452_create_comments.rb +0 -11
  75. data/test/dummy/db/migrate/base_migration.rb +0 -5
  76. data/test/dummy/db/schema.rb +0 -63
  77. data/test/dummy/log/development.log +0 -136
  78. data/test/dummy/log/test.log +0 -1720
  79. data/test/dummy/public/404.html +0 -67
  80. data/test/dummy/public/422.html +0 -67
  81. data/test/dummy/public/500.html +0 -66
  82. data/test/dummy/public/favicon.ico +0 -0
  83. data/test/support/spy_printer.rb +0 -43
  84. data/test/test_helper.rb +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b6e1c0cae1aa647256ff7eafb6a839e1b21efa82
4
- data.tar.gz: 6ca222532fbd13abd9aba412c4dfa3ece725ec94
2
+ SHA256:
3
+ metadata.gz: 649707d9a4a22831b3733778ad05614cc71f8edb704a68e9b18bd784a350daf9
4
+ data.tar.gz: 6e0fc8fe22ba882549152203c3a92f3eeebd3effb3f4eead6245001849b9e5de
5
5
  SHA512:
6
- metadata.gz: 06e9c8db1abc13bdc3cadbf7c72b2d6f25d7fa8b87be116227dcb7a59aaf9475cb35f1cca9d5e679ffed123741d77424bf0a4a80e7a72c53c00a004a830f11c6
7
- data.tar.gz: 0501fb31dd03bc30a9d51344f632fcec82e1ee4ceaa7e2e288360676b39c98978e7129a43c5e368098e1c7d16ea332dfcf863db5b2910bb37cb778326ff3f2b5
6
+ metadata.gz: 437df56684da87b78b3336337cabe138f5375c54673a5d02da29b06fa9d238e1c9fa34a4cf9973b7bf944a0ef8ebbed5ec9dd9d9d12612f87e16597292ff22aa
7
+ data.tar.gz: 3ee83bab9d6dc5c88c4c47d33d69d30ecf373bb6ab4798accfc681066fd0a1a4580295f29a869c69a9ff582c925f7c2cb328b345429a92623942995a0416e1d6
data/README.md CHANGED
@@ -3,10 +3,15 @@
3
3
  Active Record Doctor helps to keep the database in a good shape. Currently, it
4
4
  can:
5
5
 
6
- * index unindexed foreign keys
7
- * detect extraneous indexes
8
- * detect missing foreign key constraints
9
- * detect models referencing undefined tables
6
+ * index unindexed foreign keys - [`active_record_doctor:unindexed_foreign_keys`](#indexing-unindexed-foreign-keys)
7
+ * detect extraneous indexes - [`active_record_doctor:extraneous_indexes`](#removing-extraneous-indexes)
8
+ * detect unindexed `deleted_at` columns - [`active_record_doctor:unindexed_deleted_at`](#detecting-unindexed-deleted_at-columns)
9
+ * detect missing foreign key constraints - [`active_record_doctor:missing_foreign_keys`](#detecting-missing-foreign-key-constraints)
10
+ * detect models referencing undefined tables - [`active_record_doctor:undefined_table_references`](#detecting-models-referencing-undefined-tables)
11
+ * detect uniqueness validations not backed by an unique index - [`active_record_doctor:missing_unique_indexes`](#detecting-uniqueness-validations-not-backed-by-an-index)
12
+ * detect missing non-`NULL` constraints - [`active_record_doctor:missing_non_null_constraint`](#detecting-missing-non-null-constraints)
13
+ * detect missing presence validations - [`active_record_doctor:missing_presence_validation`](#detecting-missing-presence-validations)
14
+ * detect incorrect presence validations on boolean columns - [`active_record_doctor:incorrect_boolean_presence_validation`](#detecting-incorrect-presence-validations-on-boolean-columns)
10
15
 
11
16
  More features coming soon!
12
17
 
@@ -41,7 +46,7 @@ three-step process:
41
46
  1. Generate a list of unindexed foreign keys by running
42
47
 
43
48
  ```bash
44
- rake active_record_doctor:unindexed_foreign_keys > unindexed_foreign_keys.txt
49
+ bundle exec rake active_record_doctor:unindexed_foreign_keys > unindexed_foreign_keys.txt
45
50
  ```
46
51
 
47
52
  2. Remove columns that should _not_ be indexed from `unindexed_foreign_keys.txt`
@@ -57,7 +62,7 @@ three-step process:
57
62
  4. Run the migrations
58
63
 
59
64
  ```bash
60
- rake db:migrate
65
+ bundle exec rake db:migrate
61
66
  ```
62
67
 
63
68
  ### Removing Extraneous Indexes
@@ -82,7 +87,7 @@ To discover such indexes automatically just follow these steps:
82
87
  1. List extraneous indexes by running:
83
88
 
84
89
  ```bash
85
- rake active_record_doctor:extraneous_indexes
90
+ bundle exec rake active_record_doctor:extraneous_indexes
86
91
  ```
87
92
 
88
93
  2. Confirm that each of the indexes can be indeed dropped.
@@ -101,6 +106,24 @@ example, if there's a unique index on `users.login` and a non-unique index on
101
106
  `users.login, users.domain` then the tool will _not_ suggest dropping
102
107
  `users.login` as it could violate the uniqueness assumption.
103
108
 
109
+ ### Detecting Unindexed `deleted_at` Columns
110
+
111
+ If you soft-delete some models (e.g. with `paranoia`) then you need to modify
112
+ your indexes to include only non-deleted rows. Otherwise they will include
113
+ logically non-existent rows. This will make them larger and slower to use. Most
114
+ of the time they should only cover columns satisfying `deleted_at IS NULL`.
115
+
116
+ `active_record_doctor` can automatically detect indexes on tables with a
117
+ `deleted_at` column. Just run:
118
+
119
+ ```
120
+ bundle exec rake active_record_doctor:unindexed_deleted_at
121
+ ```
122
+
123
+ This will print a list of indexes that don't have the `deleted_at IS NULL`
124
+ clause. Currently, `active_record_doctor` cannot automatically generate
125
+ appropriate migrations. You need to do that manually.
126
+
104
127
  ### Detecting Missing Foreign Key Constraints
105
128
 
106
129
  If `users.profile_id` references a row in `profiles` then this can be expressed
@@ -115,7 +138,7 @@ add the constraint; for now, it's your job). You can obtain the list of foreign
115
138
  keys with the following command:
116
139
 
117
140
  ```bash
118
- rake active_record_doctor:missing_foreign_keys
141
+ bundle exec rake active_record_doctor:missing_foreign_keys
119
142
  ```
120
143
 
121
144
  The output will look like:
@@ -148,10 +171,15 @@ cases where the name can be wrong (e.g. you forgot to commit a migration or
148
171
  changed the table name). Active Record Doctor can help you identify these cases
149
172
  before they hit production.
150
173
 
151
- The only think you need to do is run:
174
+ **IMPORTANT**. Models backed by views are supported only in:
175
+
176
+ * Rails 5+ and _any_ database or
177
+ * Rails 4.2 with PostgreSQL.
178
+
179
+ The only think you need to do is run:
152
180
 
153
181
  ```
154
- rake active_record_doctor:undefined_table_references
182
+ bundle exec rake active_record_doctor:undefined_table_references
155
183
  ```
156
184
 
157
185
  If there a model references an undefined table then you'll see a message like
@@ -165,6 +193,108 @@ The following models reference undefined tables:
165
193
  On top of that `rake` will exit with status code of 1. This allows you to use
166
194
  this check as part of your Continuous Integration pipeline.
167
195
 
196
+ ### Detecting Uniqueness Validations not Backed by an Index
197
+
198
+ A model-level uniqueness validations should be backed by a database index in
199
+ order to be robust. Otherwise you risk inserting duplicate values under heavy
200
+ load.
201
+
202
+ In order to detect such validations run:
203
+
204
+ ```
205
+ bundle exec rake active_record_doctor:missing_unique_indexes
206
+ ```
207
+
208
+ If there are such indexes then the command will print:
209
+
210
+ ```
211
+ The following indexes should be created to back model-level uniqueness validations:
212
+ users: email
213
+ ```
214
+
215
+ This means that you should create a unique index on `users.email`.
216
+
217
+ ### Detecting Missing Non-`NULL` Constraints
218
+
219
+ If there's an unconditional presence validation on a column then it should be
220
+ marked as non-`NULL`-able at the database level.
221
+
222
+ In order to detect columns whose presence is required but that are marked
223
+ `null: true` in the database run the following command:
224
+
225
+ ```
226
+ bundle exec rake active_record_doctor:missing_non_null_constraint
227
+ ```
228
+
229
+ The output of the command is similar to:
230
+
231
+ ```
232
+ The following columns should be marked as `null: false`:
233
+ users: name
234
+
235
+ ```
236
+
237
+ You can mark the columns mentioned in the output as `null: false` by creating a
238
+ migration and calling `change_column_null`.
239
+
240
+ This validator skips models whose corresponding database tables don't exist.
241
+
242
+ ### Detecting Missing Presence Validations
243
+
244
+ If a column is marked as `null: false` then it's likely it should have the
245
+ corresponding presence validator.
246
+
247
+ In order to detect models lacking these validations run:
248
+
249
+ ```
250
+ bundle exec rake active_record_doctor:missing_presence_validation
251
+ ```
252
+
253
+ The output of the command looks like this:
254
+
255
+ ```
256
+ The following models and columns should have presence validations:
257
+ User: email, name
258
+ ```
259
+
260
+ This means `User` should have a presence validator on `email` and `name`.
261
+
262
+ This validator skips models whose corresponding database tables don't exist.
263
+
264
+ ### Detecting Incorrect Presence Validations on Boolean Columns
265
+
266
+ A boolean column's presence should be validated using inclusion or exclusion
267
+ validators instead of the usual presence validator.
268
+
269
+ In order to detect boolean columns whose presence is validated incorrectly run:
270
+
271
+ ```
272
+ bundle exec rake active_record_doctor:incorrect_boolean_presence_validation
273
+ ```
274
+
275
+ The output of the command looks like this:
276
+
277
+ ```
278
+ The presence of the following boolean columns is validated incorrectly:
279
+ User: active
280
+ ```
281
+
282
+ This means `active` is validated with `presence: true` instead of
283
+ `inclusion: { in: [true, false] }` or `exclusion: { in: [nil] }`.
284
+
285
+ This validator skips models whose corresponding database tables don't exist.
286
+
287
+ ## Ruby and Rails Compatibility Policy
288
+
289
+ The goal of the policy is to ensure proper functioning in reasonable
290
+ combinations of Ruby and Rails versions. Specifically:
291
+
292
+ 1. If a Rails version is officially supported by the Rails Core Team then it's
293
+ supported by `active_record_doctor`.
294
+ 2. If a Ruby version is compatible with a supported Rails version then it's
295
+ also supported by `active_record_doctor`.
296
+ 3. Only most recent teeny Ruby versions and patch Rails versions are supported.
297
+
168
298
  ## Author
169
299
 
170
300
  This gem is developed and maintained by [Greg Navis](http://www.gregnavis.com).
@@ -1,4 +1,18 @@
1
- require "active_record_doctor/railtie" if defined?(Rails)
1
+ require "active_record_doctor/printers"
2
+ require "active_record_doctor/printers/io_printer"
3
+ require "active_record_doctor/railtie" if defined?(Rails) && defined?(Rails::Railtie)
4
+ require "active_record_doctor/tasks"
5
+ require "active_record_doctor/tasks/base"
6
+ require "active_record_doctor/tasks/missing_presence_validation"
7
+ require "active_record_doctor/tasks/missing_foreign_keys"
8
+ require "active_record_doctor/tasks/missing_unique_indexes"
9
+ require "active_record_doctor/tasks/incorrect_boolean_presence_validation"
10
+ require "active_record_doctor/tasks/extraneous_indexes"
11
+ require "active_record_doctor/tasks/unindexed_deleted_at"
12
+ require "active_record_doctor/tasks/undefined_table_references"
13
+ require "active_record_doctor/tasks/missing_non_null_constraint"
14
+ require "active_record_doctor/tasks/unindexed_foreign_keys"
15
+ require "active_record_doctor/version"
2
16
 
3
17
  module ActiveRecordDoctor
4
18
  end
@@ -1,17 +1,17 @@
1
1
  module ActiveRecordDoctor
2
2
  module Printers
3
3
  class IOPrinter
4
- def initialize(io: STDOUT)
4
+ def initialize(io = STDOUT)
5
5
  @io = io
6
6
  end
7
7
 
8
- def print_unindexed_foreign_keys(unindexed_foreign_keys)
8
+ def unindexed_foreign_keys(unindexed_foreign_keys)
9
9
  @io.puts(unindexed_foreign_keys.sort.map do |table, columns|
10
10
  "#{table} #{columns.sort.join(' ')}"
11
11
  end.join("\n"))
12
12
  end
13
13
 
14
- def print_extraneous_indexes(extraneous_indexes)
14
+ def extraneous_indexes(extraneous_indexes)
15
15
  if extraneous_indexes.empty?
16
16
  @io.puts("No indexes are extraneous.")
17
17
  else
@@ -30,18 +30,74 @@ module ActiveRecordDoctor
30
30
  end
31
31
  end
32
32
 
33
- def print_missing_foreign_keys(missing_foreign_keys)
33
+ def missing_foreign_keys(missing_foreign_keys)
34
34
  @io.puts(missing_foreign_keys.sort.map do |table, columns|
35
35
  "#{table} #{columns.sort.join(' ')}"
36
36
  end.join("\n"))
37
37
  end
38
38
 
39
- def print_undefined_table_references(models)
39
+ def undefined_table_references((models, views_checked))
40
40
  return if models.empty?
41
41
 
42
+ unless views_checked
43
+ @io.puts(<<EOS)
44
+ WARNING: Models backed by database views are supported only in Rails 5+ OR
45
+ Rails 4.2 + PostgreSQL. It seems this is NOT your setup. Therefore, such models
46
+ will be erroneously reported below as not having their underlying tables/views.
47
+ Consider upgrading Rails or disabling this task temporarily.
48
+ EOS
49
+ end
50
+
42
51
  @io.puts('The following models reference undefined tables:')
43
- models.each do |model|
44
- @io.puts(" #{model.name} (the table #{model.table_name} is undefined)")
52
+ models.each do |model_name, table_name|
53
+ @io.puts(" #{model_name} (the table #{table_name} is undefined)")
54
+ end
55
+ end
56
+
57
+ def unindexed_deleted_at(indexes)
58
+ return if indexes.empty?
59
+
60
+ @io.puts('The following indexes should include `deleted_at IS NULL`:')
61
+ indexes.each do |index|
62
+ @io.puts(" #{index}")
63
+ end
64
+ end
65
+
66
+ def missing_unique_indexes(indexes)
67
+ return if indexes.empty?
68
+
69
+ @io.puts('The following indexes should be created to back model-level uniqueness validations:')
70
+ indexes.each do |table, arrays_of_columns|
71
+ arrays_of_columns.each do |columns|
72
+ @io.puts(" #{table}: #{columns.join(', ')}")
73
+ end
74
+ end
75
+ end
76
+
77
+ def missing_presence_validation(missing_presence_validators)
78
+ return if missing_presence_validators.empty?
79
+
80
+ @io.puts('The following models and columns should have presence validations:')
81
+ missing_presence_validators.each do |model_name, array_of_columns|
82
+ @io.puts(" #{model_name}: #{array_of_columns.join(', ')}")
83
+ end
84
+ end
85
+
86
+ def missing_non_null_constraint(missing_non_null_constraints)
87
+ return if missing_non_null_constraints.empty?
88
+
89
+ @io.puts('The following columns should be marked as `null: false`:')
90
+ missing_non_null_constraints.each do |table, columns|
91
+ @io.puts(" #{table}: #{columns.join(', ')}")
92
+ end
93
+ end
94
+
95
+ def incorrect_boolean_presence_validation(incorrect_boolean_presence_validations)
96
+ return if incorrect_boolean_presence_validations.empty?
97
+
98
+ @io.puts('The presence of the following boolean columns is validated incorrectly:')
99
+ incorrect_boolean_presence_validations.each do |table, columns|
100
+ @io.puts(" #{table}: #{columns.join(', ')}")
45
101
  end
46
102
  end
47
103
  end
@@ -1,7 +1,7 @@
1
1
  module ActiveRecordDoctor
2
2
  class Railtie < Rails::Railtie
3
3
  rake_tasks do
4
- load "tasks/active_record_doctor_tasks.rake"
4
+ load "tasks/active_record_doctor.rake"
5
5
  end
6
6
  end
7
7
  end
@@ -1,4 +1,10 @@
1
+ require "active_support"
2
+ require "active_support/core_ext/class/subclasses"
3
+
1
4
  module ActiveRecordDoctor
2
5
  module Tasks
6
+ def self.all
7
+ ActiveRecordDoctor::Tasks::Base.subclasses
8
+ end
3
9
  end
4
10
  end
@@ -0,0 +1,86 @@
1
+ require "active_record_doctor/printers/io_printer"
2
+
3
+ module ActiveRecordDoctor
4
+ module Tasks
5
+ class Base
6
+ def self.run
7
+ new.run
8
+ end
9
+
10
+ def self.description
11
+ @description
12
+ end
13
+
14
+ def initialize(printer = ActiveRecordDoctor::Printers::IOPrinter.new)
15
+ @printer = printer
16
+ end
17
+
18
+ private
19
+
20
+ def success(result)
21
+ [result, true]
22
+ end
23
+
24
+ def connection
25
+ @connection ||= ActiveRecord::Base.connection
26
+ end
27
+
28
+ def indexes(table_name)
29
+ connection.indexes(table_name)
30
+ end
31
+
32
+ def tables
33
+ @tables ||=
34
+ if ActiveRecord::VERSION::MAJOR == 5
35
+ connection.data_sources
36
+ else
37
+ connection.tables
38
+ end
39
+ end
40
+
41
+ def table_exists?(table_name)
42
+ connection.table_exists?(table_name)
43
+ end
44
+
45
+ def views
46
+ @views ||=
47
+ if ActiveRecord::VERSION::MAJOR == 5
48
+ connection.views
49
+ elsif connection.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
50
+ ActiveRecord::Base.connection.execute(<<-SQL).map { |tuple| tuple.fetch("relname") }
51
+ SELECT c.relname FROM pg_class c WHERE c.relkind IN ('m', 'v')
52
+ SQL
53
+ else
54
+ # We don't support this Rails/database combination yet.
55
+ nil
56
+ end
57
+ end
58
+
59
+ def hash_from_pairs(pairs)
60
+ Hash[*pairs.flatten(1)]
61
+ end
62
+
63
+ def eager_load!
64
+ # We call GC.start to make the test suite work. It's (probably) not
65
+ # needed for use during development. However, if we remove it then the
66
+ # test suite will start accumulating temporary model classes in the
67
+ # object space. Running the garbage collector gets rid of them.
68
+ GC.start
69
+
70
+ Rails.application.eager_load!
71
+ end
72
+
73
+ def models
74
+ descendants(ActiveRecord::Base)
75
+ end
76
+
77
+ def descendants(superclass)
78
+ superclass.descendants
79
+ end
80
+
81
+ def descendant?(klass, superclass)
82
+ !klass.nil? && (klass.superclass == superclass || descendant?(klass.superclass, superclass))
83
+ end
84
+ end
85
+ end
86
+ end