sprig 0.1.2 → 0.1.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 45e7ad607a25ae6b5ddd621ba63d2dc51d97ecf9
4
- data.tar.gz: b156960667081a75408bab2b2f5363a89b320d35
3
+ metadata.gz: d2748e6392c09851ed00b725700d826a95fd3568
4
+ data.tar.gz: b826aa49873ab82d3a738d03328d444d19e67ff1
5
5
  SHA512:
6
- metadata.gz: 69ab1c4161a19017678b2e03bbd6af6edb62ef498a56cc0d7c8e16b6097e9b72e66890f17605786eaa78bbbc9eb264e9034075728de8275e12f02ff2a28a112c
7
- data.tar.gz: 231648356a91e8f181161a6c9077161d4eb1d2e178ad1e04c96290029f11fdbb9b9a1f7459f774db6ce143cc2a23514d99faa3a8da65a419e347652adde26b1e
6
+ metadata.gz: 007edb3280e9bd30a4ddebe436a3d07e9a7c0d72525dab0e0ea3b22754027f7551fb857838b076411f682a27e3bc3b1a0a322642d1f00f50f5f981cff66c563b
7
+ data.tar.gz: fd2563c0b6bfb9086a396d1c6b914b38c9263084a9056dc0551686393165f69a56d046e5a228de92d54bf0066e18c276a782bf1d57832d7c1ba611b61ba23bb8
data/README.md CHANGED
@@ -147,4 +147,52 @@ Sprig.configure do |c|
147
147
  end
148
148
  ```
149
149
 
150
+ ## Populate Seed Files from Database
151
+
152
+ Don't want to write Sprig seed files from scratch? Well, Sprig can create them for you!
153
+
154
+ Via a rake task:
155
+ ```
156
+ rake db:seed:reap
157
+ ```
158
+ Or from the Rails console:
159
+ ```
160
+ Sprig::Harvest.reap
161
+ ```
162
+
163
+ By default, Sprig will create seed files (currently in `.yaml` only) for every model in your Rails
164
+ application. The seed files will be placed in a folder in `db/seeds` named after the current
165
+ `Rails.env`.
166
+
167
+ If any of the models in your application are using STI, Sprig will create a single seed file named
168
+ after the STI base model. STI sub-type records will all be written to that file.
169
+
170
+ ### Additional Configuration
171
+
172
+ Don't like the defaults when reaping Sprig records? You may specify the environment (`db/seeds`
173
+ target folder) or models (`ActiveRecord::Base.subclasses`-only) you want seed files for.
174
+
175
+ Example (rake task):
176
+ ```
177
+ rake db:seed:reap ENV=integration MODELS=User, Post
178
+ ```
179
+
180
+ Example (Rails console):
181
+ ```
182
+ Sprig::Harvest.reap(env: 'integration', models: [User, Post])
183
+ ```
184
+
185
+ ### Adding to Existing Seed Files (`.yaml` only)
186
+
187
+ Already have some seed files set up? No worries! Sprig will detect existing seed files and append
188
+ to them with the records from your database with no extra work needed. Sprig will automatically
189
+ assign unique `sprig_ids` so you won't have to deal with pesky duplicates.
190
+
191
+ NOTE: Sprig does not account for your application or database validations. If you reap seed files
192
+ from your database multiple times in a row without deleting the previous seed files or sprig
193
+ records, you'll end up with duplicate sprig records (but they'll all have unique `sprig_ids`). This
194
+ may cause validation issues when you seed your database.
195
+
196
+ ## License
197
+
150
198
  This project rocks and uses MIT-LICENSE.
@@ -1,6 +1,7 @@
1
1
  module Sprig
2
2
  autoload :Configuration, 'sprig/configuration'
3
3
  autoload :Planter, 'sprig/planter'
4
+ autoload :TsortableHash, 'sprig/tsortable_hash'
4
5
  autoload :Dependency, 'sprig/dependency'
5
6
  autoload :DependencyCollection, 'sprig/dependency_collection'
6
7
  autoload :DependencySorter, 'sprig/dependency_sorter'
@@ -16,6 +17,8 @@ module Sprig
16
17
  autoload :SprigRecordStore, 'sprig/sprig_record_store'
17
18
  autoload :Data, 'sprig/data'
18
19
  autoload :Seed, 'sprig/seed'
20
+ autoload :Harvest, 'sprig/harvest'
21
+ autoload :Railtie, 'sprig/railtie'
19
22
 
20
23
  class << self
21
24
  def configuration
@@ -54,16 +54,6 @@ module Sprig
54
54
  MissingDependencyError.new(missing_dependency)
55
55
  end
56
56
 
57
- class TsortableHash < Hash
58
- include TSort
59
-
60
- alias tsort_each_node each_key
61
-
62
- def tsort_each_child(node, &block)
63
- fetch(node).each(&block)
64
- end
65
- end
66
-
67
57
  class MissingDependencyError < StandardError
68
58
  def initialize(missing_dependency = nil)
69
59
  super message_for(missing_dependency)
@@ -0,0 +1,33 @@
1
+ module Sprig
2
+ module Harvest
3
+ autoload :Configuration, 'sprig/harvest/configuration'
4
+ autoload :Model, 'sprig/harvest/model'
5
+ autoload :Record, 'sprig/harvest/record'
6
+ autoload :SeedFile, 'sprig/harvest/seed_file'
7
+
8
+ class << self
9
+ def reap(options = {})
10
+ configure do |config|
11
+ config.env = options[:env] || options['ENV']
12
+ config.classes = options[:models] || options['MODELS']
13
+ end
14
+
15
+ Model.all.each { |model| SeedFile.new(model).write }
16
+ end
17
+
18
+ private
19
+
20
+ cattr_reader :configuration
21
+
22
+ delegate :env, :classes, to: :configuration
23
+
24
+ def configuration
25
+ @@configuration ||= Configuration.new
26
+ end
27
+
28
+ def configure
29
+ yield configuration
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,63 @@
1
+ module Sprig
2
+ module Harvest
3
+ class Configuration
4
+ VALID_CLASSES = ActiveRecord::Base.subclasses
5
+
6
+ def env
7
+ @env ||= Rails.env
8
+ end
9
+
10
+ def env=(given_env)
11
+ parse_valid_env_from given_env do |environment|
12
+ @env = environment
13
+ end
14
+ end
15
+
16
+ def classes
17
+ @classes ||= VALID_CLASSES
18
+ end
19
+
20
+ def classes=(given_classes)
21
+ parse_valid_classes_from given_classes do |classes|
22
+ @classes = classes
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def parse_valid_env_from(input)
29
+ return if input.nil?
30
+ environment = input.strip.downcase
31
+ create_seeds_folder(environment)
32
+ yield environment
33
+ end
34
+
35
+ def create_seeds_folder(env)
36
+ folder = Rails.root.join('db', 'seeds', env)
37
+ FileUtils.mkdir_p(folder) unless File.directory? folder
38
+ end
39
+
40
+ def parse_valid_classes_from(input)
41
+ return if input.nil?
42
+
43
+ classes = if input.is_a? String
44
+ input.split(',').map { |klass_string| klass_string.strip.classify.constantize }
45
+ else
46
+ input
47
+ end
48
+
49
+ validate_classes(classes)
50
+
51
+ yield classes
52
+ end
53
+
54
+ def validate_classes(classes)
55
+ classes.each do |klass|
56
+ unless VALID_CLASSES.include? klass
57
+ raise ArgumentError, "Cannot create a seed file for #{klass} because it is not a subclass of ActiveRecord::Base."
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,77 @@
1
+ module Sprig
2
+ module Harvest
3
+ class Model
4
+ def self.all
5
+ @@all ||= begin
6
+ models = Sprig::Harvest.classes.map { |klass| new(klass) }
7
+
8
+ tsorted_classes(models).map do |klass|
9
+ models.find { |model| model.klass == klass }
10
+ end
11
+ end
12
+ end
13
+
14
+ def self.find(klass, id)
15
+ all.find { |model| model.klass == klass }.find(id)
16
+ end
17
+
18
+ attr_reader :klass
19
+ attr_writer :existing_sprig_ids
20
+
21
+ def initialize(klass)
22
+ @klass = klass
23
+ end
24
+
25
+ def attributes
26
+ klass.column_names
27
+ end
28
+
29
+ def dependencies
30
+ @dependencies ||= klass.reflect_on_all_associations(:belongs_to).map do |association|
31
+ association.name.to_s.classify.constantize
32
+ end
33
+ end
34
+
35
+ def existing_sprig_ids
36
+ @existing_sprig_ids ||= []
37
+ end
38
+
39
+ def generate_sprig_id
40
+ existing_sprig_ids.select { |i| i.is_a? Integer }.sort.last + 1
41
+ end
42
+
43
+ def find(id)
44
+ records.find { |record| record.id == id }
45
+ end
46
+
47
+ def to_s
48
+ klass.to_s
49
+ end
50
+
51
+ def to_yaml(options = {})
52
+ namespace = options[:namespace]
53
+ formatted_records = records.map(&:to_hash)
54
+
55
+ yaml = if namespace
56
+ { namespace => formatted_records }.to_yaml
57
+ else
58
+ formatted_records.to_yaml
59
+ end
60
+
61
+ yaml.gsub("---\n", '') # Remove annoying YAML separator
62
+ end
63
+
64
+ def records
65
+ @records ||= klass.all.map { |record| Record.new(record, self) }
66
+ end
67
+
68
+ private
69
+
70
+ def self.tsorted_classes(models)
71
+ models.reduce(TsortableHash.new) do |hash, model|
72
+ hash.merge(model.klass => model.dependencies)
73
+ end.tsort
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,61 @@
1
+ module Sprig
2
+ module Harvest
3
+ class Record
4
+ attr_reader :record, :model
5
+ attr_writer :sprig_id
6
+
7
+ def initialize(record, model)
8
+ @record = record
9
+ @model = model
10
+ end
11
+
12
+ def attributes
13
+ @attributes ||= Array.new.replace(model.attributes).tap do |attrs|
14
+ attrs[0] = 'sprig_id'
15
+ end
16
+ end
17
+
18
+ def to_hash
19
+ attributes.reduce(Hash.new) { |hash, attr| hash.merge(attr => send(attr)) }
20
+ end
21
+
22
+ def sprig_id
23
+ @sprig_id ||= model.existing_sprig_ids.include?(record.id) ? model.generate_sprig_id : record.id
24
+ end
25
+
26
+ private
27
+
28
+ def method_missing(method, *args, &block)
29
+ attr = model.attributes.find { |attr| attr == method.to_s }
30
+
31
+ if attr.nil?
32
+ super
33
+ elsif dependency_finder.match(attr)
34
+ klass = klass_for(attr)
35
+ id = record.send(attr)
36
+ sprig_id = Model.find(klass, id).sprig_id
37
+
38
+ sprig_record(klass, sprig_id)
39
+ else
40
+ record.send(attr)
41
+ end
42
+ end
43
+
44
+ def respond_to_missing?(method, include_private = false)
45
+ model.attributes.include?(method.to_s) || super
46
+ end
47
+
48
+ def dependency_finder
49
+ /_id/
50
+ end
51
+
52
+ def klass_for(attr)
53
+ attr.gsub(dependency_finder, '').classify.constantize
54
+ end
55
+
56
+ def sprig_record(klass, sprig_id)
57
+ "<%= sprig_record(#{klass}, #{sprig_id}).id %>"
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,54 @@
1
+ module Sprig
2
+ module Harvest
3
+ class SeedFile
4
+ DEFAULT_NAMESPACE = 'records'
5
+
6
+ attr_reader :model
7
+
8
+ def initialize(model)
9
+ raise ArgumentError, 'Must initialize with a Sprig::Harvest::Model' unless model.is_a? Model
10
+
11
+ @model = model
12
+ end
13
+
14
+ def path
15
+ Rails.root.join('db', 'seeds', Sprig::Harvest.env, "#{model.to_s.tableize.gsub('/', '_')}.yml")
16
+ end
17
+
18
+ def exists?
19
+ File.exists?(path)
20
+ end
21
+
22
+ def write
23
+ initialize_file do |file, namespace|
24
+ file.write model.to_yaml(:namespace => namespace)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def initialize_file
31
+ existing_file = exists?
32
+ access_type = existing_file ? 'a+' : 'w'
33
+
34
+ File.open(path, access_type) do |file|
35
+ namespace = DEFAULT_NAMESPACE
36
+
37
+ if existing_file
38
+ model.existing_sprig_ids = existing_sprig_ids(file.read)
39
+ namespace = nil
40
+ file.write("\n")
41
+ end
42
+
43
+ yield file, namespace
44
+ end
45
+ end
46
+
47
+ def existing_sprig_ids(yaml)
48
+ YAML.load(yaml).fetch(DEFAULT_NAMESPACE).to_a.map do |record|
49
+ record.fetch('sprig_id')
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,7 @@
1
+ module Sprig
2
+ class Railtie < Rails::Railtie
3
+ rake_tasks do
4
+ load 'tasks/reap.rake'
5
+ end
6
+ end
7
+ end
@@ -35,7 +35,11 @@ module Sprig
35
35
  end
36
36
 
37
37
  def success_log_text
38
- "#{klass.name} with sprig_id #{sprig_id} successfully saved."
38
+ "#{klass.name} with sprig_id #{sprig_id} successfully #{success_log_status_text}."
39
+ end
40
+
41
+ def success_log_status_text
42
+ record.existing? ? "updated" : "saved"
39
43
  end
40
44
 
41
45
  def error_log_text
@@ -11,9 +11,10 @@ module Sprig
11
11
  end
12
12
 
13
13
  def initialize(klass, attributes, orm_record = nil)
14
- @klass = klass
14
+ @klass = klass
15
15
  @attributes = attributes
16
16
  @orm_record = orm_record || klass.new
17
+ @existing = @orm_record.persisted?
17
18
  end
18
19
 
19
20
  def save
@@ -21,6 +22,10 @@ module Sprig
21
22
  orm_record.save
22
23
  end
23
24
 
25
+ def existing?
26
+ @existing
27
+ end
28
+
24
29
  private
25
30
 
26
31
  attr_reader :attributes, :klass
@@ -0,0 +1,11 @@
1
+ module Sprig
2
+ class TsortableHash < Hash
3
+ include TSort
4
+
5
+ alias tsort_each_node each_key
6
+
7
+ def tsort_each_child(node, &block)
8
+ fetch(node).each(&block)
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  module Sprig
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.3"
3
3
  end
@@ -0,0 +1,9 @@
1
+ namespace :db do
2
+ namespace :seed do
3
+ desc 'Create Sprig seed files from database records'
4
+ task :reap => :environment do
5
+ Rails.application.eager_load!
6
+ Sprig::Harvest.reap(ENV)
7
+ end
8
+ end
9
+ end
Binary file
@@ -0,0 +1,7 @@
1
+ records:
2
+ - sprig_id: 10
3
+ post_id: <%= sprig_record(Post, 2).id %>
4
+ body: pork chop sandwiches
5
+ - sprig_id: 20
6
+ post_id: <%= sprig_record(Post, 1).id %>
7
+ body: do you play baseball?
@@ -0,0 +1,9 @@
1
+ records:
2
+ - sprig_id: 1
3
+ title: such post
4
+ content: Once upon a time BOOM POSTED
5
+ published: true
6
+ - sprig_id: 2
7
+ title: very words
8
+ content: Word cannon has been loaded up real good.
9
+ published: true
@@ -0,0 +1,7 @@
1
+ records:
2
+ - sprig_id: 1
3
+ post_id: <%= sprig_record(Post, 1).id %>
4
+ body:
5
+ - sprig_id: 2
6
+ post_id: <%= sprig_record(Post, 2).id %>
7
+ body:
@@ -0,0 +1,6 @@
1
+ - sprig_id: 1
2
+ post_id: <%= sprig_record(Post, 1).id %>
3
+ body:
4
+ - sprig_id: 2
5
+ post_id: <%= sprig_record(Post, 2).id %>
6
+ body:
@@ -0,0 +1,7 @@
1
+ records:
2
+ - sprig_id: 5
3
+ first_name: Bo
4
+ last_name: Janglez
5
+ - sprig_id: 9
6
+ first_name: Jimmy
7
+ last_name: Russel
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sprig::Harvest::Configuration do
4
+ subject { described_class.new }
5
+
6
+ before do
7
+ stub_rails_root
8
+ end
9
+
10
+ describe "#env" do
11
+ context "from a fresh configuration" do
12
+ its(:env) { should == Rails.env }
13
+ end
14
+ end
15
+
16
+ describe "#env=" do
17
+ context "when given nil" do
18
+ it "does not change the env" do
19
+ subject.env = nil
20
+
21
+ subject.env.should_not == nil
22
+ end
23
+ end
24
+
25
+ context "given a non-nil value" do
26
+ let(:input) { ' ShaBOOSH' }
27
+
28
+ it "formats the given value and then sets the environment" do
29
+ subject.env = input
30
+
31
+ subject.env.should == 'shaboosh'
32
+ end
33
+
34
+ context "and the corresponding seeds folder does not yet exist" do
35
+ after do
36
+ FileUtils.remove_dir('./spec/fixtures/db/seeds/shaboosh')
37
+ end
38
+
39
+ it "creates the seeds folder" do
40
+ subject.env = input
41
+
42
+ File.directory?('./spec/fixtures/db/seeds/shaboosh').should == true
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ describe "#classes" do
49
+ context "from a fresh configuration" do
50
+ its(:classes) { should == ActiveRecord::Base.subclasses }
51
+ end
52
+ end
53
+
54
+ describe "#classes=" do
55
+ context "when given nil" do
56
+ it "does not set classes to nil" do
57
+ subject.classes = nil
58
+
59
+ subject.classes.should_not == nil
60
+ end
61
+ end
62
+
63
+ context "when given an array of classes" do
64
+ context "where one or more classes are not subclasses of ActiveRecord::Base" do
65
+ it "raises an error" do
66
+ expect {
67
+ subject.classes = [Comment, Sprig::Harvest::Model]
68
+ }.to raise_error ArgumentError, 'Cannot create a seed file for Sprig::Harvest::Model because it is not a subclass of ActiveRecord::Base.'
69
+ end
70
+ end
71
+
72
+ context "where all classes are subclasses of ActiveRecord::Base" do
73
+ it "sets classes to the given input" do
74
+ subject.classes = [Comment, Post]
75
+
76
+ subject.classes.should == [Comment, Post]
77
+ end
78
+ end
79
+ end
80
+
81
+ context "when given a string" do
82
+ context "where one or more classes are not subclasses of ActiveRecord::Base" do
83
+ it "raises an error" do
84
+ expect {
85
+ subject.classes = 'Sprig::Harvest::Model'
86
+ }.to raise_error ArgumentError, 'Cannot create a seed file for Sprig::Harvest::Model because it is not a subclass of ActiveRecord::Base.'
87
+ end
88
+ end
89
+
90
+ context "where all classes are subclasses of ActiveRecord::Base" do
91
+ it "sets classes to the parsed input" do
92
+ subject.classes = ' comment, post'
93
+
94
+ subject.classes.should == [Comment, Post]
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,120 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sprig::Harvest::Model do
4
+ describe ".all" do
5
+ let(:all_models) do
6
+ [
7
+ described_class.new(Post),
8
+ described_class.new(Comment),
9
+ described_class.new(User)
10
+ ]
11
+ end
12
+
13
+ before do
14
+ Sprig::Harvest.stub(:classes).and_return([Comment, Post, User])
15
+ end
16
+
17
+ it "returns an dependency-sorted array of Sprig::Harvest::Models" do
18
+ described_class.all.all? { |model| model.is_a? Sprig::Harvest::Model }.should == true
19
+ described_class.all.map(&:klass).should == all_models.map(&:klass)
20
+ end
21
+ end
22
+
23
+ describe ".find" do
24
+ let!(:user) { User.create(:first_name => 'Bo', :last_name => 'Janglez') }
25
+ let!(:post1) { Post.create }
26
+ let!(:post2) { Post.create }
27
+ let!(:comment1) { Comment.create(:post => post1) }
28
+ let!(:comment2) { Comment.create(:post => post2) }
29
+
30
+ subject { described_class }
31
+
32
+ it "returns the Sprig::Harvest::Record for the given class and id" do
33
+ subject.find(User, 1).record.should == user
34
+ subject.find(Post, 1).record.should == post1
35
+ subject.find(Post, 2).record.should == post2
36
+ subject.find(Comment, 1).record.should == comment1
37
+ subject.find(Comment, 2).record.should == comment2
38
+ end
39
+ end
40
+
41
+ describe "#attributes" do
42
+ subject { described_class.new(User) }
43
+
44
+ its(:attributes) { should == User.column_names }
45
+ end
46
+
47
+ describe "#dependencies" do
48
+ subject { described_class.new(Comment) }
49
+
50
+ its(:dependencies) { should == [Post] }
51
+ end
52
+
53
+ describe "#find" do
54
+ let!(:post1) { Post.create }
55
+ let!(:post2) { Post.create }
56
+
57
+ subject { described_class.new(Post) }
58
+
59
+ it "returns the Sprig::Harvest::Record with the given id" do
60
+ harvest_record = subject.find(2)
61
+ harvest_record.should be_an_instance_of Sprig::Harvest::Record
62
+ harvest_record.record.should == post2
63
+ end
64
+ end
65
+
66
+ describe "#generate_sprig_id" do
67
+ subject { described_class.new(Comment) }
68
+
69
+ context "when the existing sprig_ids are all integers" do
70
+ before do
71
+ subject.existing_sprig_ids = [5, 20, 8]
72
+ end
73
+
74
+ it "returns an integer-type sprig_id that is not taken" do
75
+ subject.generate_sprig_id.should == 21
76
+ end
77
+ end
78
+
79
+ context "when the existing sprig ids contain non-integer values" do
80
+ before do
81
+ subject.existing_sprig_ids = [1, 5, 'l_2', 'l_10', 'such_sprigs', 10.9]
82
+ end
83
+ it "returns an integer-type sprig_id that is not taken" do
84
+ subject.generate_sprig_id.should == 6
85
+ end
86
+ end
87
+ end
88
+
89
+ describe "#to_s" do
90
+ subject { described_class.new(Comment) }
91
+
92
+ its(:to_s) { should == "Comment" }
93
+ end
94
+
95
+ describe "#to_yaml" do
96
+ let!(:user) { User.create(:first_name => 'Bo', :last_name => 'Janglez') }
97
+ let!(:post1) { Post.create }
98
+ let!(:post2) { Post.create }
99
+ let!(:comment1) { Comment.create(:post => post1) }
100
+ let!(:comment2) { Comment.create(:post => post2) }
101
+
102
+ subject { described_class.new(Comment) }
103
+
104
+ context "when passed a value for the namespace" do
105
+ it "returns the correct yaml" do
106
+ subject.to_yaml(:namespace => 'records').should == yaml_from_file('records_with_namespace.yml')
107
+ end
108
+ end
109
+
110
+ context "when no namespace is given" do
111
+ it "returns the correct yaml" do
112
+ subject.to_yaml.should == yaml_from_file('records_without_namespace.yml')
113
+ end
114
+ end
115
+ end
116
+
117
+ def yaml_from_file(basename)
118
+ File.read('spec/fixtures/yaml/' + basename)
119
+ end
120
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ class Very; end
4
+
5
+ describe Sprig::Harvest::Record do
6
+ let(:record) { double('ActiveRecord::Base Instance') }
7
+ let(:model) { double('Sprig::Harvest::Model') }
8
+ let(:harvest_record) { double('Sprig::Harvest::Record for class Very', :sprig_id => 5) }
9
+ let(:sprig_record) { "<%= sprig_record(Very, 5).id %>" }
10
+
11
+ subject { described_class.new(record, model) }
12
+
13
+ before do
14
+ attrs = {
15
+ 'id' => 0,
16
+ 'such' => 1,
17
+ 'wow' => 2,
18
+ 'very_id' => 3
19
+ }
20
+
21
+ attrs.each_pair do |attr, val|
22
+ record.stub(attr).and_return(val)
23
+ end
24
+
25
+ model.stub(:attributes).and_return(attrs.keys)
26
+ model.stub(:existing_sprig_ids).and_return([])
27
+
28
+ Sprig::Harvest::Model.stub(:find).and_return(harvest_record)
29
+ end
30
+
31
+ its(:id) { should == 0 }
32
+ its(:such) { should == 1 }
33
+ its(:wow) { should == 2 }
34
+ its(:very_id) { should == sprig_record }
35
+
36
+ describe "#attributes" do
37
+ it "returns an array of attributes from the given model with sprig_id swapped out for id" do
38
+ subject.attributes.should == %w(
39
+ sprig_id
40
+ such
41
+ wow
42
+ very_id
43
+ )
44
+ end
45
+ end
46
+
47
+ describe "#to_hash" do
48
+ it "returns its attributes and their values in a hash" do
49
+ subject.to_hash.should == {
50
+ 'sprig_id' => 0,
51
+ 'such' => 1,
52
+ 'wow' => 2,
53
+ 'very_id' => sprig_record
54
+ }
55
+ end
56
+ end
57
+
58
+ describe "#sprig_id" do
59
+ its(:sprig_id) { should == record.id }
60
+
61
+ context "when an existing seed record has a sprig_id equal to the record's id" do
62
+ before do
63
+ model.stub(:existing_sprig_ids).and_return([record.id])
64
+ model.stub(:generate_sprig_id).and_return(25)
65
+ end
66
+
67
+ its(:sprig_id) { should == 25 }
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,109 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sprig::Harvest::SeedFile do
4
+ let(:model) { Sprig::Harvest::Model.new(Comment) }
5
+
6
+ subject { described_class.new(model) }
7
+
8
+ before do
9
+ stub_rails_root
10
+ Sprig::Harvest.stub(:env).and_return('dreamland')
11
+ end
12
+
13
+ describe "#initialize" do
14
+ context "given a non-Sprig::Harvest::Model" do
15
+ it "raises an error" do
16
+ expect {
17
+ described_class.new(User)
18
+ }.to raise_error ArgumentError, 'Must initialize with a Sprig::Harvest::Model'
19
+ end
20
+ end
21
+ end
22
+
23
+ describe "#path" do
24
+ around do |example|
25
+ setup_seed_folder('./spec/fixtures/db/seeds/dreamland', &example)
26
+ end
27
+
28
+ its(:path) { should == Rails.root.join('db', 'seeds', 'dreamland', 'comments.yml') }
29
+ end
30
+
31
+ describe "#exists?" do
32
+ subject { described_class.new(model) }
33
+
34
+ around do |example|
35
+ setup_seed_folder('./spec/fixtures/db/seeds/dreamland', &example)
36
+ end
37
+
38
+ context "when the seed file already exists" do
39
+ before do
40
+ File.stub(:exists?).with(subject.path).and_return(true)
41
+ end
42
+
43
+ its(:exists?) { should == true }
44
+ end
45
+
46
+ context "when the seed file does not exist" do
47
+ its(:exists?) { should == false }
48
+ end
49
+ end
50
+
51
+ describe "#write" do
52
+ let!(:user) { User.create(:first_name => 'Bo', :last_name => 'Janglez') }
53
+ let!(:post1) { Post.create }
54
+ let!(:post2) { Post.create }
55
+ let!(:comment1) { Comment.create(:post => post1) }
56
+ let!(:comment2) { Comment.create(:post => post2) }
57
+
58
+ around do |example|
59
+ setup_seed_folder('./spec/fixtures/db/seeds/dreamland', &example)
60
+ end
61
+
62
+ context "when the seed file already exists" do
63
+ before do
64
+ yaml = File.read('./spec/fixtures/yaml/comment_seeds.yml')
65
+ File.open(subject.path, 'w') { |file| file.write(yaml) }
66
+ end
67
+
68
+ it "pulls out the existing sprig ids and stores them on the given model" do
69
+ model.should_receive(:existing_sprig_ids=).with([10, 20])
70
+
71
+ subject.write
72
+ end
73
+
74
+ it "grabs the yaml for the given model without a namespace" do
75
+ model.should_receive(:to_yaml).with(:namespace => nil)
76
+
77
+ subject.write
78
+ end
79
+
80
+ it "populates the file" do
81
+ starting_size = File.size(subject.path)
82
+
83
+ subject.write
84
+
85
+ File.size?(subject.path).should > starting_size
86
+ end
87
+ end
88
+
89
+ context "when the seed file does not yet exist" do
90
+ it "does not pass any existing sprig ids to the given model" do
91
+ model.should_not_receive(:existing_sprig_ids=)
92
+
93
+ subject.write
94
+ end
95
+
96
+ it "grabs the yaml for the given model with the 'records' namespace" do
97
+ model.should_receive(:to_yaml).with(:namespace => 'records')
98
+
99
+ subject.write
100
+ end
101
+
102
+ it "populates the file" do
103
+ subject.write
104
+
105
+ File.size?(subject.path).should > 0
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sprig::Harvest do
4
+ describe ".reap" do
5
+ let(:seed_file) { double('Sprig::Harvest::SeedFile', :write => 'such seeds') }
6
+
7
+ before do
8
+ stub_rails_root
9
+ stub_rails_env('dreamland')
10
+ Sprig::Harvest::SeedFile.stub(:new).and_return(seed_file)
11
+ end
12
+
13
+ around do |example|
14
+ setup_seed_folder('./spec/fixtures/db/seeds/dreamland', &example)
15
+ end
16
+
17
+ it "generates a seed file for each class" do
18
+ seed_file.should_receive(:write).exactly(3).times
19
+
20
+ subject.reap
21
+ end
22
+
23
+ context "when passed an environment in the options hash" do
24
+ context "in :env" do
25
+ it "sets the environment" do
26
+ subject.reap(:env => 'integration')
27
+ subject.env.should == 'integration'
28
+ end
29
+ end
30
+
31
+ context "in 'ENV'" do
32
+ it "sets the environment" do
33
+ subject.reap('ENV' => ' Integration')
34
+ subject.env.should == 'integration'
35
+ end
36
+ end
37
+ end
38
+
39
+ context "when passed a set of classes in the options hash" do
40
+ context "in :classes" do
41
+ it "sets the classes" do
42
+ subject.reap(:models => [User, Post])
43
+ subject.classes.should == [User, Post]
44
+ end
45
+ end
46
+
47
+ context "sets the classes" do
48
+ it "passes the value to its configuration" do
49
+ subject.reap('MODELS' => 'User, Post')
50
+ subject.classes.should == [User, Post]
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sprig::Seed::Entry do
4
+ describe ".success_log_text" do
5
+ context "on a new record" do
6
+ it "indicates the record was 'saved'" do
7
+ subject = described_class.new(Post, { title: "Hello World!", content: "Stuff", sprig_id: 1 }, {})
8
+ subject.save_record
9
+
10
+ subject.success_log_text.should == "Post with sprig_id 1 successfully saved."
11
+ end
12
+ end
13
+
14
+ context "on an existing record" do
15
+ let!(:existing) do
16
+ Post.create(
17
+ :title => "Existing title",
18
+ :content => "Existing content",
19
+ :published => false
20
+ )
21
+ end
22
+
23
+ it "indicates the record was 'updated'" do
24
+ subject = described_class.new(Post, { title: "Existing title", content: "Existing content", sprig_id: 1 }, { find_existing_by: [:title] })
25
+ subject.save_record
26
+
27
+ subject.success_log_text.should == "Post with sprig_id 1 successfully updated."
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sprig::Seed::Record do
4
+ describe ".existing?" do
5
+ let!(:existing) do
6
+ Post.create(
7
+ :title => "Existing title",
8
+ :content => "Existing content",
9
+ :published => false
10
+ )
11
+ end
12
+
13
+ it "returns true if the record has already been saved to the database" do
14
+ subject = described_class.new_or_existing(Post, { title: "Existing title" }, { title: "Existing title" })
15
+
16
+ subject.existing?.should == true
17
+ end
18
+
19
+ it "returns false if the record is new" do
20
+ subject = described_class.new_or_existing(Post, { title: "New title" }, { title: "New title" })
21
+
22
+ subject.existing?.should == false
23
+ end
24
+ end
25
+ end
@@ -78,3 +78,12 @@ def load_seeds(*files)
78
78
  `rm ./spec/fixtures/db/seeds/#{env}/#{file}`
79
79
  end
80
80
  end
81
+
82
+ # Create and remove seed folder around a spec
83
+ def setup_seed_folder(path)
84
+ FileUtils.mkdir_p(path)
85
+
86
+ yield
87
+
88
+ FileUtils.remove_dir(path)
89
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sprig
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lawson Kurtz
@@ -9,118 +9,118 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-05-14 00:00:00.000000000 Z
12
+ date: 2014-06-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - "~>"
18
+ - - ~>
19
19
  - !ruby/object:Gem::Version
20
20
  version: '3.1'
21
21
  type: :development
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - "~>"
25
+ - - ~>
26
26
  - !ruby/object:Gem::Version
27
27
  version: '3.1'
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: sqlite3
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - "~>"
32
+ - - ~>
33
33
  - !ruby/object:Gem::Version
34
34
  version: 1.3.8
35
35
  type: :development
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - "~>"
39
+ - - ~>
40
40
  - !ruby/object:Gem::Version
41
41
  version: 1.3.8
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: rspec
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
- - - "~>"
46
+ - - ~>
47
47
  - !ruby/object:Gem::Version
48
48
  version: 2.14.0
49
49
  type: :development
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
- - - "~>"
53
+ - - ~>
54
54
  - !ruby/object:Gem::Version
55
55
  version: 2.14.0
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: database_cleaner
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
- - - "~>"
60
+ - - ~>
61
61
  - !ruby/object:Gem::Version
62
62
  version: 1.2.0
63
63
  type: :development
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
- - - "~>"
67
+ - - ~>
68
68
  - !ruby/object:Gem::Version
69
69
  version: 1.2.0
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: webmock
72
72
  requirement: !ruby/object:Gem::Requirement
73
73
  requirements:
74
- - - "~>"
74
+ - - ~>
75
75
  - !ruby/object:Gem::Version
76
76
  version: 1.15.0
77
77
  type: :development
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
- - - "~>"
81
+ - - ~>
82
82
  - !ruby/object:Gem::Version
83
83
  version: 1.15.0
84
84
  - !ruby/object:Gem::Dependency
85
85
  name: vcr
86
86
  requirement: !ruby/object:Gem::Requirement
87
87
  requirements:
88
- - - "~>"
88
+ - - ~>
89
89
  - !ruby/object:Gem::Version
90
90
  version: 2.8.0
91
91
  type: :development
92
92
  prerelease: false
93
93
  version_requirements: !ruby/object:Gem::Requirement
94
94
  requirements:
95
- - - "~>"
95
+ - - ~>
96
96
  - !ruby/object:Gem::Version
97
97
  version: 2.8.0
98
98
  - !ruby/object:Gem::Dependency
99
99
  name: pry
100
100
  requirement: !ruby/object:Gem::Requirement
101
101
  requirements:
102
- - - ">="
102
+ - - '>='
103
103
  - !ruby/object:Gem::Version
104
104
  version: '0'
105
105
  type: :development
106
106
  prerelease: false
107
107
  version_requirements: !ruby/object:Gem::Requirement
108
108
  requirements:
109
- - - ">="
109
+ - - '>='
110
110
  - !ruby/object:Gem::Version
111
111
  version: '0'
112
112
  - !ruby/object:Gem::Dependency
113
113
  name: generator_spec
114
114
  requirement: !ruby/object:Gem::Requirement
115
115
  requirements:
116
- - - ">="
116
+ - - '>='
117
117
  - !ruby/object:Gem::Version
118
118
  version: '0'
119
119
  type: :development
120
120
  prerelease: false
121
121
  version_requirements: !ruby/object:Gem::Requirement
122
122
  requirements:
123
- - - ">="
123
+ - - '>='
124
124
  - !ruby/object:Gem::Version
125
125
  version: '0'
126
126
  description: Sprig is a library for managing interconnected, environment-specific
@@ -144,6 +144,11 @@ files:
144
144
  - lib/sprig/dependency_sorter.rb
145
145
  - lib/sprig/directive.rb
146
146
  - lib/sprig/directive_list.rb
147
+ - lib/sprig/harvest.rb
148
+ - lib/sprig/harvest/configuration.rb
149
+ - lib/sprig/harvest/model.rb
150
+ - lib/sprig/harvest/record.rb
151
+ - lib/sprig/harvest/seed_file.rb
147
152
  - lib/sprig/helpers.rb
148
153
  - lib/sprig/logging.rb
149
154
  - lib/sprig/null_record.rb
@@ -155,6 +160,7 @@ files:
155
160
  - lib/sprig/parser/yml.rb
156
161
  - lib/sprig/planter.rb
157
162
  - lib/sprig/process_notifier.rb
163
+ - lib/sprig/railtie.rb
158
164
  - lib/sprig/seed.rb
159
165
  - lib/sprig/seed/attribute.rb
160
166
  - lib/sprig/seed/attribute_collection.rb
@@ -163,7 +169,9 @@ files:
163
169
  - lib/sprig/seed/record.rb
164
170
  - lib/sprig/source.rb
165
171
  - lib/sprig/sprig_record_store.rb
172
+ - lib/sprig/tsortable_hash.rb
166
173
  - lib/sprig/version.rb
174
+ - lib/tasks/reap.rake
167
175
  - spec/db/activerecord.db
168
176
  - spec/feature/configurations_spec.rb
169
177
  - spec/fixtures/cassettes/google_spreadsheet_json_posts.yml
@@ -184,12 +192,24 @@ files:
184
192
  - spec/fixtures/seeds/test/posts_find_existing_by_single.yml
185
193
  - spec/fixtures/seeds/test/posts_missing_dependency.yml
186
194
  - spec/fixtures/seeds/test/posts_missing_record.yml
195
+ - spec/fixtures/yaml/comment_seeds.yml
196
+ - spec/fixtures/yaml/post_seeds.yml
197
+ - spec/fixtures/yaml/records_with_namespace.yml
198
+ - spec/fixtures/yaml/records_without_namespace.yml
199
+ - spec/fixtures/yaml/user_seeds.yml
187
200
  - spec/lib/generators/sprig/install_generator_spec.rb
188
201
  - spec/lib/sprig/configuration_spec.rb
189
202
  - spec/lib/sprig/directive_list_spec.rb
190
203
  - spec/lib/sprig/directive_spec.rb
204
+ - spec/lib/sprig/harvest/configuration_spec.rb
205
+ - spec/lib/sprig/harvest/model_spec.rb
206
+ - spec/lib/sprig/harvest/record_spec.rb
207
+ - spec/lib/sprig/harvest/seed_file_spec.rb
208
+ - spec/lib/sprig/harvest_spec.rb
191
209
  - spec/lib/sprig/null_record_spec.rb
192
210
  - spec/lib/sprig/process_notifier_spec.rb
211
+ - spec/lib/sprig/seed/entry_spec.rb
212
+ - spec/lib/sprig/seed/record_spec.rb
193
213
  - spec/lib/sprig_spec.rb
194
214
  - spec/spec_helper.rb
195
215
  - spec/sprig_spec.rb
@@ -206,17 +226,17 @@ require_paths:
206
226
  - lib
207
227
  required_ruby_version: !ruby/object:Gem::Requirement
208
228
  requirements:
209
- - - ">="
229
+ - - '>='
210
230
  - !ruby/object:Gem::Version
211
231
  version: '0'
212
232
  required_rubygems_version: !ruby/object:Gem::Requirement
213
233
  requirements:
214
- - - ">="
234
+ - - '>='
215
235
  - !ruby/object:Gem::Version
216
236
  version: '0'
217
237
  requirements: []
218
238
  rubyforge_project:
219
- rubygems_version: 2.2.2
239
+ rubygems_version: 2.2.1
220
240
  signing_key:
221
241
  specification_version: 4
222
242
  summary: Relational, environment-specific seeding for Rails apps.
@@ -241,15 +261,28 @@ test_files:
241
261
  - spec/fixtures/seeds/test/posts_find_existing_by_single.yml
242
262
  - spec/fixtures/seeds/test/posts_missing_dependency.yml
243
263
  - spec/fixtures/seeds/test/posts_missing_record.yml
264
+ - spec/fixtures/yaml/comment_seeds.yml
265
+ - spec/fixtures/yaml/post_seeds.yml
266
+ - spec/fixtures/yaml/records_with_namespace.yml
267
+ - spec/fixtures/yaml/records_without_namespace.yml
268
+ - spec/fixtures/yaml/user_seeds.yml
244
269
  - spec/lib/generators/sprig/install_generator_spec.rb
245
270
  - spec/lib/sprig/configuration_spec.rb
246
271
  - spec/lib/sprig/directive_list_spec.rb
247
272
  - spec/lib/sprig/directive_spec.rb
273
+ - spec/lib/sprig/harvest/configuration_spec.rb
274
+ - spec/lib/sprig/harvest/model_spec.rb
275
+ - spec/lib/sprig/harvest/record_spec.rb
276
+ - spec/lib/sprig/harvest/seed_file_spec.rb
277
+ - spec/lib/sprig/harvest_spec.rb
248
278
  - spec/lib/sprig/null_record_spec.rb
249
279
  - spec/lib/sprig/process_notifier_spec.rb
280
+ - spec/lib/sprig/seed/entry_spec.rb
281
+ - spec/lib/sprig/seed/record_spec.rb
250
282
  - spec/lib/sprig_spec.rb
251
283
  - spec/spec_helper.rb
252
284
  - spec/sprig_spec.rb
253
285
  - spec/support/helpers/colored_text.rb
254
286
  - spec/support/helpers/logger_mock.rb
255
287
  - spec/support/shared_examples/a_logging_entity.rb
288
+ has_rdoc: