config_scripts 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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