ransack 4.1.1 → 4.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -4
  3. data/lib/polyamorous/activerecord/join_association_7_2.rb +55 -0
  4. data/lib/polyamorous/polyamorous.rb +5 -1
  5. data/lib/ransack/adapters/active_record/context.rb +32 -5
  6. data/lib/ransack/constants.rb +1 -1
  7. data/lib/ransack/context.rb +7 -4
  8. data/lib/ransack/helpers/form_builder.rb +6 -7
  9. data/lib/ransack/helpers/form_helper.rb +86 -20
  10. data/lib/ransack/invalid_search_error.rb +3 -0
  11. data/lib/ransack/locale/ja.yml +51 -51
  12. data/lib/ransack/locale/ko.yml +70 -0
  13. data/lib/ransack/locale/uk.yml +72 -0
  14. data/lib/ransack/nodes/condition.rb +38 -6
  15. data/lib/ransack/nodes/grouping.rb +1 -1
  16. data/lib/ransack/nodes/sort.rb +1 -1
  17. data/lib/ransack/nodes/value.rb +1 -1
  18. data/lib/ransack/search.rb +4 -3
  19. data/lib/ransack/version.rb +1 -1
  20. data/lib/ransack.rb +5 -0
  21. data/spec/console.rb +3 -15
  22. data/spec/factories/articles.rb +7 -0
  23. data/spec/factories/comments.rb +7 -0
  24. data/spec/factories/notes.rb +13 -0
  25. data/spec/factories/people.rb +10 -0
  26. data/spec/factories/tags.rb +5 -0
  27. data/spec/polyamorous/join_association_spec.rb +0 -1
  28. data/spec/polyamorous/join_dependency_spec.rb +0 -1
  29. data/spec/ransack/adapters/active_record/base_spec.rb +144 -3
  30. data/spec/ransack/adapters/active_record/context_spec.rb +72 -0
  31. data/spec/ransack/helpers/form_builder_spec.rb +0 -2
  32. data/spec/ransack/helpers/form_helper_spec.rb +219 -5
  33. data/spec/ransack/invalid_search_error_spec.rb +27 -0
  34. data/spec/ransack/nodes/condition_spec.rb +230 -0
  35. data/spec/ransack/nodes/grouping_spec.rb +2 -2
  36. data/spec/ransack/nodes/value_spec.rb +12 -1
  37. data/spec/ransack/predicate_spec.rb +16 -9
  38. data/spec/ransack/ransacker_spec.rb +69 -0
  39. data/spec/ransack/search_spec.rb +129 -2
  40. data/spec/ransack/translate_spec.rb +0 -1
  41. data/spec/spec_helper.rb +7 -21
  42. data/spec/support/schema.rb +48 -9
  43. metadata +32 -97
  44. data/.github/FUNDING.yml +0 -3
  45. data/.github/SECURITY.md +0 -12
  46. data/.github/workflows/codeql.yml +0 -72
  47. data/.github/workflows/cronjob.yml +0 -99
  48. data/.github/workflows/deploy.yml +0 -35
  49. data/.github/workflows/rubocop.yml +0 -20
  50. data/.github/workflows/test-deploy.yml +0 -29
  51. data/.github/workflows/test.yml +0 -131
  52. data/.gitignore +0 -7
  53. data/.nojekyll +0 -0
  54. data/.rubocop.yml +0 -50
  55. data/CHANGELOG.md +0 -1176
  56. data/CONTRIBUTING.md +0 -171
  57. data/Gemfile +0 -53
  58. data/Rakefile +0 -24
  59. data/bug_report_templates/test-ransack-scope-and-column-same-name.rb +0 -78
  60. data/bug_report_templates/test-ransacker-arel-present-predicate.rb +0 -75
  61. data/docs/.gitignore +0 -19
  62. data/docs/.nojekyll +0 -0
  63. data/docs/babel.config.js +0 -3
  64. data/docs/blog/2022-03-27-ransack-3.0.0.md +0 -20
  65. data/docs/docs/getting-started/_category_.json +0 -4
  66. data/docs/docs/getting-started/advanced-mode.md +0 -46
  67. data/docs/docs/getting-started/configuration.md +0 -47
  68. data/docs/docs/getting-started/search-matches.md +0 -67
  69. data/docs/docs/getting-started/simple-mode.md +0 -288
  70. data/docs/docs/getting-started/sorting.md +0 -71
  71. data/docs/docs/getting-started/using-predicates.md +0 -282
  72. data/docs/docs/going-further/_category_.json +0 -4
  73. data/docs/docs/going-further/acts-as-taggable-on.md +0 -114
  74. data/docs/docs/going-further/associations.md +0 -70
  75. data/docs/docs/going-further/custom-predicates.md +0 -52
  76. data/docs/docs/going-further/documentation.md +0 -43
  77. data/docs/docs/going-further/exporting-to-csv.md +0 -49
  78. data/docs/docs/going-further/external-guides.md +0 -57
  79. data/docs/docs/going-further/form-customisation.md +0 -63
  80. data/docs/docs/going-further/i18n.md +0 -53
  81. data/docs/docs/going-further/img/create_release.png +0 -0
  82. data/docs/docs/going-further/merging-searches.md +0 -41
  83. data/docs/docs/going-further/other-notes.md +0 -428
  84. data/docs/docs/going-further/polymorphic-search.md +0 -46
  85. data/docs/docs/going-further/ransackers.md +0 -331
  86. data/docs/docs/going-further/release_process.md +0 -36
  87. data/docs/docs/going-further/saving-queries.md +0 -82
  88. data/docs/docs/going-further/searching-postgres.md +0 -57
  89. data/docs/docs/going-further/wiki-contributors.md +0 -82
  90. data/docs/docs/intro.md +0 -99
  91. data/docs/docusaurus.config.js +0 -120
  92. data/docs/package.json +0 -42
  93. data/docs/sidebars.js +0 -31
  94. data/docs/src/components/HomepageFeatures/index.js +0 -64
  95. data/docs/src/components/HomepageFeatures/styles.module.css +0 -11
  96. data/docs/src/css/custom.css +0 -39
  97. data/docs/src/pages/index.module.css +0 -23
  98. data/docs/src/pages/markdown-page.md +0 -7
  99. data/docs/static/.nojekyll +0 -0
  100. data/docs/static/img/docusaurus.png +0 -0
  101. data/docs/static/img/favicon.ico +0 -0
  102. data/docs/static/img/logo.svg +0 -1
  103. data/docs/static/img/tutorial/docsVersionDropdown.png +0 -0
  104. data/docs/static/img/tutorial/localeDropdown.png +0 -0
  105. data/docs/static/img/undraw_docusaurus_mountain.svg +0 -171
  106. data/docs/static/img/undraw_docusaurus_react.svg +0 -170
  107. data/docs/static/img/undraw_docusaurus_tree.svg +0 -40
  108. data/docs/static/logo/ransack-h.png +0 -0
  109. data/docs/static/logo/ransack-h.svg +0 -34
  110. data/docs/static/logo/ransack-v.png +0 -0
  111. data/docs/static/logo/ransack-v.svg +0 -34
  112. data/docs/static/logo/ransack.png +0 -0
  113. data/docs/static/logo/ransack.svg +0 -21
  114. data/docs/yarn.lock +0 -8879
  115. data/ransack.gemspec +0 -26
  116. data/spec/blueprints/articles.rb +0 -5
  117. data/spec/blueprints/comments.rb +0 -5
  118. data/spec/blueprints/notes.rb +0 -5
  119. data/spec/blueprints/people.rb +0 -8
  120. data/spec/blueprints/tags.rb +0 -3
@@ -0,0 +1,69 @@
1
+ require 'spec_helper'
2
+
3
+ module Ransack
4
+ describe Ransacker do
5
+ let(:klass) { Person }
6
+ let(:name) { :test_ransacker }
7
+ let(:opts) { {} }
8
+
9
+ describe '#initialize' do
10
+ context 'with minimal options' do
11
+ subject { Ransacker.new(klass, name, opts) }
12
+
13
+ it 'sets the name' do
14
+ expect(subject.name).to eq(name)
15
+ end
16
+
17
+ it 'sets default type to string' do
18
+ expect(subject.type).to eq(:string)
19
+ end
20
+
21
+ it 'sets default args to [:parent]' do
22
+ expect(subject.args).to eq([:parent])
23
+ end
24
+ end
25
+
26
+ context 'with custom options' do
27
+ let(:opts) { { type: :integer, args: [:parent, :custom_arg], formatter: proc { |v| v.to_i } } }
28
+
29
+ subject { Ransacker.new(klass, name, opts) }
30
+
31
+ it 'sets the custom type' do
32
+ expect(subject.type).to eq(:integer)
33
+ end
34
+
35
+ it 'sets the custom args' do
36
+ expect(subject.args).to eq([:parent, :custom_arg])
37
+ end
38
+
39
+ it 'sets the formatter' do
40
+ expect(subject.formatter).to eq(opts[:formatter])
41
+ end
42
+ end
43
+
44
+ context 'with callable option' do
45
+ let(:callable) { proc { |parent| parent.table[:id] } }
46
+ let(:opts) { { callable: callable } }
47
+
48
+ subject { Ransacker.new(klass, name, opts) }
49
+
50
+ it 'initializes successfully' do
51
+ expect(subject).to be_a(Ransacker)
52
+ end
53
+ end
54
+ end
55
+
56
+ describe 'basic functionality' do
57
+ subject { Ransacker.new(klass, name, opts) }
58
+
59
+ it 'responds to required methods' do
60
+ expect(subject).to respond_to(:name)
61
+ expect(subject).to respond_to(:type)
62
+ expect(subject).to respond_to(:args)
63
+ expect(subject).to respond_to(:formatter)
64
+ expect(subject).to respond_to(:attr_from)
65
+ expect(subject).to respond_to(:call)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -178,7 +178,6 @@ module Ransack
178
178
  # AND "articles"."title" = 'Test' AND "articles"."published" = 't' AND ('default_scope' = 'default_scope')
179
179
  # ) ORDER BY "people"."id" DESC
180
180
 
181
- pending("spec should pass, but I do not know how/where to fix lib code")
182
181
  s = Search.new(Person, published_articles_title_not_eq: 'Test')
183
182
  expect(s.result.to_sql).to include 'default_scope'
184
183
  expect(s.result.to_sql).to include 'published'
@@ -270,6 +269,7 @@ module Ransack
270
269
  end
271
270
 
272
271
  specify { expect { subject }.to raise_error ArgumentError }
272
+ specify { expect { subject }.to raise_error InvalidSearchError }
273
273
  end
274
274
 
275
275
  context 'when ignore_unknown_conditions configuration option is true' do
@@ -300,6 +300,7 @@ module Ransack
300
300
 
301
301
  context 'when ignore_unknown_conditions search parameter is false' do
302
302
  specify { expect { with_ignore_unknown_conditions_false }.to raise_error ArgumentError }
303
+ specify { expect { with_ignore_unknown_conditions_false }.to raise_error InvalidSearchError }
303
304
  end
304
305
 
305
306
  context 'when ignore_unknown_conditions search parameter is true' do
@@ -312,6 +313,46 @@ module Ransack
312
313
  expect { Search.new(Person, params) }.not_to change { params }
313
314
  end
314
315
 
316
+ context 'with empty search parameters' do
317
+ it 'handles completely empty parameters' do
318
+ search = Search.new(Person, {})
319
+ expect(search.result.to_sql).not_to match(/WHERE/)
320
+ end
321
+
322
+ it 'handles nil parameters' do
323
+ search = Search.new(Person, nil)
324
+ expect(search.result.to_sql).not_to match(/WHERE/)
325
+ end
326
+ end
327
+
328
+ context 'with whitespace-only values' do
329
+ before do
330
+ Ransack.configure { |c| c.strip_whitespace = true }
331
+ end
332
+
333
+ it 'removes whitespace-only values' do
334
+ expect_any_instance_of(Search).to receive(:build).with({})
335
+ Search.new(Person, name_eq: ' ')
336
+ end
337
+
338
+ it 'keeps values with content after whitespace stripping' do
339
+ expect_any_instance_of(Search).to receive(:build).with({ 'name_eq' => 'test' })
340
+ Search.new(Person, name_eq: ' test ')
341
+ end
342
+ end
343
+
344
+ context 'with special characters in values' do
345
+ it 'handles values with special regex characters' do
346
+ search = Search.new(Person, name_cont: 'test[(){}^$|?*+.\\')
347
+ expect { search.result }.not_to raise_error
348
+ end
349
+
350
+ it 'handles values with SQL injection attempts' do
351
+ search = Search.new(Person, name_cont: "'; DROP TABLE people; --")
352
+ expect { search.result }.not_to raise_error
353
+ end
354
+ end
355
+
315
356
  context "ransackable_scope" do
316
357
  around(:each) do |example|
317
358
  Person.define_singleton_method(:name_eq) do |name|
@@ -335,6 +376,74 @@ module Ransack
335
376
  end
336
377
  end
337
378
 
379
+ context "ransackable_scope with array arguments" do
380
+ around(:each) do |example|
381
+ Person.define_singleton_method(:domestic) do |countries|
382
+ self.where(name: countries)
383
+ end
384
+
385
+ Person.define_singleton_method(:flexible_scope) do |*args|
386
+ self.where(id: args)
387
+ end
388
+
389
+ Person.define_singleton_method(:two_param_scope) do |param1, param2|
390
+ self.where(name: param1, id: param2)
391
+ end
392
+
393
+ begin
394
+ example.run
395
+ ensure
396
+ Person.singleton_class.undef_method :domestic
397
+ Person.singleton_class.undef_method :flexible_scope
398
+ Person.singleton_class.undef_method :two_param_scope
399
+ end
400
+ end
401
+
402
+ it "handles scopes that take arrays as single arguments (arity 1)" do
403
+ allow(Person).to receive(:ransackable_scopes)
404
+ .and_return(Person.ransackable_scopes + [:domestic])
405
+
406
+ # This should not raise ArgumentError
407
+ expect {
408
+ s = Search.new(Person, domestic: ['US', 'JP'])
409
+ s.result # This triggers the actual scope call
410
+ }.not_to raise_error
411
+
412
+ s = Search.new(Person, domestic: ['US', 'JP'])
413
+ expect(s.instance_variable_get(:@scope_args)["domestic"]).to eq("US")
414
+ end
415
+
416
+ it "handles scopes with flexible arity (negative arity)" do
417
+ allow(Person).to receive(:ransackable_scopes)
418
+ .and_return(Person.ransackable_scopes + [:flexible_scope])
419
+
420
+ expect {
421
+ s = Search.new(Person, flexible_scope: ['US', 'JP'])
422
+ s.result
423
+ }.not_to raise_error
424
+ end
425
+
426
+ it "handles scopes with arity > 1" do
427
+ allow(Person).to receive(:ransackable_scopes)
428
+ .and_return(Person.ransackable_scopes + [:two_param_scope])
429
+
430
+ expect {
431
+ s = Search.new(Person, two_param_scope: ['param1', 'param2'])
432
+ s.result
433
+ }.not_to raise_error
434
+ end
435
+
436
+ it "still supports the workaround with nested arrays" do
437
+ allow(Person).to receive(:ransackable_scopes)
438
+ .and_return(Person.ransackable_scopes + [:domestic])
439
+
440
+ # The workaround from the issue should still work
441
+ expect {
442
+ s = Search.new(Person, domestic: [['US', 'JP']])
443
+ s.result
444
+ }.not_to raise_error
445
+ end
446
+ end
338
447
  end
339
448
 
340
449
  describe '#result' do
@@ -347,6 +456,7 @@ module Ransack
347
456
  let(:notable_type_field) {
348
457
  "#{quote_table_name("notes")}.#{quote_column_name("notable_type")}"
349
458
  }
459
+
350
460
  it 'evaluates conditions contextually' do
351
461
  s = Search.new(Person, children_name_eq: 'Ernie')
352
462
  expect(s.result).to be_an ActiveRecord::Relation
@@ -453,7 +563,7 @@ module Ransack
453
563
 
454
564
  all_or_load, uniq_or_distinct = :load, :distinct
455
565
  expect(s.result.send(all_or_load).size)
456
- .to eq(9000)
566
+ .to eq(8998)
457
567
  expect(s.result(distinct: true).size)
458
568
  .to eq(10)
459
569
  expect(s.result.send(all_or_load).send(uniq_or_distinct))
@@ -477,6 +587,11 @@ module Ransack
477
587
  @s = Search.new(Person)
478
588
  end
479
589
 
590
+ it 'doesn\'t creates sorts' do
591
+ @s.sorts = ''
592
+ expect(@s.sorts.size).to eq(0)
593
+ end
594
+
480
595
  it 'creates sorts based on a single attribute/direction' do
481
596
  @s.sorts = 'id desc'
482
597
  expect(@s.sorts.size).to eq(1)
@@ -614,6 +729,18 @@ module Ransack
614
729
  expect(@s.result.first.id).to eq 1
615
730
  end
616
731
 
732
+ it 'raises ArgumentError when an invalid argument is sent' do
733
+ expect do
734
+ @s.sorts = 1234
735
+ end.to raise_error(ArgumentError, "Invalid argument (Integer) supplied to sorts=")
736
+ end
737
+
738
+ it 'raises InvalidSearchError when an invalid argument is sent' do
739
+ expect do
740
+ @s.sorts = 1234
741
+ end.to raise_error(Ransack::InvalidSearchError, "Invalid argument (Integer) supplied to sorts=")
742
+ end
743
+
617
744
  it "PG's sort option", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do
618
745
  default = Ransack.options.clone
619
746
 
@@ -2,7 +2,6 @@ require 'spec_helper'
2
2
 
3
3
  module Ransack
4
4
  describe Translate do
5
-
6
5
  describe '.attribute' do
7
6
  it 'translate namespaced attribute like AR does' do
8
7
  ar_translation = ::Namespace::Article.human_attribute_name(:title)
data/spec/spec_helper.rb CHANGED
@@ -1,38 +1,27 @@
1
- require 'machinist/active_record'
2
- require 'polyamorous/polyamorous'
3
- require 'sham'
4
- require 'faker'
5
1
  require 'ransack'
2
+ require 'factory_bot'
3
+ require 'faker'
6
4
  require 'action_controller'
7
5
  require 'ransack/helpers'
8
6
  require 'pry'
9
7
  require 'simplecov'
10
8
  require 'byebug'
9
+ require 'rspec'
11
10
 
12
11
  SimpleCov.start
13
12
  I18n.enforce_available_locales = false
14
13
  Time.zone = 'Eastern Time (US & Canada)'
15
14
  I18n.load_path += Dir[File.join(File.dirname(__FILE__), 'support', '*.yml')]
16
15
 
17
- Dir[File.expand_path('../{helpers,support,blueprints}/*.rb', __FILE__)]
16
+ Dir[File.expand_path('../{helpers,support,factories}/*.rb', __FILE__)]
18
17
  .each { |f| require f }
19
18
 
20
19
  Faker::Config.random = Random.new(0)
21
- Sham.define do
22
- name { Faker::Name.name }
23
- title { Faker::Lorem.sentence }
24
- body { Faker::Lorem.paragraph }
25
- salary { |index| 30000 + (index * 1000) }
26
- tag_name { Faker::Lorem.words(number: 3).join(' ') }
27
- note { Faker::Lorem.words(number: 7).join(' ') }
28
- only_admin { Faker::Lorem.words(number: 3).join(' ') }
29
- only_search { Faker::Lorem.words(number: 3).join(' ') }
30
- only_sort { Faker::Lorem.words(number: 3).join(' ') }
31
- notable_id { |id| id }
32
- end
33
20
 
34
21
  RSpec.configure do |config|
35
22
  config.alias_it_should_behave_like_to :it_has_behavior, 'has behavior'
23
+
24
+ config.include FactoryBot::Syntax::Methods
36
25
 
37
26
  config.before(:suite) do
38
27
  message = "Running Ransack specs with #{
@@ -42,12 +31,9 @@ RSpec.configure do |config|
42
31
  line = '=' * message.length
43
32
  puts line, message, line
44
33
  Schema.create
45
- SubDB::Schema.create
34
+ SubDB::Schema.create if defined?(SubDB)
46
35
  end
47
36
 
48
- config.before(:all) { Sham.reset(:before_all) }
49
- config.before(:each) { Sham.reset(:before_each) }
50
-
51
37
  config.include RansackHelper
52
38
  config.include PolyamorousHelper
53
39
  end
@@ -1,4 +1,5 @@
1
1
  require 'active_record'
2
+ require 'activerecord-postgis-adapter'
2
3
 
3
4
  case ENV['DB'].try(:downcase)
4
5
  when 'mysql', 'mysql2'
@@ -20,6 +21,17 @@ when 'pg', 'postgres', 'postgresql'
20
21
  host: ENV.fetch("DATABASE_HOST") { "localhost" },
21
22
  min_messages: 'warning'
22
23
  )
24
+ when 'postgis'
25
+ # To test with PostGIS: `DB=postgis bundle exec rake spec`
26
+ ActiveRecord::Base.establish_connection(
27
+ adapter: 'postgis',
28
+ postgis_extension: 'postgis',
29
+ database: 'ransack',
30
+ username: ENV.fetch("DATABASE_USERNAME") { "postgres" },
31
+ password: ENV.fetch("DATABASE_PASSWORD") { "" },
32
+ host: ENV.fetch("DATABASE_HOST") { "localhost" },
33
+ min_messages: 'warning'
34
+ )
23
35
  else
24
36
  # Otherwise, assume SQLite3: `bundle exec rake spec`
25
37
  ActiveRecord::Base.establish_connection(
@@ -104,7 +116,7 @@ class Person < ApplicationRecord
104
116
  )
105
117
  end
106
118
 
107
- ransacker :sql_literal_id do
119
+ ransacker :sql_literal_id, type: :integer do
108
120
  Arel.sql('people.id')
109
121
  end
110
122
 
@@ -126,6 +138,17 @@ class Person < ApplicationRecord
126
138
  Arel.sql(query)
127
139
  end
128
140
 
141
+ ransacker :article_tags, formatter: proc { |id|
142
+ if Tag.exists?(id)
143
+ joins(articles: :tags)
144
+ .where(tags: { id: id })
145
+ .distinct
146
+ .select(:id).arel
147
+ end
148
+ } do |parent|
149
+ parent.table[:id]
150
+ end
151
+
129
152
  def self.ransackable_attributes(auth_object = nil)
130
153
  if auth_object == :admin
131
154
  authorizable_ransackable_attributes - ['only_sort']
@@ -151,6 +174,7 @@ class Article < ApplicationRecord
151
174
  has_many :comments
152
175
  has_and_belongs_to_many :tags
153
176
  has_many :notes, as: :notable
177
+ has_many :recent_notes, as: :notable
154
178
 
155
179
  alias_attribute :content, :body
156
180
 
@@ -220,18 +244,28 @@ end
220
244
  class Comment < ApplicationRecord
221
245
  belongs_to :article
222
246
  belongs_to :person
247
+ has_and_belongs_to_many :tags
223
248
 
224
249
  default_scope { where(disabled: false) }
225
250
  end
226
251
 
227
252
  class Tag < ApplicationRecord
228
253
  has_and_belongs_to_many :articles
254
+ has_and_belongs_to_many :comments
229
255
  end
230
256
 
231
257
  class Note < ApplicationRecord
232
258
  belongs_to :notable, polymorphic: true
233
259
  end
234
260
 
261
+ class RecentNote < ApplicationRecord
262
+ DEFAULT_NOTABLE_ID = 1
263
+ self.table_name = "notes"
264
+ default_scope { where(notable_id: DEFAULT_NOTABLE_ID) }
265
+
266
+ belongs_to :notable, polymorphic: true
267
+ end
268
+
235
269
  class Account < ApplicationRecord
236
270
  belongs_to :agent_account, class_name: "Account"
237
271
  belongs_to :trade_account, class_name: "Account"
@@ -298,6 +332,11 @@ module Schema
298
332
  t.integer :tag_id
299
333
  end
300
334
 
335
+ create_table :comments_tags, force: true, id: false do |t|
336
+ t.integer :comment_id
337
+ t.integer :tag_id
338
+ end
339
+
301
340
  create_table :notes, force: true do |t|
302
341
  t.integer :notable_id
303
342
  t.string :notable_type
@@ -331,23 +370,23 @@ module Schema
331
370
  end
332
371
 
333
372
  10.times do
334
- person = Person.make
335
- Note.make(notable: person)
373
+ person = FactoryBot.create(:person)
374
+ FactoryBot.create(:note, :for_person, notable: person)
336
375
  3.times do
337
- article = Article.make(person: person)
376
+ article = FactoryBot.create(:article, person: person)
338
377
  3.times do
339
- article.tags = [Tag.make, Tag.make, Tag.make]
378
+ article.tags = [FactoryBot.create(:tag), FactoryBot.create(:tag), FactoryBot.create(:tag)]
340
379
  end
341
- Note.make(notable: article)
380
+ FactoryBot.create(:note, :for_article, notable: article)
342
381
  10.times do
343
- Comment.make(article: article, person: person)
382
+ FactoryBot.create(:comment, article: article, person: person)
344
383
  end
345
384
  end
346
385
  end
347
386
 
348
- Comment.make(
387
+ FactoryBot.create(:comment,
349
388
  body: 'First post!',
350
- article: Article.make(title: 'Hello, world!')
389
+ article: FactoryBot.create(:article, title: 'Hello, world!')
351
390
  )
352
391
  end
353
392
  end