sprig 0.1.0
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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +131 -0
- data/Rakefile +5 -0
- data/lib/sprig.rb +29 -0
- data/lib/sprig/configuration.rb +16 -0
- data/lib/sprig/data.rb +6 -0
- data/lib/sprig/dependency.rb +45 -0
- data/lib/sprig/dependency_collection.rb +23 -0
- data/lib/sprig/dependency_sorter.rb +83 -0
- data/lib/sprig/directive.rb +37 -0
- data/lib/sprig/directive_list.rb +30 -0
- data/lib/sprig/helpers.rb +25 -0
- data/lib/sprig/parser.rb +9 -0
- data/lib/sprig/parser/base.rb +15 -0
- data/lib/sprig/parser/csv.rb +22 -0
- data/lib/sprig/parser/google_spreadsheet_json.rb +35 -0
- data/lib/sprig/parser/json.rb +9 -0
- data/lib/sprig/parser/yml.rb +9 -0
- data/lib/sprig/planter.rb +39 -0
- data/lib/sprig/seed.rb +9 -0
- data/lib/sprig/seed/attribute.rb +54 -0
- data/lib/sprig/seed/attribute_collection.rb +33 -0
- data/lib/sprig/seed/entry.rb +80 -0
- data/lib/sprig/seed/factory.rb +56 -0
- data/lib/sprig/seed/record.rb +35 -0
- data/lib/sprig/source.rb +143 -0
- data/lib/sprig/sprig_logger.rb +68 -0
- data/lib/sprig/sprig_record_store.rb +31 -0
- data/lib/sprig/version.rb +3 -0
- data/spec/db/activerecord.db +0 -0
- data/spec/feature/configurations_spec.rb +30 -0
- data/spec/fixtures/cassettes/google_spreadsheet_json_posts.yml +60 -0
- data/spec/fixtures/models/comment.rb +5 -0
- data/spec/fixtures/models/post.rb +2 -0
- data/spec/fixtures/seeds/staging/posts.yml +6 -0
- data/spec/fixtures/seeds/test/comments.yml +6 -0
- data/spec/fixtures/seeds/test/legacy_posts.yml +6 -0
- data/spec/fixtures/seeds/test/posts.csv +2 -0
- data/spec/fixtures/seeds/test/posts.json +1 -0
- data/spec/fixtures/seeds/test/posts.yml +6 -0
- data/spec/fixtures/seeds/test/posts_find_existing_by_multiple.yml +9 -0
- data/spec/fixtures/seeds/test/posts_find_existing_by_single.yml +8 -0
- data/spec/fixtures/seeds/test/posts_missing_dependency.yml +7 -0
- data/spec/lib/sprig/configuration_spec.rb +21 -0
- data/spec/lib/sprig/directive_list_spec.rb +24 -0
- data/spec/lib/sprig/directive_spec.rb +60 -0
- data/spec/spec_helper.rb +75 -0
- data/spec/sprig_spec.rb +205 -0
- metadata +211 -0
data/lib/sprig/source.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
module Sprig
|
2
|
+
class Source
|
3
|
+
|
4
|
+
def initialize(table_name, args = {})
|
5
|
+
@table_name = table_name
|
6
|
+
@args = args
|
7
|
+
end
|
8
|
+
|
9
|
+
def records
|
10
|
+
data[:records] || []
|
11
|
+
end
|
12
|
+
|
13
|
+
def options
|
14
|
+
data[:options] || {}
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :table_name, :args
|
20
|
+
|
21
|
+
def data
|
22
|
+
@data ||= begin
|
23
|
+
parser_class.new(source).parse.to_hash.with_indifferent_access
|
24
|
+
ensure
|
25
|
+
source.close
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def source
|
30
|
+
@source ||= begin
|
31
|
+
source = args.fetch(:source) { default_source }
|
32
|
+
|
33
|
+
unless source.respond_to?(:read) && source.respond_to?(:close)
|
34
|
+
raise ArgumentError, 'Data sources must act like an IO.'
|
35
|
+
end
|
36
|
+
|
37
|
+
source
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def parser_class
|
42
|
+
@parser_class ||= begin
|
43
|
+
parser_class = args.fetch(:parser) { default_parser_class }
|
44
|
+
|
45
|
+
unless parser_class.method_defined?(:parse)
|
46
|
+
raise ArgumentError, 'Parsers must define #parse.'
|
47
|
+
end
|
48
|
+
|
49
|
+
parser_class
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def default_source
|
54
|
+
File.open(SourceDeterminer.new(table_name).file)
|
55
|
+
end
|
56
|
+
|
57
|
+
def default_parser_class
|
58
|
+
ParserDeterminer.new(source).parser
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
class SourceDeterminer
|
63
|
+
attr_reader :table_name
|
64
|
+
|
65
|
+
def initialize(table_name)
|
66
|
+
@table_name = table_name
|
67
|
+
end
|
68
|
+
|
69
|
+
def file
|
70
|
+
File.new(seed_directory.join(filename))
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
class FileNotFoundError < StandardError; end
|
76
|
+
|
77
|
+
def filename
|
78
|
+
available_files.detect {|name| name =~ /^#{table_name}\./ } || file_not_found
|
79
|
+
end
|
80
|
+
|
81
|
+
def available_files
|
82
|
+
Dir.entries(seed_directory)
|
83
|
+
end
|
84
|
+
|
85
|
+
def file_not_found
|
86
|
+
raise FileNotFoundError,
|
87
|
+
"No datasource file could be found for '#{table_name}'. Try creating "\
|
88
|
+
"#{table_name}.yml, #{table_name}.json, or #{table_name}.csv within "\
|
89
|
+
"#{seed_directory}, or define a custom datasource."
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
class ParserDeterminer
|
95
|
+
|
96
|
+
def initialize(file)
|
97
|
+
@file = file
|
98
|
+
end
|
99
|
+
|
100
|
+
def parser
|
101
|
+
match = parser_matchers.detect {|p| p[:extension] =~ extension } || unparsable_file
|
102
|
+
match[:parser]
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
class UnparsableFileError < StandardError; end
|
108
|
+
|
109
|
+
attr_reader :file
|
110
|
+
|
111
|
+
def extension
|
112
|
+
File.extname(file)
|
113
|
+
end
|
114
|
+
|
115
|
+
def parser_matchers
|
116
|
+
[
|
117
|
+
{
|
118
|
+
extension: /\.y(a)?ml/i,
|
119
|
+
parser: Sprig::Parser::Yml
|
120
|
+
},
|
121
|
+
{
|
122
|
+
extension: /\.json/i,
|
123
|
+
parser: Sprig::Parser::Json
|
124
|
+
},
|
125
|
+
{
|
126
|
+
extension: /\.csv/i,
|
127
|
+
parser: Sprig::Parser::Csv
|
128
|
+
}
|
129
|
+
]
|
130
|
+
end
|
131
|
+
|
132
|
+
def parsable_formats
|
133
|
+
['YAML', 'JSON', 'CSV']
|
134
|
+
end
|
135
|
+
|
136
|
+
def unparsable_file
|
137
|
+
raise UnparsableFileError,
|
138
|
+
"No parser was found for the file '#{file}'. Provide a custom parser, or "\
|
139
|
+
"use a supported data format (#{parsable_formats})."
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
|
3
|
+
module Sprig
|
4
|
+
class SprigLogger
|
5
|
+
def initialize
|
6
|
+
@success_count = 0
|
7
|
+
@error_count = 0
|
8
|
+
@errors = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def log_success(seed)
|
12
|
+
message = seed.success_log_text
|
13
|
+
puts green(message)
|
14
|
+
@success_count += 1
|
15
|
+
end
|
16
|
+
|
17
|
+
def log_error(seed)
|
18
|
+
message = seed.error_log_text
|
19
|
+
@errors << seed.record
|
20
|
+
puts red(message)
|
21
|
+
@error_count += 1
|
22
|
+
end
|
23
|
+
|
24
|
+
def log_summary
|
25
|
+
puts 'Seeding complete.'
|
26
|
+
|
27
|
+
if @success_count > 0
|
28
|
+
puts green(success_summary)
|
29
|
+
else
|
30
|
+
puts red(success_summary)
|
31
|
+
end
|
32
|
+
|
33
|
+
if @error_count > 0
|
34
|
+
puts red(error_summary)
|
35
|
+
|
36
|
+
@errors.each do |error|
|
37
|
+
puts red("#{error}\n#{error.errors.messages}\n\n")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def processing
|
43
|
+
print "Planting those seeds...\r"
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def colorize(message, color_code)
|
49
|
+
"\e[#{color_code}m#{message}\e[0m"
|
50
|
+
end
|
51
|
+
|
52
|
+
def red(message)
|
53
|
+
colorize(message, 31)
|
54
|
+
end
|
55
|
+
|
56
|
+
def green(message)
|
57
|
+
colorize(message, 32)
|
58
|
+
end
|
59
|
+
|
60
|
+
def success_summary
|
61
|
+
"#{@success_count} #{'seed'.pluralize(@success_count)} successfully planted."
|
62
|
+
end
|
63
|
+
|
64
|
+
def error_summary
|
65
|
+
"#{@error_count} #{'seed'.pluralize(@error_count)} couldn't be planted:"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Sprig
|
4
|
+
class SprigRecordStore
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
class RecordNotFoundError < StandardError;end
|
8
|
+
|
9
|
+
def save(record, sprig_id)
|
10
|
+
records_of_klass(record.class)[sprig_id.to_s] = record
|
11
|
+
end
|
12
|
+
|
13
|
+
def get(klass, sprig_id)
|
14
|
+
records_of_klass(klass)[sprig_id.to_s] || record_not_found
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def records_of_klass(klass)
|
20
|
+
records[klass.name.tableize] ||= {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def records
|
24
|
+
@records ||= {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def record_not_found
|
28
|
+
raise(RecordNotFoundError, "Record for class #{klass} and sprig_id #{sprig_id} could not be found.")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
Binary file
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Configurating Sprig" do
|
4
|
+
before do
|
5
|
+
stub_rails_root '~'
|
6
|
+
stub_rails_env 'development'
|
7
|
+
end
|
8
|
+
|
9
|
+
it "can set the directory" do
|
10
|
+
Sprig.configuration.directory.to_path.should == '~/db/seeds/development'
|
11
|
+
|
12
|
+
Sprig.configure do |c|
|
13
|
+
c.directory = 'seed_files'
|
14
|
+
end
|
15
|
+
|
16
|
+
Sprig.configuration.directory.to_path.should == '~/seed_files/development'
|
17
|
+
end
|
18
|
+
|
19
|
+
it "can reset the configuration" do
|
20
|
+
Sprig.configure do |c|
|
21
|
+
c.directory = 'seed_files'
|
22
|
+
end
|
23
|
+
|
24
|
+
Sprig.configuration.directory.to_path.should == '~/seed_files/development'
|
25
|
+
|
26
|
+
Sprig.reset_configuration
|
27
|
+
|
28
|
+
Sprig.configuration.directory.to_path.should == '~/db/seeds/development'
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://spreadsheets.google.com/feeds/list/0AjVLPMnHm86rdDVHQ2dCUS03RTN5ZUtVNzVOYVBwT0E/1/public/values?alt=json
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Accept:
|
11
|
+
- ! '*/*'
|
12
|
+
User-Agent:
|
13
|
+
- Ruby
|
14
|
+
response:
|
15
|
+
status:
|
16
|
+
code: 200
|
17
|
+
message: OK
|
18
|
+
headers:
|
19
|
+
Content-Type:
|
20
|
+
- application/json; charset=UTF-8
|
21
|
+
Access-Control-Allow-Origin:
|
22
|
+
- ! '*'
|
23
|
+
Expires:
|
24
|
+
- Thu, 02 Jan 2014 22:06:43 GMT
|
25
|
+
Date:
|
26
|
+
- Thu, 02 Jan 2014 22:06:43 GMT
|
27
|
+
Cache-Control:
|
28
|
+
- private, max-age=0, must-revalidate, no-transform
|
29
|
+
Vary:
|
30
|
+
- Accept, X-GData-Authorization, GData-Version
|
31
|
+
Gdata-Version:
|
32
|
+
- '1.0'
|
33
|
+
Last-Modified:
|
34
|
+
- Thu, 02 Jan 2014 21:46:27 GMT
|
35
|
+
Set-Cookie:
|
36
|
+
- NID=67=twk9_9Iqw6h1ZztnpEkt7dtJJIvFCbyeEjQhC5XRwV7jMa4uPJoArpItl5KnmhwPif-owRtCYsUwyChgEMO7GDroZPOFBOV0KvMLzUQmlSn-0tGxTefBSvdVHJ8MIHsi;Domain=.google.com;Path=/;Expires=Fri,
|
37
|
+
04-Jul-2014 22:06:43 GMT;HttpOnly
|
38
|
+
P3p:
|
39
|
+
- CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657
|
40
|
+
for more info."
|
41
|
+
X-Content-Type-Options:
|
42
|
+
- nosniff
|
43
|
+
X-Frame-Options:
|
44
|
+
- SAMEORIGIN
|
45
|
+
X-Xss-Protection:
|
46
|
+
- 1; mode=block
|
47
|
+
Server:
|
48
|
+
- GSE
|
49
|
+
Alternate-Protocol:
|
50
|
+
- 443:quic
|
51
|
+
Transfer-Encoding:
|
52
|
+
- chunked
|
53
|
+
body:
|
54
|
+
encoding: US-ASCII
|
55
|
+
string: ! '{"version":"1.0","encoding":"UTF-8","feed":{"xmlns":"http://www.w3.org/2005/Atom","xmlns$openSearch":"http://a9.com/-/spec/opensearchrss/1.0/","xmlns$gsx":"http://schemas.google.com/spreadsheets/2006/extended","id":{"$t":"https://spreadsheets.google.com/feeds/list/0AjVLPMnHm86rdDVHQ2dCUS03RTN5ZUtVNzVOYVBwT0E/1/public/values"},"updated":{"$t":"2014-01-02T21:46:27.808Z"},"category":[{"scheme":"http://schemas.google.com/spreadsheets/2006","term":"http://schemas.google.com/spreadsheets/2006#list"}],"title":{"type":"text","$t":"Sheet1"},"link":[{"rel":"alternate","type":"text/html","href":"https://spreadsheets.google.com/pub?key\u003d0AjVLPMnHm86rdDVHQ2dCUS03RTN5ZUtVNzVOYVBwT0E"},{"rel":"http://schemas.google.com/g/2005#feed","type":"application/atom+xml","href":"https://spreadsheets.google.com/feeds/list/0AjVLPMnHm86rdDVHQ2dCUS03RTN5ZUtVNzVOYVBwT0E/1/public/values"},{"rel":"self","type":"application/atom+xml","href":"https://spreadsheets.google.com/feeds/list/0AjVLPMnHm86rdDVHQ2dCUS03RTN5ZUtVNzVOYVBwT0E/1/public/values?alt\u003djson"}],"author":[{"name":{"$t":"ryan.foster"},"email":{"$t":"ryan.foster@viget.com"}}],"openSearch$totalResults":{"$t":"1"},"openSearch$startIndex":{"$t":"1"},"entry":[{"id":{"$t":"https://spreadsheets.google.com/feeds/list/0AjVLPMnHm86rdDVHQ2dCUS03RTN5ZUtVNzVOYVBwT0E/1/public/values/cokwr"},"updated":{"$t":"2014-01-02T21:46:27.808Z"},"category":[{"scheme":"http://schemas.google.com/spreadsheets/2006","term":"http://schemas.google.com/spreadsheets/2006#list"}],"title":{"type":"text","$t":"1"},"content":{"type":"text","$t":"title:
|
56
|
+
Google spreadsheet json title, content: Google spreadsheet json content"},"link":[{"rel":"self","type":"application/atom+xml","href":"https://spreadsheets.google.com/feeds/list/0AjVLPMnHm86rdDVHQ2dCUS03RTN5ZUtVNzVOYVBwT0E/1/public/values/cokwr"}],"gsx$sprig-id":{"$t":"1"},"gsx$title":{"$t":"Google
|
57
|
+
spreadsheet json title"},"gsx$content":{"$t":"Google spreadsheet json content"}}]}}'
|
58
|
+
http_version:
|
59
|
+
recorded_at: Thu, 02 Jan 2014 22:06:42 GMT
|
60
|
+
recorded_with: VCR 2.8.0
|
@@ -0,0 +1 @@
|
|
1
|
+
{"records":[{"sprig_id":1,"title":"Json title","content":"Json content"}]}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Sprig::Configuration do
|
4
|
+
before do
|
5
|
+
stub_rails_root '~'
|
6
|
+
stub_rails_env 'development'
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "#directory" do
|
10
|
+
|
11
|
+
it "returns db/seeds/:env by default" do
|
12
|
+
subject.directory.to_path.should == '~/db/seeds/development'
|
13
|
+
end
|
14
|
+
|
15
|
+
it "returns a custom directory" do
|
16
|
+
subject.directory = 'seed_files'
|
17
|
+
|
18
|
+
subject.directory.to_path.should == '~/seed_files/development'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Sprig::DirectiveList do
|
4
|
+
|
5
|
+
describe "#add_seeds_to_hopper" do
|
6
|
+
let(:hopper) { Array.new }
|
7
|
+
let(:directive) { double('directive') }
|
8
|
+
let(:seed_factory) { double('seed_factory') }
|
9
|
+
|
10
|
+
subject { described_class.new(Post) }
|
11
|
+
|
12
|
+
before do
|
13
|
+
Sprig::Directive.stub(:new).with(Post).and_return(directive)
|
14
|
+
|
15
|
+
Sprig::Seed::Factory.stub(:new_from_directive).with(directive).and_return(seed_factory)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "builds seeds from directives and adds to the given array" do
|
19
|
+
seed_factory.should_receive(:add_seeds_to_hopper).with(hopper)
|
20
|
+
|
21
|
+
subject.add_seeds_to_hopper(hopper)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|