config_scripts 0.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 (83) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.rspec +2 -0
  4. data/.yardopts +1 -0
  5. data/CHANGELOG.md +17 -0
  6. data/Gemfile +4 -0
  7. data/Gemfile.lock +124 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +55 -0
  10. data/Rakefile +1 -0
  11. data/TODO.md +8 -0
  12. data/config_scripts.gemspec +31 -0
  13. data/lib/config_scripts/generators/config_script.rb +24 -0
  14. data/lib/config_scripts/generators/migrations.rb +36 -0
  15. data/lib/config_scripts/generators.rb +2 -0
  16. data/lib/config_scripts/scripts/script.rb +135 -0
  17. data/lib/config_scripts/scripts/script_history.rb +39 -0
  18. data/lib/config_scripts/scripts.rb +9 -0
  19. data/lib/config_scripts/seeds/seed_set.rb +321 -0
  20. data/lib/config_scripts/seeds/seed_type.rb +361 -0
  21. data/lib/config_scripts/seeds.rb +8 -0
  22. data/lib/config_scripts/tasks/pending_migrations.rake +11 -0
  23. data/lib/config_scripts/tasks/seeds.rake +18 -0
  24. data/lib/config_scripts/tasks.rb +12 -0
  25. data/lib/config_scripts/version.rb +4 -0
  26. data/lib/config_scripts.rb +9 -0
  27. data/spec/dummy/README.rdoc +28 -0
  28. data/spec/dummy/Rakefile +6 -0
  29. data/spec/dummy/app/assets/images/.keep +0 -0
  30. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  31. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  32. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  33. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  34. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  35. data/spec/dummy/app/mailers/.keep +0 -0
  36. data/spec/dummy/app/models/.keep +0 -0
  37. data/spec/dummy/app/models/concerns/.keep +0 -0
  38. data/spec/dummy/app/models/hair_color.rb +2 -0
  39. data/spec/dummy/app/models/person.rb +4 -0
  40. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  41. data/spec/dummy/bin/bundle +3 -0
  42. data/spec/dummy/bin/rails +4 -0
  43. data/spec/dummy/bin/rake +4 -0
  44. data/spec/dummy/config/application.rb +23 -0
  45. data/spec/dummy/config/boot.rb +5 -0
  46. data/spec/dummy/config/database.yml +25 -0
  47. data/spec/dummy/config/environment.rb +5 -0
  48. data/spec/dummy/config/environments/development.rb +29 -0
  49. data/spec/dummy/config/environments/production.rb +80 -0
  50. data/spec/dummy/config/environments/test.rb +36 -0
  51. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  52. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  53. data/spec/dummy/config/initializers/inflections.rb +16 -0
  54. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  55. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  56. data/spec/dummy/config/initializers/session_store.rb +3 -0
  57. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  58. data/spec/dummy/config/locales/en.yml +23 -0
  59. data/spec/dummy/config/routes.rb +56 -0
  60. data/spec/dummy/config.ru +4 -0
  61. data/spec/dummy/db/migrate/20140208014550_create_config_scripts.rb +7 -0
  62. data/spec/dummy/db/migrate/20140208161829_create_people.rb +9 -0
  63. data/spec/dummy/db/migrate/20140208182050_create_hair_colors.rb +9 -0
  64. data/spec/dummy/db/migrate/20140208182101_add_hair_color_to_person.rb +6 -0
  65. data/spec/dummy/db/migrate/20140208225801_add_scope_to_people.rb +6 -0
  66. data/spec/dummy/db/migrate/20140209132911_add_hex_value_to_hair_color.rb +5 -0
  67. data/spec/dummy/db/schema.rb +38 -0
  68. data/spec/dummy/lib/assets/.keep +0 -0
  69. data/spec/dummy/log/.keep +0 -0
  70. data/spec/dummy/public/404.html +58 -0
  71. data/spec/dummy/public/422.html +58 -0
  72. data/spec/dummy/public/500.html +57 -0
  73. data/spec/dummy/public/favicon.ico +0 -0
  74. data/spec/generators/config_script_spec.rb +23 -0
  75. data/spec/generators/migrations_spec.rb +23 -0
  76. data/spec/scripts/script_history_spec.rb +53 -0
  77. data/spec/scripts/script_spec.rb +282 -0
  78. data/spec/seeds/seed_set_spec.rb +371 -0
  79. data/spec/seeds/seed_type_spec.rb +439 -0
  80. data/spec/spec_helper.rb +38 -0
  81. data/templates/config_script.rb +9 -0
  82. data/templates/create_config_scripts_migration.rb +7 -0
  83. metadata +321 -0
@@ -0,0 +1,439 @@
1
+ describe ConfigScripts::Seeds::SeedType do
2
+ let(:seed_set) { ConfigScripts::Seeds::SeedSet.new('test_seeds', 1) }
3
+ let(:seed_type) { ConfigScripts::Seeds::SeedType.new(seed_set, Person, 'people') }
4
+
5
+ describe "DSL" do
6
+ describe "creation" do
7
+ subject { ConfigScripts::Seeds::SeedType.new(seed_set, Person, 'people') { @val = true} }
8
+
9
+ it "sets the attributes from the arguments" do
10
+ expect(subject.seed_set).to eq seed_set
11
+ expect(subject.klass).to eq Person
12
+ expect(subject.filename).to eq 'people'
13
+ expect(subject.instance_eval{@val}).to eq true
14
+ end
15
+
16
+ it "gives it an empty list of attributes" do
17
+ expect(subject.attributes).to eq []
18
+ end
19
+
20
+ it "gives it the id as the identifier attributes" do
21
+ expect(subject.identifier_attributes).to eq [:id]
22
+ end
23
+
24
+ it "records the associations for the model" do
25
+ expect(subject.associations).to eq hair_color: HairColor, scope: nil
26
+ end
27
+ end
28
+
29
+ describe "has_attributes" do
30
+ it "adds the arguments to their list of attributes" do
31
+ expect(seed_type.attributes).to be_blank
32
+ seed_type.has_attributes :name, :hair_color
33
+ expect(seed_type.attributes).to eq [:name, :hair_color]
34
+ end
35
+ end
36
+
37
+ describe "has_identifier_attributes" do
38
+ it "replaces their identifier attributes with the arguments" do
39
+ seed_type.has_identifier_attributes :name, :test
40
+ expect(seed_type.identifier_attributes).to eq [:name, :test]
41
+ end
42
+ end
43
+
44
+ describe "has_scope" do
45
+ it "adds the method and args to their list of scopes" do
46
+ seed_type.has_scope :where, 'name=?', 'foo'
47
+ seed_type.has_scope :order, 'name ASC'
48
+ expect(seed_type.scopes).to eq [
49
+ [:where, ['name=?', 'foo']],
50
+ [:order, ['name ASC']]
51
+ ]
52
+ end
53
+ end
54
+
55
+ describe "when_writing" do
56
+ before do
57
+ seed_type.when_writing :name do |person|
58
+ person.name.upcase
59
+ end
60
+ end
61
+
62
+ it "adds a proc to the dynamic_writers hash" do
63
+ expect(seed_type.dynamic_writers[:name]).not_to be_nil
64
+ end
65
+ end
66
+
67
+ describe "when_reading" do
68
+ before do
69
+ seed_type.when_reading :name do |person|
70
+ end
71
+ end
72
+
73
+ it "adds a proc to the dynamic_readers hash" do
74
+ expect(seed_type.dynamic_readers[:name]).not_to be_nil
75
+ end
76
+ end
77
+ end
78
+
79
+ describe "reading and writing" do
80
+ describe "write_to_folder" do
81
+ let(:csv_file) { double }
82
+ let(:person1) { Person.create }
83
+ let(:person2) { Person.create }
84
+ let(:folder) { '/tmp/seeds' }
85
+
86
+ before do
87
+ seed_type.stub items: [person1, person2]
88
+
89
+ CSV.stub(:open) do |&block|
90
+ block.call(csv_file)
91
+ end
92
+ csv_file.stub :<<
93
+
94
+ seed_type.stub write_value_for_attribute: nil
95
+ seed_type.stub(:write_value_for_attribute).with(person1, :color).and_return('blue')
96
+ seed_type.stub(:write_value_for_attribute).with(person1, :shape).and_return('square')
97
+ seed_type.stub(:write_value_for_attribute).with(person2, :color).and_return('red')
98
+ seed_type.stub(:write_value_for_attribute).with(person2, :shape).and_return('triangle')
99
+ end
100
+
101
+ context "with attributes" do
102
+ before do
103
+ seed_type.has_attributes :color, :shape
104
+ seed_type.write_to_folder(folder)
105
+ end
106
+
107
+ it "opens a CSV file" do
108
+ expect(CSV).to have_received(:open).with('/tmp/seeds/people.csv', 'w')
109
+ end
110
+
111
+ it "gets the attributes for each item from write_value_for_attribute" do
112
+ expect(seed_type).to have_received(:write_value_for_attribute).with(person1, :color)
113
+ expect(seed_type).to have_received(:write_value_for_attribute).with(person1, :shape)
114
+ expect(seed_type).to have_received(:write_value_for_attribute).with(person2, :color)
115
+ expect(seed_type).to have_received(:write_value_for_attribute).with(person2, :shape)
116
+ end
117
+
118
+ it "writes a header to a CSV file" do
119
+ expect(csv_file).to have_received(:<<).with([:color, :shape])
120
+ end
121
+
122
+ it "writes the attributes to a CSV file" do
123
+ expect(csv_file).to have_received(:<<).with(['blue', 'square'])
124
+ expect(csv_file).to have_received(:<<).with(['red', 'triangle'])
125
+ end
126
+ end
127
+
128
+ context "with no attributes" do
129
+ before do
130
+ seed_type.write_to_folder(folder)
131
+ end
132
+
133
+ it "does not open a CSV file" do
134
+ expect(CSV).not_to have_received(:open)
135
+ end
136
+ end
137
+ end
138
+
139
+ describe "read_from_folder" do
140
+ let(:csv_file) { double }
141
+ let!(:color1) { HairColor.create(color: :red) }
142
+ let!(:color2) { HairColor.create(color: :blue) }
143
+
144
+ let(:folder) { '/tmp/seeds' }
145
+
146
+ before do
147
+ CSV.stub(:open) do |&block|
148
+ block.call(csv_file)
149
+ end
150
+
151
+ csv_file.stub(:each) do |&block|
152
+ block.yield('name' => 'a', 'hair_color' => 'b')
153
+ block.yield('name' => 'c', 'hair_color' => 'd')
154
+ end
155
+
156
+ seed_type.stub(:read_value_for_attribute).with('a', :name).and_return('John Doe')
157
+ seed_type.stub(:read_value_for_attribute).with('b', :hair_color).and_return(color1)
158
+ seed_type.stub(:read_value_for_attribute).with('c', :name).and_return('Jane Doe')
159
+ seed_type.stub(:read_value_for_attribute).with('d', :hair_color).and_return(color2)
160
+ end
161
+
162
+ context "with attributes" do
163
+ before do
164
+ seed_type.has_attributes :name, :hair_color
165
+ seed_type.read_from_folder(folder)
166
+ end
167
+
168
+ it "opens a CSV file" do
169
+ expect(CSV).to have_received(:open).with('/tmp/seeds/people.csv', headers: true)
170
+ end
171
+
172
+ it "gets the attributes from read_value_for_attribute" do
173
+ expect(seed_type).to have_received(:read_value_for_attribute).with('a', :name)
174
+ expect(seed_type).to have_received(:read_value_for_attribute).with('b', :hair_color)
175
+ expect(seed_type).to have_received(:read_value_for_attribute).with('c', :name)
176
+ expect(seed_type).to have_received(:read_value_for_attribute).with('d', :hair_color)
177
+ end
178
+
179
+ it "creates a record for each row in the file" do
180
+ people = Person.all
181
+ expect(people.first.name).to eq 'John Doe'
182
+ expect(people.first.hair_color).to eq color1
183
+ expect(people.last.name).to eq 'Jane Doe'
184
+ expect(people.last.hair_color).to eq color2
185
+ end
186
+ end
187
+
188
+ context "with no attributes" do
189
+ before do
190
+ seed_type.read_from_folder(folder)
191
+ end
192
+
193
+ it "does not open a CSV file" do
194
+ expect(CSV).not_to have_received(:open)
195
+ end
196
+ end
197
+ end
198
+
199
+ describe "write_value_for_attribute" do
200
+ let(:identifier) { "foo" }
201
+ let(:color) { HairColor.create }
202
+ let(:person) { Person.create(hair_color: color, name: 'Jane Doe', scope: color)}
203
+ subject { seed_type.write_value_for_attribute(person, attribute) }
204
+
205
+ before do
206
+ seed_set.stub seed_identifier_for_record: identifier
207
+ end
208
+
209
+ context "with a dynamic writer" do
210
+ let(:attribute) { :name }
211
+ before do
212
+ seed_type.when_writing :name do |person|
213
+ person.name.upcase
214
+ end
215
+ end
216
+
217
+ it "runs the block on the record, and uses the result" do
218
+ expect(subject).to eq "JANE DOE"
219
+ end
220
+ end
221
+
222
+ context "with an association" do
223
+ let(:attribute) { :hair_color }
224
+ it "returns the seed identifier from the seed set" do
225
+ expect(subject).to eq identifier
226
+ expect(seed_set).to have_received(:seed_identifier_for_record).with(color)
227
+ end
228
+ end
229
+
230
+ context "with a polymorphic association" do
231
+ let(:attribute) { :scope }
232
+
233
+ it "returns the seed identifier from the seed set, with a class prefix" do
234
+ expect(subject).to eq "HairColor::#{identifier}"
235
+ expect(seed_set).to have_received(:seed_identifier_for_record).with(color)
236
+ end
237
+ end
238
+
239
+ context "with a normal value" do
240
+ let(:attribute) { :name }
241
+
242
+ it "returns the value" do
243
+ expect(subject).to eq person.name
244
+ end
245
+ end
246
+ end
247
+
248
+ describe "read_value_for_attribute" do
249
+ let(:value) { 'my::value' }
250
+ let(:color) { HairColor.create }
251
+ subject { seed_type.read_value_for_attribute(value, attribute) }
252
+
253
+ before do
254
+ seed_set.stub record_for_seed_identifier: color
255
+ end
256
+
257
+ context "with an association" do
258
+ let(:attribute) { :hair_color }
259
+
260
+ it "gets the record from the seed set" do
261
+ expect(subject).to eq color
262
+ expect(seed_set).to have_received(:record_for_seed_identifier).with(HairColor, ['my', 'value'])
263
+ end
264
+ end
265
+
266
+ context "with a polymorphic association" do
267
+ let(:attribute) { :scope }
268
+ let(:value) { "HairColor::my::value" }
269
+
270
+ it "gets the record from the seed set" do
271
+ expect(subject).to eq color
272
+ expect(seed_set).to have_received(:record_for_seed_identifier).with(HairColor, ['my', 'value'])
273
+ end
274
+ end
275
+
276
+ context "with a dynamic reader" do
277
+ let(:attribute) { :name }
278
+
279
+ before do
280
+ seed_type.when_reading :name do |value|
281
+ value + "2"
282
+ end
283
+ end
284
+
285
+ it "runs the block on the value and uses the result" do
286
+ expect(subject).to eq "my::value2"
287
+ end
288
+ end
289
+
290
+ context "with a normal attribute" do
291
+ let(:attribute) { :name }
292
+
293
+ it "returns the value" do
294
+ expect(subject).to eq value
295
+ end
296
+ end
297
+ end
298
+
299
+ describe "read_value_for_association" do
300
+ let(:color) { HairColor.create }
301
+
302
+ before do
303
+ seed_set.stub record_for_seed_identifier: color
304
+ end
305
+
306
+ context "with a normal association" do
307
+ subject { seed_type.read_value_for_association(:hair_color, ['red', 'FF0000']) }
308
+
309
+ it "gets the record from the seed set" do
310
+ expect(subject).to eq color
311
+ expect(seed_set).to have_received(:record_for_seed_identifier).with(HairColor, ['red', 'FF0000'])
312
+ end
313
+ end
314
+
315
+ context "with a polymorphic association" do
316
+ subject { seed_type.read_value_for_association(:scope, ['HairColor', 'blonde', 'FFD700']) }
317
+
318
+ it "extracts the class from the identifier and uses the seed set to get the record" do
319
+ expect(subject).to eq color
320
+ expect(seed_set).to have_received(:record_for_seed_identifier).with(HairColor, ['blonde', 'FFD700'])
321
+ end
322
+ end
323
+ end
324
+ end
325
+
326
+ describe "fetching" do
327
+ describe "items" do
328
+ let!(:person1) { Person.create(name: 'John Doe') }
329
+ let!(:person2) { Person.create(name: 'John Amos') }
330
+ let!(:person3) { Person.create(name: 'Jane Doe') }
331
+
332
+ subject { seed_type.items }
333
+
334
+ before do
335
+ seed_type.has_scope :where, 'name like ?', 'John%'
336
+ seed_type.has_scope :order, 'name ASC'
337
+ end
338
+
339
+ it "gets the records by applying the scope" do
340
+ expect(subject).to eq [person2, person1]
341
+ end
342
+ end
343
+
344
+ describe "options" do
345
+ it "is the seed set's options" do
346
+ expect(seed_type.options).to eq seed_set.options
347
+ end
348
+ end
349
+
350
+ describe "seed_identifier_for_record" do
351
+ let(:record) { double }
352
+ subject { seed_type.seed_identifier_for_record(record) }
353
+
354
+ before do
355
+ seed_type.has_identifier_attributes :shape, :color
356
+ seed_type.stub(:write_value_for_attribute).with(record, :shape).and_return('triangle')
357
+ seed_type.stub(:write_value_for_attribute).with(record, :color).and_return('red')
358
+ end
359
+
360
+ it "is the write values for the identifier attributes, joined with a double colon" do
361
+ expect(subject).to eq 'triangle::red'
362
+ end
363
+ end
364
+
365
+ describe "record_for_seed_identifier" do
366
+ let!(:color1) { HairColor.create(color: 'brown', hex_value: '964B00') }
367
+ let!(:color2) { HairColor.create(color: 'red', hex_value: 'FF0000') }
368
+ let!(:color3) { HairColor.create(color: 'blonde', hex_value: 'FFD700') }
369
+
370
+ let!(:person1) { Person.create(hair_color: color1, name: 'John') }
371
+ let!(:person2) { Person.create(hair_color: color3, name: 'Jane') }
372
+ let!(:person3) { Person.create(hair_color: color1, name: 'Jane') }
373
+ let!(:person4) { Person.create(hair_color: color2, name: 'John') }
374
+ let!(:person5) { Person.create(hair_color: color2, name: 'Jane') }
375
+
376
+ subject { seed_type.record_for_seed_identifier(identifier) }
377
+
378
+ context "with an single-field reference to another record" do
379
+ let(:identifier) { ['brown', 'Jane'] }
380
+
381
+ before do
382
+ seed_type.has_identifier_attributes :hair_color, :name
383
+ seed_set.seeds_for HairColor do
384
+ has_identifier_attributes :color
385
+ end
386
+ end
387
+
388
+ it "finds a record matching all the parts of the seed identifier" do
389
+ expect(subject).to eq person3
390
+ end
391
+
392
+ it "removes all the keys from the identifier" do
393
+ subject
394
+ expect(identifier).to eq []
395
+ end
396
+ end
397
+
398
+ context "with a multi-field reference to another record" do
399
+ let(:identifier) { ['red','FF0000','John'] }
400
+
401
+ before do
402
+ seed_type.has_identifier_attributes :hair_color, :name
403
+ seed_set.seeds_for HairColor do
404
+ has_identifier_attributes :color, :hex_value
405
+ end
406
+ end
407
+
408
+ it "finds a record matching all the parts of the seed identifier" do
409
+ expect(subject).to eq person4
410
+ end
411
+
412
+ it "removes all the keys from the identifier" do
413
+ subject
414
+ expect(identifier).to eq []
415
+ end
416
+ end
417
+
418
+ context "with more fields in the identifier than it needs" do
419
+ let(:identifier) { ['red','FF0000','John', 'test'] }
420
+
421
+ before do
422
+ seed_type.has_identifier_attributes :hair_color, :name
423
+ seed_set.seeds_for HairColor do
424
+ has_identifier_attributes :color, :hex_value
425
+ end
426
+ end
427
+
428
+ it "finds a record matching all the parts of the seed identifier" do
429
+ expect(subject).to eq person4
430
+ end
431
+
432
+ it "removes all the keys it needs from the identifier" do
433
+ subject
434
+ expect(identifier).to eq ['test']
435
+ end
436
+ end
437
+ end
438
+ end
439
+ end
@@ -0,0 +1,38 @@
1
+ require 'simplecov'
2
+ SimpleCov.start do
3
+ coverage_dir 'doc/coverage'
4
+ add_filter 'spec'
5
+ add_filter 'lib/config_scripts/tasks.rb'
6
+ end
7
+
8
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
9
+ ENV["RAILS_ENV"] ||= 'test'
10
+
11
+ require File.expand_path("../dummy/config/environment.rb", __FILE__)
12
+ require 'rspec/rails'
13
+ require 'rspec/autorun'
14
+ require 'timecop'
15
+
16
+ # Requires supporting ruby files with custom matchers and macros, etc,
17
+ # in spec/support/ and its subdirectories.
18
+ Dir[File.expand_path("../support/*.rb", __FILE__)].each { |f| puts f; require f }
19
+
20
+ # Checks for pending migrations before tests are run.
21
+ # If you are not using ActiveRecord, you can remove this line.
22
+ ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)
23
+
24
+ RSpec.configure do |config|
25
+ # Run specs in random order to surface order dependencies. If you find an
26
+ # order dependency and want to debug it, you can fix the order by providing
27
+ # the seed, which is printed after each run.
28
+ # --seed 1234
29
+ config.order = "random"
30
+
31
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
32
+ # examples within a transaction, remove the following line or assign false
33
+ # instead of true.
34
+ config.use_transactional_fixtures = true
35
+
36
+ config.filter_run focus: true
37
+ config.run_all_when_everything_filtered = true
38
+ end
@@ -0,0 +1,9 @@
1
+ class <%= name %>Config < ConfigScripts::Scripts::Script
2
+ def up
3
+ raise "Not supported"
4
+ end
5
+
6
+ def down
7
+ raise "Not supported"
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ class CreateConfigScripts < ActiveRecord::Migration
2
+ def change
3
+ create_table :config_scripts do |t|
4
+ t.string :name
5
+ end
6
+ end
7
+ end