sprig 0.1.6 → 0.3.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 (64) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +61 -10
  3. data/lib/generators/sprig/install_generator.rb +5 -1
  4. data/lib/sprig.rb +21 -0
  5. data/lib/sprig/configuration.rb +19 -2
  6. data/lib/sprig/directive.rb +3 -3
  7. data/lib/sprig/helpers.rb +15 -3
  8. data/lib/sprig/planter.rb +1 -1
  9. data/lib/sprig/process_notifier.rb +4 -4
  10. data/lib/sprig/seed/attribute.rb +27 -3
  11. data/lib/sprig/seed/entry.rb +8 -4
  12. data/lib/sprig/seed/factory.rb +1 -1
  13. data/lib/sprig/source.rb +10 -1
  14. data/lib/sprig/version.rb +8 -1
  15. data/spec/adapters/active_record.rb +34 -0
  16. data/spec/adapters/mongoid.rb +19 -0
  17. data/spec/adapters/mongoid.yml +12 -0
  18. data/spec/feature/configurations_spec.rb +5 -5
  19. data/spec/fixtures/models/{comment.rb → active_record/comment.rb} +0 -0
  20. data/spec/fixtures/models/{post.rb → active_record/post.rb} +0 -0
  21. data/spec/fixtures/models/{tag.rb → active_record/tag.rb} +0 -0
  22. data/spec/fixtures/models/{user.rb → active_record/user.rb} +0 -0
  23. data/spec/fixtures/models/mongoid/comment.rb +9 -0
  24. data/spec/fixtures/models/mongoid/post.rb +18 -0
  25. data/spec/fixtures/models/mongoid/tag.rb +7 -0
  26. data/spec/fixtures/models/mongoid/user.rb +9 -0
  27. data/spec/fixtures/seeds/shared/comments.yml +4 -0
  28. data/spec/fixtures/seeds/shared/files/cat.png +0 -0
  29. data/spec/fixtures/seeds/shared/invalid_users.yml +4 -0
  30. data/spec/fixtures/seeds/shared/legacy_posts.yml +4 -0
  31. data/spec/fixtures/seeds/shared/posts.csv +2 -0
  32. data/spec/fixtures/seeds/shared/posts.json +1 -0
  33. data/spec/fixtures/seeds/shared/posts.md +5 -0
  34. data/spec/fixtures/seeds/shared/posts.yml +4 -0
  35. data/spec/fixtures/seeds/shared/posts_delete_existing_by.yml +7 -0
  36. data/spec/fixtures/seeds/shared/posts_find_existing_by_missing.yml +7 -0
  37. data/spec/fixtures/seeds/shared/posts_find_existing_by_multiple.yml +8 -0
  38. data/spec/fixtures/seeds/shared/posts_find_existing_by_single.yml +7 -0
  39. data/spec/fixtures/seeds/shared/posts_missing_dependency.yml +5 -0
  40. data/spec/fixtures/seeds/shared/posts_missing_record.yml +5 -0
  41. data/spec/fixtures/seeds/shared/posts_partially_dynamic_value.yml +4 -0
  42. data/spec/fixtures/seeds/shared/posts_with_cyclic_dependencies.yml +4 -0
  43. data/spec/fixtures/seeds/shared/posts_with_files.yml +5 -0
  44. data/spec/fixtures/seeds/shared/posts_with_habtm.yml +7 -0
  45. data/spec/fixtures/seeds/shared/tags.yml +5 -0
  46. data/spec/fixtures/seeds/test/posts_partially_dynamic_value.yml +4 -0
  47. data/spec/lib/generators/sprig/install_generator_spec.rb +6 -2
  48. data/spec/lib/sprig/configuration_spec.rb +6 -6
  49. data/spec/lib/sprig/directive_list_spec.rb +4 -4
  50. data/spec/lib/sprig/directive_spec.rb +21 -11
  51. data/spec/lib/sprig/null_record_spec.rb +2 -2
  52. data/spec/lib/sprig/parser/base_spec.rb +1 -1
  53. data/spec/lib/sprig/process_notifier_spec.rb +5 -3
  54. data/spec/lib/sprig/seed/entry_spec.rb +3 -3
  55. data/spec/lib/sprig/seed/record_spec.rb +3 -3
  56. data/spec/lib/sprig/source_spec.rb +2 -2
  57. data/spec/lib/sprig_spec.rb +59 -8
  58. data/spec/spec_helper.rb +35 -31
  59. data/spec/sprig_shared_spec.rb +467 -0
  60. data/spec/sprig_spec.rb +105 -34
  61. data/spec/support/helpers/logger_mock.rb +2 -1
  62. metadata +167 -87
  63. data/lib/sprig/data.rb +0 -6
  64. data/spec/db/activerecord.db +0 -0
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Sprig::NullRecord do
3
+ RSpec.describe Sprig::NullRecord do
4
4
  let(:error_msg) { 'Something bad happened.' }
5
5
  subject { described_class.new(error_msg) }
6
6
 
@@ -14,7 +14,7 @@ describe Sprig::NullRecord do
14
14
 
15
15
  describe "#method_missing" do
16
16
  it "returns nil for undefined method calls" do
17
- subject.enhance_your_calm.should == nil
17
+ expect(subject.enhance_your_calm).to eq(nil)
18
18
  end
19
19
  end
20
20
 
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Sprig::Parser::Base do
3
+ RSpec.describe Sprig::Parser::Base do
4
4
  describe "#parse" do
5
5
  it "enforces implementation in a subclass" do
6
6
  expect {
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Sprig::ProcessNotifier do
3
+ RSpec.describe Sprig::ProcessNotifier do
4
4
  it_behaves_like "a logging entity" do
5
5
  subject { described_class.new }
6
6
  end
@@ -79,10 +79,12 @@ describe Sprig::ProcessNotifier do
79
79
  end
80
80
 
81
81
  describe "#in_progress" do
82
+ let(:seed) { Sprig::Seed::Entry.new(Post, { title: "Hello World!", content: "Stuff", sprig_id: 1 }, {}) }
83
+
82
84
  it "logs an in-progress message" do
83
- log_should_receive(:debug, with: "Planting those seeds...\r")
85
+ log_should_receive(:debug, with: "Planting Post with sprig_id 1")
84
86
 
85
- subject.in_progress
87
+ subject.in_progress(seed)
86
88
  end
87
89
  end
88
90
  end
@@ -1,13 +1,13 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Sprig::Seed::Entry do
3
+ RSpec.describe Sprig::Seed::Entry do
4
4
  describe ".success_log_text" do
5
5
  context "on a new record" do
6
6
  it "indicates the record was 'saved'" do
7
7
  subject = described_class.new(Post, { title: "Hello World!", content: "Stuff", sprig_id: 1 }, {})
8
8
  subject.save_record
9
9
 
10
- subject.success_log_text.should == "Post with sprig_id 1 successfully saved."
10
+ expect(subject.success_log_text).to eq("Saved")
11
11
  end
12
12
  end
13
13
 
@@ -24,7 +24,7 @@ describe Sprig::Seed::Entry do
24
24
  subject = described_class.new(Post, { title: "Existing title", content: "Existing content", sprig_id: 1 }, { find_existing_by: [:title] })
25
25
  subject.save_record
26
26
 
27
- subject.success_log_text.should == "Post with sprig_id 1 successfully updated."
27
+ expect(subject.success_log_text).to eq("Updated")
28
28
  end
29
29
  end
30
30
  end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Sprig::Seed::Record do
3
+ RSpec.describe Sprig::Seed::Record do
4
4
  describe ".existing?" do
5
5
  let!(:existing) do
6
6
  Post.create(
@@ -13,13 +13,13 @@ describe Sprig::Seed::Record do
13
13
  it "returns true if the record has already been saved to the database" do
14
14
  subject = described_class.new_or_existing(Post, { title: "Existing title" }, { title: "Existing title" })
15
15
 
16
- subject.existing?.should == true
16
+ expect(subject.existing?).to eq(true)
17
17
  end
18
18
 
19
19
  it "returns false if the record is new" do
20
20
  subject = described_class.new_or_existing(Post, { title: "New title" }, { title: "New title" })
21
21
 
22
- subject.existing?.should == false
22
+ expect(subject.existing?).to eq(false)
23
23
  end
24
24
  end
25
25
  end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Sprig::NullRecord do
3
+ RSpec.describe Sprig::NullRecord do
4
4
  let(:error_msg) { 'Something bad happened.' }
5
5
  subject { described_class.new(error_msg) }
6
6
 
@@ -14,7 +14,7 @@ describe Sprig::NullRecord do
14
14
 
15
15
  describe "#method_missing" do
16
16
  it "returns nil for undefined method calls" do
17
- subject.enhance_your_calm.should == nil
17
+ expect(subject.enhance_your_calm).to eq(nil)
18
18
  end
19
19
  end
20
20
 
@@ -1,16 +1,67 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Sprig do
3
+ RSpec.describe Sprig do
4
4
  let(:configuration) { double('Configuration') }
5
5
 
6
6
  before do
7
- Sprig::Configuration.stub(:new).and_return(configuration)
7
+ allow(Sprig::Configuration).to receive(:new).and_return(configuration)
8
+ end
9
+
10
+ describe '.adapter' do
11
+ it { expect(described_class).to respond_to(:adapter).with(0).arguments }
12
+
13
+ it { expect(described_class.adapter).not_to be nil }
14
+ end
15
+
16
+ describe '.adapter=' do
17
+ let(:value) { :mongo_mapper }
18
+
19
+ around(:each) do |example|
20
+ previous_value = described_class.adapter
21
+
22
+ example.run
23
+
24
+ described_class.adapter = previous_value
25
+ end
26
+
27
+ it { expect(described_class).to respond_to(:adapter=).with(1).argument }
28
+
29
+ it 'changes the value' do
30
+ expect { described_class.adapter = value }.to change(described_class, :adapter).to(value)
31
+ end
32
+ end
33
+
34
+ describe '.adapter_model_class' do
35
+ let(:expected_class) do
36
+ case Sprig.adapter
37
+ when :active_record
38
+ ActiveRecord::Base
39
+ when :mongoid
40
+ Mongoid::Document
41
+ end
42
+ end
43
+
44
+ before(:each) { described_class.instance_variable_set(:@adapter_model_class, nil) }
45
+
46
+ it { expect(described_class).to respond_to(:adapter_model_class).with(0).arguments }
47
+
48
+ it { expect(described_class.adapter_model_class).to be expected_class }
49
+
50
+ describe 'with an unrecognized adapter' do
51
+ let(:value) { :mongo_mapper }
52
+
53
+ it 'raises an error' do
54
+ allow(described_class).to receive(:adapter).and_return(value)
55
+
56
+ expect { described_class.adapter_model_class }.to raise_error(/unknown model class/i)
57
+ end
58
+ end
8
59
  end
9
60
 
10
61
  describe ".configuration" do
11
62
  context "when there is not yet a Configuration instance" do
12
63
  it "returns a new Configuration instance" do
13
- described_class.configuration.should == configuration
64
+ expect(described_class.configuration).to eq(configuration)
14
65
  end
15
66
  end
16
67
 
@@ -21,9 +72,9 @@ describe Sprig do
21
72
 
22
73
  it "returns the existing Configuration instance" do
23
74
  new_configuration = double('Configuration')
24
- Sprig::Configuration.stub(:new).and_return(new_configuration)
75
+ allow(Sprig::Configuration).to receive(:new).and_return(new_configuration)
25
76
 
26
- described_class.configuration.should == configuration
77
+ expect(described_class.configuration).to eq(configuration)
27
78
  end
28
79
  end
29
80
  end
@@ -36,16 +87,16 @@ describe Sprig do
36
87
  it "clears the existing configuration" do
37
88
  described_class.reset_configuration
38
89
  new_configuration = double('Configuration')
39
- Sprig::Configuration.stub(:new).and_return(new_configuration)
90
+ allow(Sprig::Configuration).to receive(:new).and_return(new_configuration)
40
91
 
41
- described_class.configuration.should == new_configuration
92
+ expect(described_class.configuration).to eq(new_configuration)
42
93
  end
43
94
  end
44
95
 
45
96
  describe ".configure" do
46
97
  it "yields the configuration" do
47
98
  described_class.configure do |config|
48
- config.should == configuration
99
+ expect(config).to eq(configuration)
49
100
  end
50
101
  end
51
102
  end
data/spec/spec_helper.rb CHANGED
@@ -7,8 +7,6 @@ SimpleCov.formatter = Coveralls::SimpleCov::Formatter
7
7
  SimpleCov.start "rails"
8
8
 
9
9
  require "rails"
10
- require "active_record"
11
- require "database_cleaner"
12
10
  require "webmock"
13
11
  require "vcr"
14
12
  require "pry"
@@ -17,25 +15,21 @@ require "generator_spec"
17
15
  require "sprig"
18
16
  include Sprig::Helpers
19
17
 
20
- Dir[File.dirname(__FILE__) + '/fixtures/models/*.rb'].each {|file| require file }
21
18
  Dir[File.dirname(__FILE__) + '/support/**/*.rb'].each {|file| require file}
22
19
 
23
20
  RSpec.configure do |c|
24
21
  c.include ColoredText
25
22
  c.include LoggerMock
26
23
 
27
- c.before(:suite) do
28
- DatabaseCleaner.strategy = :transaction
29
- DatabaseCleaner.clean_with(:truncation)
30
- end
24
+ c.disable_monkey_patching!
25
+
26
+ c.order = :random
31
27
 
32
- c.before(:each) do
33
- DatabaseCleaner.start
28
+ c.mock_with :rspec do |mocks|
29
+ mocks.verify_partial_doubles = true
34
30
  end
35
31
 
36
32
  c.after(:each) do
37
- DatabaseCleaner.clean
38
-
39
33
  Sprig.reset_configuration
40
34
  end
41
35
  end
@@ -46,51 +40,61 @@ VCR.configure do |c|
46
40
  c.hook_into :webmock
47
41
  end
48
42
 
49
- # Database
50
- ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => "spec/db/activerecord.db")
43
+ # ActiveRecord (via SQlite3)
44
+ begin
45
+ require 'active_record'
46
+ require 'sqlite3'
51
47
 
52
- User.connection.execute "DROP TABLE IF EXISTS users;"
53
- User.connection.execute "CREATE TABLE users (id INTEGER PRIMARY KEY , first_name VARCHAR(255), last_name VARCHAR(255), type VARCHAR(255));"
48
+ Sprig.adapter = :active_record
49
+ rescue LoadError; end
54
50
 
55
- Post.connection.execute "DROP TABLE IF EXISTS posts;"
56
- Post.connection.execute "CREATE TABLE posts (id INTEGER PRIMARY KEY AUTOINCREMENT, title VARCHAR(255), content VARCHAR(255), photo VARCHAR(255), published BOOLEAN , user_id INTEGER);"
51
+ # Mongoid
52
+ begin
53
+ require 'mongoid'
57
54
 
58
- Comment.connection.execute "DROP TABLE IF EXISTS comments;"
59
- Comment.connection.execute "CREATE TABLE comments (id INTEGER PRIMARY KEY , post_id INTEGER, body VARCHAR(255));"
55
+ Sprig.adapter = :mongoid
56
+ rescue LoadError; end
60
57
 
61
- Tag.connection.execute "DROP TABLE IF EXISTS tags;"
62
- Tag.connection.execute "CREATE TABLE tags (id INTEGER PRIMARY KEY , name VARCHAR(255));"
58
+ # Require model files.
59
+ Dir[File.dirname(__FILE__) + "/fixtures/models/#{Sprig.adapter}/*.rb"].each {|file| require file}
63
60
 
64
- Tag.connection.execute "DROP TABLE IF EXISTS posts_tags;"
65
- Tag.connection.execute "CREATE TABLE posts_tags (id INTEGER PRIMARY KEY , post_id INTEGER, tag_id INTEGER);"
61
+ require "adapters/#{Sprig.adapter}.rb"
66
62
 
67
63
  # Helpers
68
64
  #
69
65
  # Setup fake `Rails.root`
70
66
  def stub_rails_root(path='./spec/fixtures')
71
- Rails.stub(:root).and_return(Pathname.new(path))
67
+ allow(Rails).to receive(:root).and_return(Pathname.new(path))
72
68
  end
73
69
 
74
70
  # Setup fake `Rails.env`
75
71
  def stub_rails_env(env='development')
76
- Rails.stub(:env).and_return(env)
72
+ allow(Rails).to receive(:env).and_return(env)
77
73
  end
78
74
 
79
75
  # Copy and Remove Seed files around a spec
80
- def load_seeds(*files)
76
+ def load_seeds(*files, &block)
81
77
  env = Rails.env
78
+ prepare_seeds(env, *files, &block)
79
+ end
80
+
81
+ # Copy and Remove shared seed files around a spec
82
+ def load_shared_seeds(*files, &block)
83
+ prepare_seeds('shared', *files, &block)
84
+ end
82
85
 
83
- `cp -R ./spec/fixtures/seeds/#{env}/files ./spec/fixtures/db/seeds/#{env}`
86
+ def prepare_seeds(directory, *files, &block)
87
+ `cp -R ./spec/fixtures/seeds/#{directory}/files ./spec/fixtures/db/seeds/#{directory}`
84
88
 
85
89
  files.each do |file|
86
- `cp ./spec/fixtures/seeds/#{env}/#{file} ./spec/fixtures/db/seeds/#{env}`
90
+ `cp ./spec/fixtures/seeds/#{directory}/#{file} ./spec/fixtures/db/seeds/#{directory}`
87
91
  end
88
92
 
89
- yield
93
+ block.call
90
94
 
91
- `rm -R ./spec/fixtures/db/seeds/#{env}/files`
95
+ `rm -R ./spec/fixtures/db/seeds/#{directory}/files`
92
96
 
93
97
  files.each do |file|
94
- `rm ./spec/fixtures/db/seeds/#{env}/#{file}`
98
+ `rm ./spec/fixtures/db/seeds/#{directory}/#{file}`
95
99
  end
96
100
  end
@@ -0,0 +1,467 @@
1
+ require 'spec_helper'
2
+ require 'open-uri'
3
+
4
+ RSpec.describe "Seeding an application with shared seeds" do
5
+ let(:missing_record_error) do
6
+ if defined?(ActiveRecord) && Post < ActiveRecord::Base
7
+ ActiveRecord::RecordNotFound
8
+ elsif defined?(Mongoid) && Post < Mongoid::Document
9
+ Mongoid::Errors::DocumentNotFound
10
+ end
11
+ end
12
+
13
+ before do
14
+ stub_rails_root
15
+ end
16
+
17
+ context "with a yaml file" do
18
+ around do |example|
19
+ load_shared_seeds('posts.yml', &example)
20
+ end
21
+
22
+ it "seeds the db" do
23
+ sprig_shared [Post]
24
+
25
+ expect(Post.count).to eq(1)
26
+ expect(Post.pluck(:title)).to eq(['Yaml title'])
27
+ end
28
+ end
29
+
30
+ context "with a csv file" do
31
+ around do |example|
32
+ load_shared_seeds('posts.csv', &example)
33
+ end
34
+
35
+ it "seeds the db" do
36
+ sprig_shared [Post]
37
+
38
+ expect(Post.count).to eq(1)
39
+ expect(Post.pluck(:title)).to eq(['Csv title'])
40
+ end
41
+ end
42
+
43
+ context "with a json file" do
44
+ around do |example|
45
+ load_shared_seeds('posts.json', &example)
46
+ end
47
+
48
+ it "seeds the db" do
49
+ sprig_shared [Post]
50
+
51
+ expect(Post.count).to eq(1)
52
+ expect(Post.pluck(:title)).to eq(['Json title'])
53
+ end
54
+ end
55
+
56
+ context "with a partially-dynamic value" do
57
+ around do |example|
58
+ load_shared_seeds('posts_partially_dynamic_value.yml', &example)
59
+ end
60
+
61
+ it "seeds the db with the full value" do
62
+ sprig_shared [
63
+ {
64
+ :class => Post,
65
+ :source => open('spec/fixtures/seeds/shared/posts_partially_dynamic_value.yml')
66
+ }
67
+ ]
68
+
69
+ expect(Post.count).to eq(1)
70
+ expect(Post.pluck(:title)).to eq(['Partially Dynamic Title'])
71
+ end
72
+ end
73
+
74
+ context "with a symlinked file" do
75
+ around do |example|
76
+ `ln -s ./spec/fixtures/seeds/shared/posts.yml ./spec/fixtures/db/seeds/shared/`
77
+ example.call
78
+ `rm ./spec/fixtures/db/seeds/shared/posts.yml`
79
+ end
80
+
81
+ it "seeds the db" do
82
+ sprig_shared [Post]
83
+
84
+ expect(Post.count).to eq(1)
85
+ expect(Post.pluck(:title)).to eq(['Yaml title'])
86
+ end
87
+ end
88
+
89
+ context "with a google spreadsheet" do
90
+ it "seeds the db", :vcr => { :cassette_name => 'google_spreadsheet_json_posts' } do
91
+ sprig_shared [
92
+ {
93
+ :class => Post,
94
+ :parser => Sprig::Parser::GoogleSpreadsheetJson,
95
+ :source => open('https://spreadsheets.google.com/feeds/list/0AjVLPMnHm86rdDVHQ2dCUS03RTN5ZUtVNzVOYVBwT0E/1/public/values?alt=json'),
96
+ }
97
+ ]
98
+
99
+ expect(Post.count).to eq(1)
100
+ expect(Post.pluck(:title)).to eq(['Google spreadsheet json title'])
101
+ end
102
+ end
103
+
104
+ context "with an invalid custom parser" do
105
+ around do |example|
106
+ load_shared_seeds('posts.yml', &example)
107
+ end
108
+
109
+ it "fails with an argument error" do
110
+ expect {
111
+ sprig_shared [
112
+ {
113
+ :class => Post,
114
+ :source => open('spec/fixtures/seeds/shared/posts.yml'),
115
+ :parser => Object # Not a valid parser
116
+ }
117
+ ]
118
+ }.to raise_error(ArgumentError, 'Parsers must define #parse.')
119
+ end
120
+ end
121
+
122
+ context "with a custom source" do
123
+ around do |example|
124
+ load_shared_seeds('legacy_posts.yml', &example)
125
+ end
126
+
127
+ it "seeds" do
128
+ sprig_shared [
129
+ {
130
+ :class => Post,
131
+ :source => open('spec/fixtures/seeds/shared/legacy_posts.yml')
132
+ }
133
+ ]
134
+
135
+ expect(Post.count).to eq(1)
136
+ expect(Post.pluck(:title)).to eq(['Legacy yaml title'])
137
+ end
138
+ end
139
+
140
+ context "with a custom source that cannot be parsed by native parsers" do
141
+ around do |example|
142
+ load_shared_seeds('posts.md', &example)
143
+ end
144
+
145
+ it "fails with an unparsable file error" do
146
+ expect {
147
+ sprig_shared [
148
+ {
149
+ :class => Post,
150
+ :source => open('spec/fixtures/seeds/shared/posts.md')
151
+ }
152
+ ]
153
+ }.to raise_error(Sprig::Source::ParserDeterminer::UnparsableFileError)
154
+ end
155
+ end
156
+
157
+ context "with an invalid custom source" do
158
+ it "fails with an argument error" do
159
+ expect {
160
+ sprig_shared [ { :class => Post, :source => 42 } ]
161
+ }.to raise_error(ArgumentError, 'Data sources must act like an IO.')
162
+ end
163
+ end
164
+
165
+ context "with multiple file relationships" do
166
+ around do |example|
167
+ load_shared_seeds('posts.yml', 'comments.yml', &example)
168
+ end
169
+
170
+ it "seeds the db" do
171
+ sprig_shared [Post, Comment]
172
+
173
+ expect(Post.count).to eq(1)
174
+ expect(Comment.count).to eq(1)
175
+ expect(Comment.first.post).to eq(Post.first)
176
+ end
177
+ end
178
+
179
+ context "with missing seed files" do
180
+ it "raises a missing file error" do
181
+ expect {
182
+ sprig_shared [Post]
183
+ }.to raise_error(Sprig::Source::SourceDeterminer::FileNotFoundError)
184
+ end
185
+ end
186
+
187
+ context "with a relationship to an undefined record" do
188
+ around do |example|
189
+ load_shared_seeds('posts.yml', 'posts_missing_dependency.yml', &example)
190
+ end
191
+
192
+ it "raises a helpful error message" do
193
+ expect {
194
+ sprig_shared [
195
+ {
196
+ :class => Post,
197
+ :source => open('spec/fixtures/seeds/shared/posts_missing_dependency.yml')
198
+ }
199
+ ]
200
+ }.to raise_error(
201
+ Sprig::DependencySorter::MissingDependencyError,
202
+ "Undefined reference to 'sprig_record(Comment, 42)'"
203
+ )
204
+ end
205
+ end
206
+
207
+ context "with a relationship to a record that didn't save" do
208
+ around do |example|
209
+ load_shared_seeds('invalid_users.yml', 'posts_missing_record.yml', &example)
210
+ end
211
+
212
+ it "does not error, but carries on with the seeding" do
213
+ expect {
214
+ sprig_shared [
215
+ {
216
+ :class => Post,
217
+ :source => open('spec/fixtures/seeds/shared/posts_missing_record.yml')
218
+ },
219
+ {
220
+ :class => User,
221
+ :source => open('spec/fixtures/seeds/shared/invalid_users.yml')
222
+ }
223
+ ]
224
+ }.to_not raise_error
225
+ end
226
+ end
227
+
228
+ context "with multiple files for a class" do
229
+ around do |example|
230
+ load_shared_seeds('posts.yml', 'legacy_posts.yml', &example)
231
+ end
232
+
233
+ it "seeds the db" do
234
+ sprig_shared [
235
+ Post,
236
+ {
237
+ :class => Post,
238
+ :source => open('spec/fixtures/seeds/shared/legacy_posts.yml')
239
+ }
240
+ ]
241
+
242
+ expect(Post.count).to eq(2)
243
+ expect(Post.pluck(:title)).to eq(['Yaml title', 'Legacy yaml title'])
244
+ end
245
+ end
246
+
247
+ context "with files defined as attributes" do
248
+ around do |example|
249
+ load_shared_seeds('posts_with_files.yml', &example)
250
+ end
251
+
252
+ it "seeds the db" do
253
+ sprig_shared [
254
+ {
255
+ :class => Post,
256
+ :source => open('spec/fixtures/seeds/shared/posts_with_files.yml')
257
+ }
258
+ ]
259
+
260
+ expect(Post.count).to eq(1)
261
+ expect(Post.pluck(:photo)).to eq(['cat.png'])
262
+ end
263
+ end
264
+
265
+ context "with has_and_belongs_to_many relationships" do
266
+ around do |example|
267
+ load_shared_seeds('posts_with_habtm.yml', 'tags.yml', &example)
268
+ end
269
+
270
+ it "saves the habtm relationships" do
271
+ sprig_shared [
272
+ Tag,
273
+ {
274
+ :class => Post,
275
+ :source => open('spec/fixtures/seeds/shared/posts_with_habtm.yml')
276
+ }
277
+ ]
278
+
279
+ expect(Post.first.tags.map(&:name)).to eq(['Botany', 'Biology'])
280
+ end
281
+ end
282
+
283
+ context "with cyclic dependencies" do
284
+ around do |example|
285
+ load_shared_seeds('comments.yml', 'posts_with_cyclic_dependencies.yml', &example)
286
+ end
287
+
288
+ it "raises an cyclic dependency error" do
289
+ expect {
290
+ sprig_shared [
291
+ {
292
+ :class => Post,
293
+ :source => open('spec/fixtures/seeds/shared/posts_with_cyclic_dependencies.yml')
294
+ },
295
+ Comment
296
+ ]
297
+ }.to raise_error(Sprig::DependencySorter::CircularDependencyError)
298
+ end
299
+ end
300
+
301
+ context "with a malformed directive" do
302
+ let(:expected_error_message) { "Sprig::Directive must be instantiated with a(n) #{Sprig.adapter_model_class} class or a Hash with :class defined" }
303
+
304
+ context "including a class that is not a subclass of AR" do
305
+ it "raises an argument error" do
306
+ expect {
307
+ sprig_shared [
308
+ Object
309
+ ]
310
+ }.to raise_error(ArgumentError, expected_error_message)
311
+ end
312
+ end
313
+
314
+ context "including a non-class, non-hash" do
315
+ it "raises an argument error" do
316
+ expect {
317
+ sprig_shared [
318
+ 42
319
+ ]
320
+ }.to raise_error(ArgumentError, expected_error_message)
321
+ end
322
+ end
323
+ end
324
+
325
+
326
+ context "with custom seed options" do
327
+ context "using delete_existing_by" do
328
+ around do |example|
329
+ load_shared_seeds('posts_delete_existing_by.yml', &example)
330
+ end
331
+
332
+ context "with an existing record" do
333
+ let!(:existing_match) do
334
+ Post.create(
335
+ :title => "Such Title",
336
+ :content => "Old Content")
337
+ end
338
+
339
+ let!(:existing_nonmatch) do
340
+ Post.create(
341
+ :title => "Wow Title",
342
+ :content => "Much Content")
343
+ end
344
+
345
+ it "replaces only the matching existing record" do
346
+ sprig_shared [
347
+ {
348
+ :class => Post,
349
+ :source => open("spec/fixtures/seeds/shared/posts_delete_existing_by.yml")
350
+ }
351
+ ]
352
+
353
+ expect(Post.count).to eq(2)
354
+
355
+ expect {
356
+ existing_match.reload
357
+ }.to raise_error(missing_record_error)
358
+
359
+ expect {
360
+ existing_nonmatch.reload
361
+ }.to_not raise_error
362
+ end
363
+ end
364
+ end
365
+
366
+ context "using find_existing_by" do
367
+ context "with a missing attribute" do
368
+ around do |example|
369
+ load_shared_seeds('posts_find_existing_by_missing.yml', &example)
370
+ end
371
+
372
+ it "raises a missing attribute error" do
373
+ expect {
374
+ sprig_shared [
375
+ {
376
+ :class => Post,
377
+ :source => open("spec/fixtures/seeds/shared/posts_find_existing_by_missing.yml")
378
+ }
379
+ ]
380
+ }.to raise_error(Sprig::Seed::AttributeCollection::AttributeNotFoundError, "Attribute 'unicorn' is not present.")
381
+ end
382
+ end
383
+
384
+ context "with a single attribute" do
385
+ around do |example|
386
+ load_shared_seeds('posts.yml', 'posts_find_existing_by_single.yml', &example)
387
+ end
388
+
389
+ context "with an existing record" do
390
+ let!(:existing) do
391
+ Post.create(
392
+ :title => "Existing title",
393
+ :content => "Existing content")
394
+ end
395
+
396
+ it "updates the existing record" do
397
+ sprig_shared [
398
+ {
399
+ :class => Post,
400
+ :source => open("spec/fixtures/seeds/shared/posts_find_existing_by_single.yml")
401
+ }
402
+ ]
403
+
404
+ expect(Post.count).to eq(1)
405
+ expect(existing.reload.content).to eq("Updated content")
406
+ end
407
+ end
408
+ end
409
+
410
+ context "with multiple attributes" do
411
+ around do |example|
412
+ load_shared_seeds('posts.yml', 'posts_find_existing_by_multiple.yml', &example)
413
+ end
414
+
415
+ context "with an existing record" do
416
+ let!(:existing) do
417
+ Post.create(
418
+ :title => "Existing title",
419
+ :content => "Existing content",
420
+ :published => false
421
+ )
422
+ end
423
+
424
+ it "updates the existing record" do
425
+ sprig_shared [
426
+ {
427
+ :class => Post,
428
+ :source => open("spec/fixtures/seeds/shared/posts_find_existing_by_multiple.yml")
429
+ }
430
+ ]
431
+
432
+ expect(Post.count).to eq(1)
433
+ expect(existing.reload.published).to eq(true)
434
+ end
435
+ end
436
+ end
437
+ end
438
+
439
+ context "defined within the directive" do
440
+ let!(:existing) do
441
+ Post.create(
442
+ :title => "Yaml title",
443
+ :content => "Existing content")
444
+ end
445
+
446
+ around do |example|
447
+ load_shared_seeds('posts.yml', &example)
448
+ end
449
+
450
+ it "respects the directive option" do
451
+ sprig_shared [
452
+ {
453
+ :class => Post,
454
+ :source => open("spec/fixtures/seeds/shared/posts.yml"),
455
+ :delete_existing_by => :title
456
+ }
457
+ ]
458
+
459
+ expect(Post.count).to eq(1)
460
+
461
+ expect {
462
+ existing.reload
463
+ }.to raise_error(missing_record_error)
464
+ end
465
+ end
466
+ end
467
+ end