vuderacha-syrup 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.
@@ -0,0 +1,71 @@
1
+ # Handler class for actually running an application
2
+ module Syrup
3
+ class Runner
4
+ # Initializes the runner with the given configuration store
5
+ def initialize(config_store, logger)
6
+ @config_store = config_store
7
+ @logger = logger
8
+ end
9
+
10
+ # Runs the application
11
+ def run(app_name)
12
+ @logger.debug "Preparing to execute #{app_name}"
13
+ app = @config_store.applications[app_name]
14
+
15
+ # Gather the properties
16
+ props = {}
17
+ # {}.merge! won't work since the config_store properties aren't a real hash...
18
+ @config_store.properties.each do |k,v| props[k] = v end
19
+ app.properties.each do |k,v| props[k] = v end
20
+
21
+ # Find the Fabric by going from app-specific to env-specific to default
22
+ fabric = app.fabric
23
+ fabric = @config_store.fabric if fabric.nil?
24
+ fabric = default_fabric if fabric.nil?
25
+
26
+ # Calculate the application executable
27
+ @app_path = app.app
28
+
29
+ # Activate all of the properties
30
+ props.each do |k,v|
31
+ @logger.debug "Applying constant #{k}=#{v}" if @verbose
32
+ Kernel.const_set k, v
33
+ end
34
+
35
+ @@active_runner = self # Record the active instance of this runner
36
+ old_argv = ARGV.clone
37
+ begin
38
+ # Overwrite ARGV
39
+ ARGV.replace(app.start_parameters)
40
+
41
+ # Execute the fabric, which should internally perform a Syrup::Runner.run_application when it has prepared
42
+ # adequately for the application to execute
43
+ load fabric
44
+ ensure
45
+ ARGV.replace(old_argv)
46
+ @@active_runner = nil
47
+ end
48
+ end
49
+
50
+ def run_application
51
+ raise "Cannot execute application outside of Fabric" if @app_path.nil?
52
+
53
+ load @app_path
54
+ end
55
+
56
+ def self.run_application
57
+ raise "Cannot execute application outside of Fabric" if @@active_runner.nil?
58
+
59
+ active.run_application
60
+ end
61
+
62
+ def self.active
63
+ @@active_runner
64
+ end
65
+
66
+ private
67
+ def default_fabric
68
+ File.join(File.dirname(__FILE__), 'fabrics', 'default.rb')
69
+ end
70
+ end
71
+ end
data/lib/syrup.rb ADDED
@@ -0,0 +1,211 @@
1
+ require 'optparse'
2
+ require 'ostruct'
3
+ require 'syrup/daemon'
4
+ require 'syrup/manager'
5
+ require 'syrup/runner'
6
+ require 'syrup/fabric_support'
7
+
8
+ module Syrup
9
+ # The main application class for Syrup. Handles parsing of command line arguments, configuration, and
10
+ # delegation to the appropriate control classes.
11
+ class Application
12
+ def self.version
13
+ # Load the version from the VERSION.yml file
14
+ version = YAML::load_file(File.join(File.dirname(__FILE__), '..', 'VERSION.yml'))
15
+
16
+ "#{version[:major]}.#{version[:minor]}.#{version[:patch]}"
17
+ end
18
+
19
+ def self.logger
20
+ @logger ||= Syrup::Logger.new
21
+ end
22
+
23
+ def run(arguments)
24
+ # Get the logger
25
+ logger = Syrup::Application.logger
26
+
27
+ # Configure defaults
28
+ @options = OpenStruct.new
29
+ @options.directory = File.expand_path('~/.syrup')
30
+ @options.verbose = false
31
+ @options.application = nil
32
+
33
+ # Parse the options
34
+ @opts = build_options
35
+ @opts.parse! arguments
36
+
37
+ # Work out what command was issued
38
+ command, command_args = determine_command(arguments)
39
+ fail "No command given" if command.nil?
40
+
41
+ # Ensure that the configured directory exists
42
+ logger.info "Configured directory as #{@options.directory}" if @options.verbose
43
+ FileUtils.mkdir_p @options.directory if not File.directory? @options.directory
44
+
45
+ # Create a manager instance
46
+ manager = Syrup::Manager.new @options.directory, logger, @options.verbose
47
+
48
+ # Handle the command
49
+ daemon = Syrup::Daemon.new @options.directory, manager
50
+ case command
51
+ when 'start'
52
+ if command_args.length < 1
53
+ manager.start_all
54
+ else
55
+ command_args.each { |arg| manager.start arg }
56
+ end
57
+ when 'stop'
58
+ if command_args.length < 1
59
+ manager.stop_all
60
+ else
61
+ manager.stop(command_args)
62
+ end
63
+ when 'restart'
64
+ if command_args.length < 1
65
+ manager.stop_all
66
+ manager.start_all
67
+ else
68
+ command_args.each { |arg| manager.stop arg }
69
+ command_args.each { |arg| manager.start arg }
70
+ end
71
+ when 'activate'
72
+ fail "No application given to activate a script for!" if command_args.length < 1
73
+ fail "No path given to activate!" if command_args.length < 2
74
+ manager.activate command_args[0], command_args[1], command_args.slice(2, command_args.length - 2)
75
+ when 'run'
76
+ # Provide the command line if provided
77
+ if command_args.length < 1
78
+ manager.run_all
79
+ else
80
+ manager.run command_args
81
+ end
82
+ # when 'run_app'
83
+ # fail('No application name provided') if command_args.length == 0
84
+ # manager.run_app command_args[0] #, command_args.slice(1, command_args.length - 1)
85
+ when 'set'
86
+ fail("No properties provided to set") if command_args.length < 1
87
+ if @options.application
88
+ manager.set_app_properties @options.application, command_args
89
+ else
90
+ manager.set_global_properties command_args
91
+ end
92
+ when 'unset'
93
+ fail("No properties provided to unset") if command_args.length < 1
94
+ if @options.application
95
+ manager.unset_app_properties @options.application, command_args
96
+ else
97
+ manager.unset_global_properties command_args
98
+ end
99
+ when 'clear'
100
+ if @options.application
101
+ manager.clear_app_properties @options.application
102
+ else
103
+ manager.clear_global_properties
104
+ end
105
+ when 'weave'
106
+ fail("No fabric provided to weave") if command_args.length < 1
107
+ if @options.application
108
+ manager.weave_for_application @options.application, command_args[0]
109
+ else
110
+ manager.weave_global command_args[0]
111
+ end
112
+ when 'unweave'
113
+ if @options.application
114
+ manager.unweave_for_application @options.application
115
+ else
116
+ manager.unweave_global
117
+ end
118
+ else
119
+ fail("Unrecognised command \"#{command}\"")
120
+ end
121
+ end
122
+
123
+ def build_options
124
+ opts = OptionParser.new
125
+ opts.banner = "Usage: syrup [options] start [name] | stop [name] | run [<path> | name] | activate [name] <path> |\n" +
126
+ " set <prop>=<value> | unset <prop> | clear | weave <fabric> | unweave"
127
+ opts.separator ""
128
+ opts.separator "Ruby options:"
129
+ opts.on('-d', '--debug', 'set debugging flags (set $DEBUG to true)') { $DEBUG = true }
130
+ opts.on("-I", "--include PATH", "specify $LOAD_PATH (may be used more than once)") { |path|
131
+ $LOAD_PATH.unshift(*path.split(":"))
132
+ }
133
+ opts.on("-r", "--require LIBRARY", "require the library, before executing your script") { |library|
134
+ require library
135
+ }
136
+
137
+ opts.separator ""
138
+ opts.separator "Syrup options:"
139
+ opts.on('-p', '--path PATH', "Sets the base path for this syrup configuration. Defaults to ~/.syrup. See also the --local option to set this") { |value|
140
+ @options.directory = value
141
+ }
142
+ opts.on('--local', "Sets the configuration base path to ./.syrup, allowing for a local configuration") {
143
+ @options.directory = File.expand_path('./.syrup')
144
+ }
145
+ opts.on('--application NAME', "Selects the application that should be updated. Defaults to global settings.") { |value|
146
+ @options.application = value
147
+ }
148
+ opts.on('--verbose', "Places Syrup in verbose mode.") {
149
+ @options.verbose = true
150
+ }
151
+
152
+ opts.separator ""
153
+ opts.separator "Common options:"
154
+ opts.on_tail("-h", "--help", "Show this message") { puts opts; exit 0 }
155
+ opts.on_tail('-v', '--version', "Print the version and exit") { output_version; exit 0 }
156
+
157
+ opts
158
+ end
159
+
160
+ # Determines the command issued in the provided arguments
161
+ def determine_command(arguments)
162
+ command = nil
163
+ command_args = []
164
+
165
+ # Work through each argument until we reach the command. Then, consume everything
166
+ # after it as arguments to the command
167
+ arguments.each do |arg|
168
+ if command.nil?
169
+ command = arg unless arg =~ /^-/
170
+ else
171
+ command_args << arg
172
+ end
173
+ end
174
+
175
+ return command, command_args
176
+ end
177
+
178
+ # Outputs the version
179
+ def output_version
180
+ puts "Syrup version #{Syrup::Application.version}"
181
+ end
182
+
183
+ def fail(msg)
184
+ puts "ERROR: #{msg}"
185
+ puts @opts
186
+ exit
187
+ end
188
+ end
189
+
190
+ class Logger
191
+ def error(msg)
192
+ puts "ERROR: #{msg}"
193
+ end
194
+ def warn(msg)
195
+ puts "WARN: #{msg}"
196
+ end
197
+ def info(msg)
198
+ puts "INFO: #{msg}"
199
+ end
200
+ def debug(msg)
201
+ puts "DEBUG: #{msg}"
202
+ end
203
+ end
204
+
205
+ # Module Singleton methods
206
+ class << self
207
+ def application
208
+ @application ||= Syrup::Application.new
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,190 @@
1
+ require 'syrup/config_store'
2
+ require 'fileutils'
3
+
4
+ describe Syrup::ConfigStore do
5
+ before :each do
6
+ # Ensure that we have a clear test data directory
7
+ @path = File.join(File.dirname(__FILE__), '..', '..', 'test-data')
8
+ FileUtils.rm_r @path if File.directory? @path
9
+ FileUtils.mkdir_p @path
10
+
11
+ # Create the store
12
+ @store = Syrup::ConfigStore.new @path
13
+ end
14
+
15
+ it "should return an empty list of applications when unconfigured" do
16
+ @store.applications.length.should == 0
17
+ end
18
+
19
+ it "should create an application when requested" do
20
+ @store.create_application 'myapp'
21
+ @store.applications['myapp'].should_not be_nil
22
+ end
23
+
24
+ it "should return the created application when one is created" do
25
+ @store.create_application('myapp').name.should == 'myapp'
26
+ end
27
+
28
+ it "should return no properties on a new config" do
29
+ @store.create_application 'myapp'
30
+ @store.applications['myapp'].properties.length.should == 0
31
+ end
32
+
33
+ it "should allow properties to be stored in a global context" do
34
+ @store.create_application 'myconfapp'
35
+ @store.properties['a']= 'b'
36
+ @store.properties['a'].should == 'b'
37
+ end
38
+
39
+ it "should allow properties to be cleared for the global content" do
40
+ @store.create_application 'myconfapp'
41
+ @store.properties['a']= 'b'
42
+ @store.properties.clear
43
+ @store.properties.length.should == 0
44
+ end
45
+
46
+ it "should allow properties to be deleted for the global context" do
47
+ @store.create_application 'myconfapp'
48
+ @store.properties['a']= 'b'
49
+ @store.properties['b']= 'c'
50
+ @store.properties.delete 'b'
51
+ @store.properties.length.should == 1
52
+ @store.properties['a'].should == 'b'
53
+ end
54
+
55
+ it "should allow all properties to be deleted for the global context" do
56
+ @store.create_application 'myconfapp'
57
+ @store.properties['a']= 'b'
58
+ @store.properties['b']= 'c'
59
+ @store.properties.delete 'a'
60
+ @store.properties.delete 'b'
61
+ @store.properties.length.should == 0
62
+ end
63
+
64
+ it "should return no properties on a new application" do
65
+ @store.create_application 'myapp'
66
+ @store.applications['myapp'].properties.length.should == 0
67
+ end
68
+
69
+ it "should allow properties to be stored for an application" do
70
+ @store.create_application 'myconfapp'
71
+ @store.applications['myconfapp'].properties['a']= 'b'
72
+ @store.applications['myconfapp'].properties['a'].should == 'b'
73
+ end
74
+
75
+ it "should allow properties to be cleared for an application" do
76
+ @store.create_application 'myconfapp'
77
+ @store.applications['myconfapp'].properties['a']= 'b'
78
+ @store.applications['myconfapp'].properties.clear
79
+ @store.applications['myconfapp'].properties.length.should == 0
80
+ end
81
+
82
+ it "should allow properties to be deleted for an application" do
83
+ @store.create_application 'myconfapp'
84
+ @store.applications['myconfapp'].properties['a']= 'b'
85
+ @store.applications['myconfapp'].properties['b']= 'c'
86
+ @store.applications['myconfapp'].properties.delete 'b'
87
+ @store.applications['myconfapp'].properties.length.should == 1
88
+ @store.applications['myconfapp'].properties['a'].should == 'b'
89
+ end
90
+
91
+ it "should allow all properties to be deleted for an application" do
92
+ @store.create_application 'myconfapp'
93
+ @store.applications['myconfapp'].properties['a']= 'b'
94
+ @store.applications['myconfapp'].properties['b']= 'c'
95
+ @store.applications['myconfapp'].properties.delete 'a'
96
+ @store.applications['myconfapp'].properties.delete 'b'
97
+ @store.applications['myconfapp'].properties.length.should == 0
98
+ end
99
+
100
+ it "should return a nil fabric by default" do
101
+ @store.fabric.should be_nil
102
+ end
103
+
104
+ it "should store the fabric location when set" do
105
+ @store.fabric = '../myfabric'
106
+ Syrup::ConfigStore.new(@path).fabric.should == File.expand_path('../myfabric')
107
+ end
108
+
109
+ it "should allow the fabric to be cleared" do
110
+ @store.fabric = '../myfabric'
111
+ @store.fabric = nil
112
+ Syrup::ConfigStore.new(@path).fabric.should be_nil
113
+ end
114
+
115
+ it "should return a nil fabric by default for applications" do
116
+ @store.create_application('myfabapp')
117
+ @store.applications['myfabapp'].fabric.should be_nil
118
+ end
119
+
120
+ it "should store the fabric location when set for applications" do
121
+ @store.create_application('myfabapp')
122
+ @store.applications['myfabapp'].fabric = '../myappfabric'
123
+ Syrup::ConfigStore.new(@path).applications['myfabapp'].fabric.should == File.expand_path('../myappfabric')
124
+ end
125
+
126
+ it "should allow the fabric to be cleared for application" do
127
+ @store.create_application('myfabapp')
128
+ @store.applications['myfabapp'].fabric = '../myappfabric'
129
+ @store.applications['myfabapp'].fabric = nil
130
+ Syrup::ConfigStore.new(@path).applications['myfabapp'].fabric.should be_nil
131
+ end
132
+
133
+ it "should return a nil application path by default for applications" do
134
+ @store.create_application('myfabapp')
135
+ @store.applications['myfabapp'].app.should be_nil
136
+ end
137
+
138
+ it "should store the application path when set for applications" do
139
+ @store.create_application('myfabapp')
140
+ @store.applications['myfabapp'].app = '../myapp.rb'
141
+ Syrup::ConfigStore.new(@path).applications['myfabapp'].app.should == File.expand_path('../myapp.rb')
142
+ end
143
+
144
+ it "should allow the application path to be cleared for application" do
145
+ @store.create_application('myfabapp')
146
+ @store.applications['myfabapp'].app = '../myapp.rb'
147
+ @store.applications['myfabapp'].app = nil
148
+ Syrup::ConfigStore.new(@path).applications['myfabapp'].app.should be_nil
149
+ end
150
+
151
+ it "should return an empty list of start parameters for a new application" do
152
+ @store.create_application('mynpapp')
153
+ @store.applications['mynpapp'].start_parameters.should == []
154
+ end
155
+
156
+ it "should allow start parameters to be stored for an application" do
157
+ @store.create_application('mypapp')
158
+ @store.applications['mypapp'].start_parameters = ['param1', 'param 2']
159
+
160
+ Syrup::ConfigStore.new(@path).applications['mypapp'].start_parameters.should == ['param1', 'param 2']
161
+ end
162
+
163
+ it "should allow start parameters to be cleared for an application" do
164
+ @store.create_application('mycapp')
165
+ @store.applications['mycapp'].start_parameters = ['param1', 'param 2']
166
+ @store.applications['mycapp'].start_parameters = []
167
+
168
+ Syrup::ConfigStore.new(@path).applications['mycapp'].start_parameters.should == []
169
+ end
170
+
171
+ it "should return a nil pid for a new application" do
172
+ @store.create_application('mypidapp')
173
+ @store.applications['mypidapp'].pid.should be_nil
174
+ end
175
+
176
+ it "should allow start parameters to be stored for an application" do
177
+ @store.create_application('mypidapp')
178
+ @store.applications['mypidapp'].pid = 1234
179
+
180
+ Syrup::ConfigStore.new(@path).applications['mypidapp'].pid.should == 1234
181
+ end
182
+
183
+ it "should allow the pid to be cleared for an application" do
184
+ @store.create_application('mypidapp')
185
+ @store.applications['mypidapp'].pid = 1234
186
+ @store.applications['mypidapp'].pid = nil
187
+
188
+ Syrup::ConfigStore.new(@path).applications['mypidapp'].pid.should be_nil
189
+ end
190
+ end