redi_search 1.0.3 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/lint.yml +20 -0
  3. data/.github/workflows/tests.yml +44 -0
  4. data/.rubocop.yml +67 -189
  5. data/Appraisals +4 -8
  6. data/Gemfile +3 -4
  7. data/README.md +79 -74
  8. data/Rakefile +15 -3
  9. data/bin/console +29 -0
  10. data/gemfiles/{rails_6.gemfile → activerecord_51.gemfile} +1 -5
  11. data/gemfiles/{rails_51.gemfile → activerecord_52.gemfile} +1 -5
  12. data/lib/redi_search.rb +8 -6
  13. data/lib/redi_search/add.rb +9 -5
  14. data/lib/redi_search/{alter.rb → add_field.rb} +13 -5
  15. data/lib/redi_search/client.rb +8 -6
  16. data/lib/redi_search/client/response.rb +8 -8
  17. data/lib/redi_search/configuration.rb +1 -11
  18. data/lib/redi_search/create.rb +6 -4
  19. data/lib/redi_search/document.rb +5 -4
  20. data/lib/redi_search/document/display.rb +9 -9
  21. data/lib/redi_search/document/finder.rb +10 -2
  22. data/lib/redi_search/index.rb +9 -9
  23. data/lib/redi_search/lazily_load.rb +7 -13
  24. data/lib/redi_search/log_subscriber.rb +23 -52
  25. data/lib/redi_search/model.rb +43 -39
  26. data/lib/redi_search/schema.rb +3 -3
  27. data/lib/redi_search/schema/field.rb +2 -3
  28. data/lib/redi_search/schema/tag_field.rb +1 -1
  29. data/lib/redi_search/schema/text_field.rb +1 -1
  30. data/lib/redi_search/search.rb +15 -26
  31. data/lib/redi_search/search/clauses.rb +6 -7
  32. data/lib/redi_search/search/clauses/application_clause.rb +20 -5
  33. data/lib/redi_search/search/clauses/boolean.rb +8 -6
  34. data/lib/redi_search/search/clauses/highlight.rb +18 -2
  35. data/lib/redi_search/search/clauses/limit.rb +7 -5
  36. data/lib/redi_search/search/clauses/return.rb +1 -1
  37. data/lib/redi_search/search/clauses/slop.rb +1 -1
  38. data/lib/redi_search/search/clauses/sort_by.rb +1 -1
  39. data/lib/redi_search/search/clauses/where.rb +13 -3
  40. data/lib/redi_search/search/result.rb +27 -11
  41. data/lib/redi_search/search/term.rb +7 -6
  42. data/lib/redi_search/spellcheck.rb +3 -4
  43. data/lib/redi_search/spellcheck/result.rb +1 -1
  44. data/lib/redi_search/validatable.rb +49 -0
  45. data/lib/redi_search/validations/inclusion.rb +26 -0
  46. data/lib/redi_search/validations/numericality.rb +45 -0
  47. data/lib/redi_search/validations/presence.rb +29 -0
  48. data/lib/redi_search/version.rb +1 -1
  49. data/redi_search.gemspec +1 -3
  50. metadata +14 -45
  51. data/.travis.yml +0 -31
  52. data/bin/test +0 -7
  53. data/gemfiles/rails_52.gemfile +0 -17
data/Appraisals CHANGED
@@ -1,13 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- appraise "rails-6" do
4
- gem "rails", "6.0.0.rc1"
3
+ appraise "activerecord-52" do
4
+ gem "activerecord", "< 6.0", ">= 5.2"
5
5
  end
6
6
 
7
- appraise "rails-52" do
8
- gem "rails", "< 6.0", ">= 5.2"
9
- end
10
-
11
- appraise "rails-51" do
12
- gem "rails", "< 5.2", ">= 5.1"
7
+ appraise "activerecord-51" do
8
+ gem "activerecord", "< 5.2", ">= 5.1"
13
9
  end
data/Gemfile CHANGED
@@ -2,18 +2,17 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
6
 
7
7
  gemspec
8
8
 
9
+ gem "appraisal", "~> 2.2"
9
10
  gem "faker"
10
11
  gem "mocha"
11
12
  gem "pry"
12
- gem "pry-rails"
13
13
  gem "rubocop"
14
14
  gem "rubocop-performance"
15
- gem "rubocop-rails"
16
15
  gem "simplecov"
17
16
  gem "sqlite3"
18
17
 
19
- gem "rails", "6.0.0.rc1"
18
+ gem "activerecord", "~> 6.0.0"
data/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  # RediSearch
8
8
 
9
- [![Build Status](https://travis-ci.com/npezza93/redi_search.svg?branch=master)](https://travis-ci.com/npezza93/redi_search)
9
+ ![Build Status](https://github.com/npezza93/redi_search/workflows/tests/badge.svg)
10
10
  [![Test Coverage](https://api.codeclimate.com/v1/badges/c6437acac5684de2549d/test_coverage)](https://codeclimate.com/github/npezza93/redi_search/test_coverage)
11
11
  [![Maintainability](https://api.codeclimate.com/v1/badges/c6437acac5684de2549d/maintainability)](https://codeclimate.com/github/npezza93/redi_search/maintainability)
12
12
 
@@ -24,11 +24,13 @@ macOS or Linux you can install via Homebrew.
24
24
 
25
25
  To install RediSearch check out,
26
26
  [https://oss.redislabs.com/redisearch/Quick_Start.html](https://oss.redislabs.com/redisearch/Quick_Start.html).
27
- Once you have RediSearch built, you can update your redis.conf file to always
28
- load the redisearch module with `loadmodule /path/to/redisearch.so`. (On macOS
29
- the redis.conf file can be found `/usr/local/etc/redis.conf`)
27
+ Once you have RediSearch built, if you are not using Docker, you can update your
28
+ redis.conf file to always load the RediSearch module with
29
+ `loadmodule /path/to/redisearch.so`. (On macOS the redis.conf file can be found
30
+ at `/usr/local/etc/redis.conf`)
30
31
 
31
- After Redis and RediSearch are up and running, add this line to your Gemfile:
32
+ After Redis and RediSearch are up and running, add the following line to your
33
+ Gemfile:
32
34
 
33
35
  ```ruby
34
36
  gem 'redi_search'
@@ -51,7 +53,7 @@ require 'redi_search'
51
53
 
52
54
  Once the gem is installed and required you'll need to configure it with your
53
55
  Redis configuration. If you're on Rails, this should go in an initializer
54
- (`config/initializers/redi_search.rb`)
56
+ (`config/initializers/redi_search.rb`).
55
57
 
56
58
  ```ruby
57
59
  RediSearch.configure do |config|
@@ -74,7 +76,7 @@ end
74
76
 
75
77
  ## Preface
76
78
  RediSearch revolves around a search index, so lets start with
77
- defining what a search index is. According to [Switype](https://swiftype.com):
79
+ defining what a search index is. According to [Swiftype](https://swiftype.com):
78
80
  > A search index is a body of structured data that a search engine refers to
79
81
  > when looking for results that are relevant to a specific query. Indexes are a
80
82
  > critical piece of any search system, since they must be tailored to the
@@ -244,38 +246,38 @@ With no options: `{ place: :geo }`
244
246
 
245
247
  A `Document` is the Ruby representation of a RediSearch document.
246
248
 
247
- You can fetch a `Document` using `.get` or `mget` class methods.
248
- - `get(index, document_id)` fetches a single document in an `Index` for a given
249
- `document_id`.
249
+ You can fetch a `Document` using `.get` or `.mget` class methods.
250
+ - `get(index, document_id)` fetches a single `Document` in an `Index` for a
251
+ given `document_id`.
250
252
  - `mget(index, *document_ids)` fetches a collection of `Document`s
251
253
  in an `Index` for the given `document_ids`.
252
254
 
253
255
  You can also make a `Document` instance using the
254
256
  `.for_object(index, record, serializer: nil, only: [])` class method. It takes
255
- an `Index` instance and a ruby object. That object must respond to all the
256
- fields specified in the indexes schema or pass a serializer class that accepts
257
- the object and responds to all the fields specified in the indexes schema.
258
- `only` accepts an array of fields from the schema and limits the fields that are
259
- passed to the `Document`.
257
+ an `Index` instance and a Ruby object. That object must respond to all the
258
+ fields specified in the `Index`'s `Schema` or pass a serializer class that
259
+ accepts the object and responds to all the fields specified in the `Index`'s
260
+ `Schema`. `only` accepts an array of fields from the schema and limits the
261
+ fields that are passed to the `Document`.
260
262
 
261
263
  Once you have an instance of a `Document`, it responds to all the fields
262
- specified in the indexes schema as methods and `document_id`. `document_id` is
263
- automatically prepended with the indexes names unless it already is to ensure
264
- uniqueness. We prepend the index name because if you have two documents with the
265
- same id in different indexes we don't want the documents to override each other.
266
- There is also a `#document_id_without_index` method which removes the prepended
267
- index name.
264
+ specified in the `Index`'s `Schema` as methods and `document_id`. `document_id`
265
+ is automatically prepended with the `Index`'s names unless it already is to
266
+ ensure uniqueness. We prepend the `Index` name because if you have two
267
+ `Document`s with the same id in different `Index`s we don't want the `Document`s
268
+ to override each other. There is also a `#document_id_without_index` method
269
+ which removes the prepended index name.
268
270
 
269
- Finally there is a `#del` method that will remove the document from the index.
270
- It optionally accepts a `delete_document` named argument that signifies whether
271
- the document should be completely removed from the Redis instance vs just the
272
- index.
271
+ Finally there is a `#del` method that will remove the `Document` from the
272
+ `Index`. It optionally accepts a `delete_document` named argument that signifies
273
+ whether the `Document` should be completely removed from the Redis instance vs
274
+ just the `Index`.
273
275
 
274
276
 
275
277
  ## Index
276
278
 
277
- To initialize an index, pass the name of the index as a string or symbol and the
278
- schema.
279
+ To initialize an `Index`, pass the name of the `Index` as a string or symbol
280
+ and the `Schema`.
279
281
 
280
282
  ```ruby
281
283
  RediSearch::Index.new(name_of_index, schema)
@@ -291,7 +293,7 @@ RediSearch::Index.new(name_of_index, schema)
291
293
  - For efficiency, RediSearch encodes indexes differently if they are
292
294
  created with less than 32 text fields. This option forces RediSearch
293
295
  to encode indexes as if there were more than 32 text fields, which
294
- allows you to add additional fields (beyond 32) using `alter`.
296
+ allows you to add additional fields (beyond 32) using `add_field`.
295
297
  - `no_offsets: #{true || false}`
296
298
  - If set, we do not store term offsets for documents (saves memory, does
297
299
  not allow exact searches or highlighting). Implies `no_highlight`.
@@ -313,40 +315,40 @@ RediSearch::Index.new(name_of_index, schema)
313
315
  memory but does not allow sorting based on the frequencies of a given
314
316
  term within the document.
315
317
  - `drop`
316
- - Drops the index from the Redis instance, returns a boolean. Has an
318
+ - Drops the `Index` from the Redis instance, returns a boolean. Has an
317
319
  accompanying bang method that will raise an exception upon failure. Will
318
- return `false` if the index has already been dropped.
320
+ return `false` if the `Index` has already been dropped.
319
321
  - `exist?`
320
- - Returns a boolean signifying index existence.
322
+ - Returns a boolean signifying `Index` existence.
321
323
  - `info`
322
- - Returns a struct object with all the information about the index.
324
+ - Returns a struct object with all the information about the `Index`.
323
325
  - `fields`
324
- - Returns an array of the field names in the index.
326
+ - Returns an array of the field names in the `Index`.
325
327
  - `add(document, score: 1.0, replace: {}, language: nil, no_save: false)`
326
328
  - Takes a `Document` object and options. Has an
327
329
  accompanying bang method that will raise an exception upon failure.
328
- - `score` -> The document's rank, a value between 0.0 and 1.0
330
+ - `score` -> The `Document`'s rank, a value between 0.0 and 1.0
329
331
  - `language` -> Use a stemmer for the supplied language during indexing.
330
- - `no_save` -> Don't save the actual document in the database and only index it.
332
+ - `no_save` -> Don't save the actual `Document` in the database and only index it.
331
333
  - `replace` -> Accepts a boolean or a hash. If a truthy value is passed, we
332
334
  will do an UPSERT style insertion - and delete an older version of the
333
- document if it exists.
335
+ `Document` if it exists.
334
336
  - `replace: { partial: true }` -> Allows you to not have to specify all
335
337
  fields for reindexing. Fields not given to the command will be loaded from
336
- the current version of the document.
337
- - `add_multiple!(documents, score: 1.0, replace: {}, language: nil, no_save: false)`
338
+ the current version of the `Document`.
339
+ - `add_multiple(documents, score: 1.0, replace: {}, language: nil, no_save: false)`
338
340
  - Takes an array of `Document` objects. This provides a more performant way to
339
- add multiple documents to the index. Accepts the same options as `add`.
341
+ add multiple documents to the `Index`. Accepts the same options as `add`.
340
342
  - `del(document, delete_document: false)`
341
- - Removes a document from the index. `delete_document` signifies whether the
342
- document should be completely removed from the Redis instance vs just the
343
- index.
343
+ - Removes a `Document` from the `Index`. `delete_document` signifies whether the
344
+ `Document` should be completely removed from the Redis instance vs just the
345
+ `Index`.
344
346
  - `document_count`
345
- - Returns the number of documents in the index
346
- - `alter(field_name, schema)`
347
- - Adds a new field to the index. Ex: `index.alter(:first_name, text: { phonetic: "dm:en" })`
347
+ - Returns the number of `Document`s in the `Index`
348
+ - `add_field(field_name, schema)`
349
+ - Adds a new field to the `Index`. Ex: `index.add_field(:first_name, text: { phonetic: "dm:en" })`
348
350
  - `reindex(documents, recreate: false, **options)`
349
- - If `recreate` is `true` the index will be dropped and recreated
351
+ - If `recreate` is `true` the `Index` will be dropped and recreated
350
352
  - `options` accepts the same options as `add`
351
353
 
352
354
 
@@ -354,8 +356,7 @@ RediSearch::Index.new(name_of_index, schema)
354
356
 
355
357
  Searching is initiated off a `RediSearch::Index` instance with clauses that can
356
358
  be chained together. When searching, an array of `Document`s is returned
357
- which has public reader methods for all the schema fields and a `document_id`
358
- method which returns the id of the document prefixed with the index name.
359
+ which has public reader methods for all the schema fields.
359
360
 
360
361
  ```ruby
361
362
  main ❯ index = RediSearch::Index.new("user_idx", name: { text: { phonetic: "dm:en" } })
@@ -440,15 +441,15 @@ index.search.where(number: -Float::INFINITY..0)
440
441
  phrase terms. (i.e the slop for exact phrases is 0)
441
442
  - `in_order`
442
443
  - Usually used in conjunction with `slop`. We make sure the query terms appear
443
- in the same order in the document as in the query, regardless of the offsets
444
- between them.
444
+ in the same order in the `Document` as in the query, regardless of the
445
+ offsets between them.
445
446
  - `no_content`
446
- - Only return the document ids and not the content. This is useful if
447
- RediSearch is being used on a Rails model where the document attributes
448
- don't matter and it's being converted into ActiveRecord objects.
447
+ - Only return the `Document` ids and not the content. This is useful if
448
+ RediSearch is being used on a Rails model where the `Document` attributes
449
+ don't matter and it's being converted into `ActiveRecord` objects.
449
450
  - `language(language)`
450
451
  - Stemmer to use for the supplied language during search for query expansion.
451
- If querying documents in Chinese, this should be set to chinese in order to
452
+ If querying `Document`s in Chinese, this should be set to chinese in order to
452
453
  properly tokenize the query terms. If an unsupported language is sent, the
453
454
  command returns an error.
454
455
  - `sort_by(field, order: :asc)`
@@ -459,7 +460,7 @@ index.search.where(number: -Float::INFINITY..0)
459
460
  - Limit the results to the specified `num` at the `offset`. The default limit
460
461
  is set to `10`.
461
462
  - `count`
462
- - Returns the number of documents found in the search query
463
+ - Returns the number of `Document`s found in the search query
463
464
  - `highlight(fields: [], opening_tag: "<b>", closing_tag: "</b>")`
464
465
  - Use this option to format occurrences of matched text. `fields` are an
465
466
  array of fields to be highlighted.
@@ -469,16 +470,14 @@ index.search.where(number: -Float::INFINITY..0)
469
470
  - `no_stop_words`
470
471
  - Do not filter stopwords from the query.
471
472
  - `with_scores`
472
- - Include the relative internal score of each document. This can be used to
473
+ - Include the relative internal score of each `Document`. This can be used to
473
474
  merge results from multiple instances. This will add a `score` method to the
474
475
  returned `Document` instances.
475
476
  - `return(*fields)`
476
- - Limit which fields from the document are returned.
477
+ - Limit which fields from the `Document` are returned.
477
478
  - `explain`
478
479
  - Returns the execution plan for a complex query. In the returned response,
479
480
  a + on a term is an indication of stemming.
480
- - `to_redis`
481
- - Returns the command to be executed without executing it.
482
481
 
483
482
 
484
483
  ## Spellcheck
@@ -507,8 +506,8 @@ main ❯ index.spellcheck("jimy", distance: 2).first.suggestions
507
506
 
508
507
  ## Rails Integration
509
508
 
510
- Integration with Rails is super easy! Call `redi_search` with the schema keyword
511
- arg from inside your model. Ex:
509
+ Integration with Rails is super easy! Call `redi_search` with the `schema`
510
+ keyword argument from inside your model. Ex:
512
511
 
513
512
  ```ruby
514
513
  class User < ApplicationRecord
@@ -522,8 +521,9 @@ end
522
521
  This will automatically add `User.search` and `User.spellcheck`
523
522
  methods which behave the same as if you called them on an `Index` instance.
524
523
 
525
- `User.reindex(only: [], **options)` is also added and behaves similarly to `RediSearch::Index#reindex`. Some of the differences include:
526
- - By default does an upsert for all documents added using the
524
+ `User.reindex(recreate: false, only: [], **options)` is also added and behaves
525
+ similarly to `RediSearch::Index#reindex`. Some of the differences include:
526
+ - By default, does an upsert for all `Document`s added using the
527
527
  option `replace: { partial: true }`.
528
528
  - `Document`s do not to be passed as the first parameter. The `search_import`
529
529
  scope is automatically called and all the records are converted
@@ -552,8 +552,8 @@ class UserSerializer < SimpleDelegator
552
552
  end
553
553
  ```
554
554
 
555
- You can create a scope on the model to eager load relationships when indexing or
556
- it can be used to limit the records to index.
555
+ You can override the `search_import` scope on the model to eager load
556
+ relationships when indexing or it can be used to limit the records to index.
557
557
 
558
558
  ```ruby
559
559
  class User < ApplicationRecord
@@ -561,7 +561,11 @@ class User < ApplicationRecord
561
561
  end
562
562
  ```
563
563
 
564
- The default index name for model indexes is
564
+ When searching, by default a collection of `Document`s is returned. Calling
565
+ `#results` on the search query will execute the search, and then look up all the
566
+ found records in the database and return an ActiveRecord relation.
567
+
568
+ The default `Index` name for model `Index`s is
565
569
  `#{model_name.plural}_#{RediSearch.env}`. The `redi_search` method takes an
566
570
  optional `index_prefix` argument which gets prepended to the index name:
567
571
 
@@ -578,15 +582,16 @@ User.redi_search_index.name
578
582
  ```
579
583
 
580
584
  When integrating RediSearch into a model, records will automatically be indexed
581
- after creating and updating and will be removed from the index upon destruction.
585
+ after creating and updating and will be removed from the `Index` upon
586
+ destruction.
582
587
 
583
- There are few more convenience methods that are publicly available:
588
+ There are a few more convenience methods that are publicly available:
584
589
  - `redi_search_document`
585
590
  - Returns the record as a `RediSearch::Document` instance
586
591
  - `redi_search_delete_document`
587
- - Removes the record from the index
592
+ - Removes the record from the `Index`
588
593
  - `redi_search_add_document`
589
- - Adds the record to the index
594
+ - Adds the record to the `Index`
590
595
  - `redi_search_index`
591
596
  - Returns the `RediSearch::Index` instance
592
597
 
@@ -594,15 +599,15 @@ There are few more convenience methods that are publicly available:
594
599
  ## Development
595
600
 
596
601
  After checking out the repo, run `bin/setup` to install dependencies. Then, run
597
- `rake test` to run the tests. You can also run `bin/console` for an interactive
598
- prompt that will allow you to experiment. You can also start a rails console if
599
- you `cd` into `test/dummy`.
602
+ `rake test` to run the both unit and integration tests. To run them individually
603
+ you can run `rake test:unit` or `rake test:integration`. You can also run
604
+ `bin/console` for an interactive prompt that will allow you to experiment.
600
605
 
601
606
  To install this gem onto your local machine, run `bundle exec rake install`. To
602
607
  release a new version, execute `bin/publish (major|minor|patch)` which will
603
608
  update the version number in `version.rb`, create a git tag for the version,
604
609
  push git commits and tags, and push the `.gem` file to
605
- [rubygems.org](https://rubygems.org).
610
+ [rubygems.org](https://rubygems.org) and GitHub.
606
611
 
607
612
  ## Contributing
608
613
 
data/Rakefile CHANGED
@@ -1,12 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bundler/gem_tasks"
4
+ require "rubocop/rake_task"
4
5
  require "rake/testtask"
5
6
 
6
- Rake::TestTask.new(:test) do |t|
7
+ RuboCop::RakeTask.new(:rubocop) do |t|
8
+ t.options = ["--display-cop-names"]
9
+ end
10
+
11
+ Rake::TestTask.new("test:unit") do |t|
12
+ t.libs << "test"
13
+ t.libs << "lib"
14
+ t.test_files = FileList["test/unit/**/*_test.rb"]
15
+ end
16
+
17
+ Rake::TestTask.new("test:integration") do |t|
7
18
  t.libs << "test"
8
19
  t.libs << "lib"
9
- t.test_files = FileList["test/**/*_test.rb"]
20
+ t.test_files = FileList["test/integration/**/*_test.rb"]
10
21
  end
11
22
 
12
- task default: :test
23
+ task test: [:default]
24
+ task default: ["test:integration", "test:unit", :rubocop]
@@ -4,5 +4,34 @@
4
4
  require "bundler/setup"
5
5
  require "redi_search"
6
6
 
7
+ require "faker"
7
8
  require "pry"
9
+ require "active_support/logger"
10
+ require "active_record"
11
+
12
+ ActiveSupport::LogSubscriber.logger = ActiveSupport::Logger.new(STDOUT)
13
+
14
+ ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
15
+
16
+ ActiveRecord::Migration.create_table :users do |t|
17
+ t.string :first
18
+ t.string :last
19
+ end
20
+
21
+ class User < ActiveRecord::Base
22
+ redi_search schema: {
23
+ first: { text: { phonetic: "dm:en" } },
24
+ last: { text: { phonetic: "dm:en" } }
25
+ }
26
+ end
27
+
28
+ def seed_users(count = 10_000)
29
+ User.insert_all(
30
+ Array.new(count).map do
31
+ { first: Faker::Name.first_name, last: Faker::Name.last_name }
32
+ end
33
+ )
34
+ User.reindex
35
+ end
36
+
8
37
  Pry.start
@@ -2,16 +2,12 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "faker"
6
- gem "minitest", "~> 5.0"
7
5
  gem "mocha"
8
6
  gem "pry"
9
- gem "pry-rails"
10
7
  gem "rubocop"
11
8
  gem "rubocop-performance"
12
- gem "rubocop-rails"
13
9
  gem "simplecov"
14
10
  gem "sqlite3"
15
- gem "rails", "6.0.0.rc1"
11
+ gem "activerecord", "< 5.2", ">= 5.1"
16
12
 
17
13
  gemspec path: "../"
@@ -2,16 +2,12 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "faker"
6
- gem "minitest", "~> 5.0"
7
5
  gem "mocha"
8
6
  gem "pry"
9
- gem "pry-rails"
10
7
  gem "rubocop"
11
8
  gem "rubocop-performance"
12
- gem "rubocop-rails"
13
9
  gem "simplecov"
14
10
  gem "sqlite3"
15
- gem "rails", "< 5.2", ">= 5.1"
11
+ gem "activerecord", "< 6.0", ">= 5.2"
16
12
 
17
13
  gemspec path: "../"