dronr 0.0.2

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 (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +2 -0
  4. data/CHANGELOG +0 -0
  5. data/COMINGSOON +108 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +82 -0
  9. data/Rakefile +2 -0
  10. data/TODO +4 -0
  11. data/bin/dronr +6 -0
  12. data/dronr.gemspec +29 -0
  13. data/lib/dronr/cli.rb +76 -0
  14. data/lib/dronr/drone.rb +44 -0
  15. data/lib/dronr/drones/cleanup.rb +11 -0
  16. data/lib/dronr/drones/compass.rb +15 -0
  17. data/lib/dronr/drones/dotenv.rb +15 -0
  18. data/lib/dronr/drones/draper.rb +11 -0
  19. data/lib/dronr/drones/dronr.rb +19 -0
  20. data/lib/dronr/drones/factory_girl.rb +11 -0
  21. data/lib/dronr/drones/foundation.rb +15 -0
  22. data/lib/dronr/drones/foundation_icons.rb +15 -0
  23. data/lib/dronr/drones/git.rb +11 -0
  24. data/lib/dronr/drones/heroku.rb +11 -0
  25. data/lib/dronr/drones/migrant.rb +11 -0
  26. data/lib/dronr/drones/postgresql.rb +15 -0
  27. data/lib/dronr/drones/rspec.rb +20 -0
  28. data/lib/dronr/drones/slim.rb +11 -0
  29. data/lib/dronr/drones/underscore.rb +15 -0
  30. data/lib/dronr/drones.rb +23 -0
  31. data/lib/dronr/manager.rb +167 -0
  32. data/lib/dronr/register/canonical.yml +37 -0
  33. data/lib/dronr/register/standard.yml +27 -0
  34. data/lib/dronr/register.rb +91 -0
  35. data/lib/dronr/version.rb +3 -0
  36. data/lib/dronr.rb +17 -0
  37. data/spec/dronr/drone_spec.rb +75 -0
  38. data/spec/dronr/register_spec.rb +152 -0
  39. data/spec/fixtures/register_drone_not_found.yml +8 -0
  40. data/spec/fixtures/register_invalid.yml +8 -0
  41. data/spec/fixtures/register_keys_missing.yml +2 -0
  42. data/spec/fixtures/register_malformed.yml +7 -0
  43. data/spec/fixtures/register_template.yml +9 -0
  44. data/spec/spec_helper.rb +18 -0
  45. data/spec/support/fixture_path.rb +9 -0
  46. data/spec/support/sample_drones.rb +15 -0
  47. data/spec/tmp/dronr.yml +7 -0
  48. metadata +200 -0
@@ -0,0 +1,167 @@
1
+ require 'rails/generators/rails/app/app_generator'
2
+
3
+ module Dronr
4
+ class Manager
5
+
6
+ attr_accessor :app_generator, :register
7
+
8
+ def initialize(app_generator, register=Register.new)
9
+ @app_generator = app_generator
10
+ @register = register
11
+ end
12
+
13
+ def self.from_scratch(app_name, template_register, user_params={})
14
+
15
+ opts = { skip_bundle: true }
16
+
17
+ template_register.drones.each do |drone|
18
+ opts.merge! drone.args_for_app_create
19
+ end
20
+
21
+ generator = Rails::Generators::AppGenerator.new([app_name], opts)
22
+ generator.invoke_all
23
+
24
+ # BOOTSTRAP
25
+ manager = new(generator, template_register)
26
+
27
+ manager.say_hello
28
+
29
+ dronr = Drones::Dronr.new(:incoming)
30
+ manager.bundle_drone(dronr)
31
+ manager.run_bundle
32
+ manager.install_drone(dronr)
33
+
34
+ manager.register.drones << dronr
35
+
36
+ manager.commit_register
37
+
38
+ manager.app_generator.run 'bin/dronr up'
39
+ end
40
+
41
+
42
+ def self.existing(options={})
43
+
44
+ generator = Rails::Generators::AppGenerator.new [Rails.root], {}, destination_root: Rails.root
45
+
46
+ manager = new(generator)
47
+
48
+ manager.load_register
49
+
50
+ canonical_register = Register.load_canonical
51
+ manager.register.sort_by_example_register(canonical_register)
52
+
53
+ manager.up
54
+
55
+ manager.commit_register
56
+
57
+ manager.say_goodbye
58
+ end
59
+
60
+ # def bootstrap
61
+ # --- add dronr to register & up
62
+ # end
63
+
64
+ # def stage
65
+ # # for each drone in 'new' register
66
+ # # sort by canonical register
67
+ # # say 'staging for x drones'
68
+
69
+ # # output all commands..
70
+ # end
71
+
72
+ def commit_register
73
+ @register.commit(register_path)
74
+ end
75
+
76
+ def load_register
77
+ @register = Register.load(register_path)
78
+ end
79
+
80
+ def up
81
+ incoming_drones = @register.incoming_drones.dup
82
+ bundled_drones = @register.bundled_drones.dup
83
+
84
+ # ----------------
85
+ # 1. BUNDLING new drones
86
+
87
+ incoming_drones.each do |drone|
88
+ bundle_drone(drone)
89
+ end
90
+
91
+ if incoming_drones.map(&:class).detect(&:bundle_block)
92
+ run_bundle
93
+ end
94
+
95
+ # ------------
96
+ # 2. INSTALLING bundled drones
97
+
98
+ bundled_drones.each do |drone|
99
+ install_drone(drone)
100
+ end
101
+
102
+ say_status :dronr, "Finishing..", :blue
103
+
104
+ bundled_drones.each do |drone|
105
+ finish_drone(drone)
106
+ end
107
+
108
+ end
109
+
110
+ def bundle_drone(drone)
111
+ say_status :dronr, "Bundling #{drone.human_name}", :blue
112
+
113
+ if block = drone.class.bundle_block
114
+ @app_generator.instance_eval(&block)
115
+ end
116
+
117
+ drone.state = :bundled
118
+ end
119
+
120
+ def install_drone(drone)
121
+ say_status :dronr, "Installing #{drone.human_name}", :yellow
122
+
123
+ if block = drone.class.install_block
124
+ @app_generator.instance_eval(&block)
125
+ end
126
+ end
127
+
128
+ def finish_drone(drone)
129
+
130
+ if block = drone.class.finish_block
131
+ @app_generator.instance_eval(&block)
132
+ end
133
+
134
+ drone.state = :finished
135
+ end
136
+
137
+
138
+ def say_status(*opts)
139
+ @app_generator.say_status(*opts)
140
+ end
141
+
142
+ def say_hello
143
+ say_status :dronr, 'Hello :)', :blue
144
+ end
145
+
146
+ def say_goodbye
147
+ if register.finished_drones.count == register.drones.count
148
+ say_status :dronr, 'All yours, sparky!', :blue
149
+ else
150
+ say_status :dronr, 'Completed iteration but some drones are unfinished. To continue working, run `bin/dronr up` in the project root.', :red
151
+ end
152
+ end
153
+
154
+ def run_bundle
155
+ @app_generator.send :bundle_command, 'install --quiet'
156
+ end
157
+
158
+ def app_root
159
+ @app_generator.instance_variable_get(:@destination_stack).first
160
+ end
161
+
162
+ def register_path
163
+ File.join(app_root, '.dronr.yml')
164
+ end
165
+
166
+ end
167
+ end
@@ -0,0 +1,37 @@
1
+ incoming:
2
+ # core:
3
+
4
+ # app:
5
+
6
+ # database:
7
+ - postgresql
8
+ - migrant
9
+
10
+ # templating:
11
+ - slim
12
+
13
+ # decorating:
14
+ - draper
15
+
16
+ # assets:
17
+ - compass
18
+ - foundation
19
+ - foundation_icons
20
+ - underscore
21
+
22
+ # test:
23
+ - rspec
24
+ - factory_girl
25
+
26
+ # deploy:
27
+ - heroku
28
+
29
+ # misc
30
+ - dotenv
31
+ - cleanup
32
+ - git
33
+
34
+ bundled:
35
+
36
+ finished:
37
+
@@ -0,0 +1,27 @@
1
+ incoming:
2
+
3
+ - postgresql
4
+ - migrant
5
+
6
+ - slim
7
+ - draper
8
+
9
+ - compass
10
+ - foundation
11
+ - foundation_icons
12
+
13
+ - underscore
14
+
15
+ - dotenv
16
+ - rspec
17
+ - factory_girl
18
+
19
+ - heroku
20
+
21
+ - cleanup
22
+ - git
23
+
24
+ bundled:
25
+
26
+ finished:
27
+
@@ -0,0 +1,91 @@
1
+ require 'yaml'
2
+
3
+ module Dronr
4
+
5
+ class Register
6
+
7
+ CANONICAL_PATH = '..'
8
+
9
+ class MalformedRegisterError < StandardError
10
+ end
11
+
12
+ class InvalidRegisterError < StandardError
13
+ end
14
+
15
+ %W{incoming bundled finished}.each do |state|
16
+ define_method "#{state}_drones" do
17
+ drones.select do |drone|
18
+ drone.state == state.to_sym
19
+ end
20
+ end
21
+ end
22
+
23
+ attr_reader :drones
24
+
25
+ def initialize(drones=[])
26
+ @drones = drones
27
+ end
28
+
29
+ def self.load_canonical
30
+ canonical_path = File.expand_path '../register/canonical.yml', __FILE__
31
+ load(canonical_path)
32
+ end
33
+
34
+ def self.load(path)
35
+ begin
36
+ yaml = YAML.load_file(path)
37
+ rescue Psych::SyntaxError
38
+ raise MalformedRegisterError.new(yaml), 'is not valid YAML'
39
+ end
40
+
41
+ unless yaml.has_key?('incoming') && yaml.has_key?('bundled') && yaml.has_key?('finished')
42
+ raise MalformedRegisterError.new(yaml), 'is missing required keys (incoming, bundled, finished)'
43
+ end
44
+
45
+ drones = %w{incoming bundled finished}.map do |state|
46
+ (yaml[state] || []).map do |drone_name|
47
+ klass = Drone.find_class_by_human_name(drone_name)
48
+ klass.new(state.to_sym)
49
+ end
50
+ end.flatten
51
+
52
+ new(drones).tap do |register|
53
+ unless register.valid?
54
+ raise InvalidRegisterError.new(register), "Couldnt load register at #{path}"
55
+ end
56
+ end
57
+ end
58
+
59
+ def valid?
60
+ has_valid_drones = drones.all? do |drone|
61
+ drone && (drone.class < Drone)
62
+ end
63
+
64
+ has_no_duplicate_drones = (drones.count == drones.map(&:class).uniq.count)
65
+
66
+ has_valid_drones && has_no_duplicate_drones
67
+ end
68
+
69
+ def sort_by_example_register(example_register)
70
+ @drones = example_register.drones.map do |drone|
71
+ @drones.detect{ |d| d.class == drone.class }
72
+ end.compact
73
+ end
74
+
75
+ def to_yaml
76
+ {
77
+ 'incoming' => incoming_drones.map(&:human_name).presence,
78
+ 'bundled' => bundled_drones.map(&:human_name).presence,
79
+ 'finished' => finished_drones.map(&:human_name).presence
80
+ }.to_yaml
81
+ end
82
+
83
+ def commit(path)
84
+ File.open(path, 'w+') do |f|
85
+ f.write(to_yaml)
86
+ end
87
+ end
88
+
89
+ end
90
+
91
+ end
@@ -0,0 +1,3 @@
1
+ module Dronr
2
+ VERSION = "0.0.2"
3
+ end
data/lib/dronr.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'dronr/version'
2
+ require 'pry'
3
+
4
+ require 'rails'
5
+ require 'rails/generators'
6
+
7
+ module Dronr
8
+
9
+ extend ActiveSupport::Autoload
10
+
11
+ autoload :CLI
12
+ autoload :Drone
13
+ autoload :Drones
14
+ autoload :Manager
15
+ autoload :Register
16
+
17
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ module Dronr
4
+
5
+ describe Drone do
6
+
7
+ describe '.find_class_by_human_name' do
8
+
9
+ it 'returns the correct class when a match exists' do
10
+ drone = Drone.find_class_by_human_name('drone1')
11
+ expect(drone).to eq Drones::Drone1
12
+ end
13
+
14
+ it 'raises an error if a matching drone cant be found' do
15
+ expect do
16
+ Drone.find_class_by_human_name('drone90210')
17
+ end.to raise_error(Drone::DroneNotFoundError)
18
+ end
19
+ end
20
+
21
+ describe '#human_name' do
22
+
23
+ it 'humanizes the class name' do
24
+ expect(Drones::Drone1.new.human_name).to eq 'drone1'
25
+ # TODO test more complex options
26
+ end
27
+
28
+ end
29
+
30
+ describe 'directive DSL' do
31
+
32
+ class TestableDrone1 < Drone
33
+ bundle do
34
+ 'TestableDrone1 #bundle'
35
+ end
36
+
37
+ install do
38
+ 'TestableDrone1 #install'
39
+ end
40
+
41
+ finish do
42
+ 'TestableDrone1 #finish'
43
+ end
44
+ end
45
+
46
+ class TestableDrone2 < Drone
47
+ bundle do
48
+ 'TestableDrone2 #bundle'
49
+ end
50
+
51
+ install do
52
+ 'TestableDrone2 #install'
53
+ end
54
+
55
+ finish do
56
+ 'TestableDrone2 #finish'
57
+ end
58
+ end
59
+
60
+
61
+ it 'captures unique #bundle and #install directives' do
62
+ expect(TestableDrone1.bundle_block.call).to eq 'TestableDrone1 #bundle'
63
+ expect(TestableDrone1.install_block.call).to eq 'TestableDrone1 #install'
64
+ expect(TestableDrone1.finish_block.call).to eq 'TestableDrone1 #finish'
65
+
66
+ expect(TestableDrone2.bundle_block.call).to eq 'TestableDrone2 #bundle'
67
+ expect(TestableDrone2.install_block.call).to eq 'TestableDrone2 #install'
68
+ expect(TestableDrone2.finish_block.call).to eq 'TestableDrone2 #finish'
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -0,0 +1,152 @@
1
+ require 'spec_helper'
2
+
3
+ module Dronr
4
+
5
+ describe Register do
6
+
7
+ describe '#load_canonical' do
8
+ it 'loads the canonical register' do
9
+ expect(Register.load_canonical).to be_a(Register)
10
+ end
11
+ end
12
+
13
+ describe '#load' do
14
+
15
+ it 'parses a YML file at the given path into a valid Register' do
16
+ register = Register.load fixture_path('register_template.yml')
17
+
18
+ expect(register.incoming_drones.map(&:class)).to eq([Drones::Drone2, Drones::Drone1])
19
+ expect(register.bundled_drones.map(&:class)).to eq([Drones::Drone4])
20
+ expect(register.finished_drones.map(&:class)).to eq([Drones::Drone3])
21
+ end
22
+
23
+ it 'raises an error if YML file is malformed' do
24
+ expect do
25
+ register = Register.load fixture_path('register_malformed.yml')
26
+ end.to raise_error(Register::MalformedRegisterError)
27
+ end
28
+
29
+ it 'raises an error if YML file is missing keys' do
30
+ expect do
31
+ register = Register.load fixture_path('register_keys_missing.yml')
32
+ end.to raise_error(Register::MalformedRegisterError)
33
+ end
34
+
35
+ it 'raises an error if a named drone couldnt be found' do
36
+ expect do
37
+ register = Register.load fixture_path('register_drone_not_found.yml')
38
+ end.to raise_error(Drone::DroneNotFoundError)
39
+ end
40
+
41
+ it 'raises an error if the register is invalid' do
42
+ expect do
43
+ register = Register.load fixture_path('register_invalid.yml')
44
+ end.to raise_error(Register::InvalidRegisterError)
45
+ end
46
+
47
+ end
48
+
49
+ describe '#sort_by_example_register' do
50
+
51
+ it 'returns drones ordered correctly' do
52
+
53
+ canonical_register = Register.new([
54
+ Drones::Drone1.new(:incoming),
55
+ Drones::Drone2.new(:incoming),
56
+ Drones::Drone3.new(:incoming),
57
+ Drones::Drone4.new(:incoming)
58
+ ])
59
+
60
+ drone4 = Drones::Drone4.new(:bundled)
61
+ drone1 = Drones::Drone1.new(:incoming)
62
+ drone3 = Drones::Drone3.new(:finished)
63
+
64
+ register = Register.new([drone4, drone1, drone3])
65
+
66
+ register.sort_by_example_register(canonical_register)
67
+ expect(register.drones).to eq [drone1, drone3, drone4]
68
+ end
69
+
70
+ end
71
+
72
+
73
+ describe '#valid?' do
74
+
75
+ it 'returns true when all entries are unique subclasses of Drone' do
76
+ register = Register.new([
77
+ Drones::Drone1.new(:incoming),
78
+ Drones::Drone2.new(:bundled),
79
+ Drones::Drone3.new(:finished)
80
+ ])
81
+ expect(register).to be_valid
82
+ end
83
+
84
+ it 'returns false if there are duplicate drones of the same kind' do
85
+ register = Register.new([
86
+ Drones::Drone1.new(:incoming),
87
+ Drones::Drone1.new(:incoming)
88
+ ])
89
+ expect(register).to_not be_valid
90
+ end
91
+
92
+ it 'returns false if a particular drone appears in both arrays' do
93
+ register = Register.new([
94
+ Drones::Drone1.new(:incoming),
95
+ Drones::Drone1.new(:bundled),
96
+ Drones::Drone2.new(:bundled)
97
+ ])
98
+ expect(register).to_not be_valid
99
+ end
100
+
101
+ it 'returns false unless all entries are subclasses of Drone' do
102
+ register = Register.new([
103
+ Drones::Drone1.new(:incoming),
104
+ nil
105
+ ])
106
+ expect(register).to_not be_valid
107
+ end
108
+ end
109
+
110
+ describe '#to_yml' do
111
+
112
+ it 'correctly generates a YML file' do
113
+ register = Register.new([
114
+ Drones::Drone1.new(:incoming),
115
+ Drones::Drone2.new(:finished),
116
+ Drones::Drone3.new(:finished)
117
+ ])
118
+
119
+ yaml = YAML.load(register.to_yaml)
120
+
121
+ expect(yaml['incoming']).to eq %w{drone1}
122
+ expect(yaml['bundled']).to eq nil
123
+ expect(yaml['finished']).to eq %w{drone2 drone3}
124
+ end
125
+
126
+ end
127
+
128
+ describe '#commit' do
129
+
130
+ it 'serializes to yaml and saves at the given path' do
131
+
132
+ register = Register.new([
133
+ Drones::Drone1.new(:incoming),
134
+ Drones::Drone2.new(:bundled),
135
+ Drones::Drone3.new(:finished)
136
+ ])
137
+
138
+ path = File.expand_path '../../tmp/dronr.yml', __FILE__
139
+
140
+ File.delete(path) if File.exists?(path)
141
+ expect(File.exists?(path)).to eq false
142
+
143
+ register.commit(path)
144
+
145
+ expect(File.exists?(path)).to eq true
146
+ expect(Register.load(path)).to be_valid
147
+ end
148
+ end
149
+
150
+ end
151
+
152
+ end
@@ -0,0 +1,8 @@
1
+ incoming:
2
+ - drone2
3
+ - drone1
4
+
5
+ bundled:
6
+
7
+ finished:
8
+ - drone90210
@@ -0,0 +1,8 @@
1
+ incoming:
2
+ - drone3
3
+ - drone1
4
+
5
+ bundled:
6
+
7
+ finished:
8
+ - drone3
@@ -0,0 +1,2 @@
1
+ finished:
2
+ - drone3
@@ -0,0 +1,7 @@
1
+ - drone2
2
+ - drone1
3
+
4
+ finished:
5
+
6
+ - drone3
7
+
@@ -0,0 +1,9 @@
1
+ incoming:
2
+ - drone2
3
+ - drone1
4
+
5
+ bundled:
6
+ - drone4
7
+
8
+ finished:
9
+ - drone3
@@ -0,0 +1,18 @@
1
+ require 'dronr'
2
+
3
+ require 'support/sample_drones'
4
+ require 'support/fixture_path'
5
+
6
+ RSpec.configure do |config|
7
+
8
+ config.expect_with :rspec do |expectations|
9
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
10
+ end
11
+
12
+ config.mock_with :rspec do |mocks|
13
+ mocks.verify_partial_doubles = true
14
+ end
15
+
16
+ config.include Helpers::FixturePath
17
+
18
+ end
@@ -0,0 +1,9 @@
1
+ module Helpers
2
+ module FixturePath
3
+
4
+ def fixture_path(file_path)
5
+ File.expand_path("../../fixtures/#{file_path}", __FILE__)
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ module Dronr
2
+ module Drones
3
+ class Drone1 < Drone
4
+ end
5
+
6
+ class Drone2 < Drone
7
+ end
8
+
9
+ class Drone3 < Drone
10
+ end
11
+
12
+ class Drone4 < Drone
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ ---
2
+ incoming:
3
+ - drone1
4
+ bundled:
5
+ - drone2
6
+ finished:
7
+ - drone3