yacl 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/HISTORY.rdoc +5 -0
  2. data/LICENSE +16 -0
  3. data/Manifest.txt +48 -0
  4. data/README.rdoc +55 -0
  5. data/Rakefile +308 -0
  6. data/example/myapp-simple/bin/myapp +16 -0
  7. data/example/myapp-simple/config/database.yml +8 -0
  8. data/example/myapp-simple/config/host.yml +2 -0
  9. data/example/myapp-simple/config/pipeline.yml +1 -0
  10. data/example/myapp-simple/lib/myapp.rb +53 -0
  11. data/example/myapp/bin/myapp +17 -0
  12. data/example/myapp/bin/myapp-job +10 -0
  13. data/example/myapp/config/database.yml +8 -0
  14. data/example/myapp/config/httpserver.yml +3 -0
  15. data/example/myapp/config/pipeline.yml +1 -0
  16. data/example/myapp/lib/myapp.rb +6 -0
  17. data/example/myapp/lib/myapp/cli.rb +92 -0
  18. data/example/myapp/lib/myapp/defaults.rb +28 -0
  19. data/example/myapp/lib/myapp/job.rb +56 -0
  20. data/lib/yacl.rb +12 -0
  21. data/lib/yacl/define.rb +9 -0
  22. data/lib/yacl/define/cli.rb +7 -0
  23. data/lib/yacl/define/cli/options.rb +97 -0
  24. data/lib/yacl/define/cli/parser.rb +112 -0
  25. data/lib/yacl/define/cli/runner.rb +82 -0
  26. data/lib/yacl/define/defaults.rb +58 -0
  27. data/lib/yacl/define/plan.rb +197 -0
  28. data/lib/yacl/loader.rb +80 -0
  29. data/lib/yacl/loader/env.rb +103 -0
  30. data/lib/yacl/loader/yaml_dir.rb +137 -0
  31. data/lib/yacl/loader/yaml_file.rb +102 -0
  32. data/lib/yacl/properties.rb +144 -0
  33. data/lib/yacl/simple.rb +52 -0
  34. data/spec/data/yaml_dir/database.yml +8 -0
  35. data/spec/data/yaml_dir/httpserver.yml +3 -0
  36. data/spec/define/cli/options_spec.rb +47 -0
  37. data/spec/define/cli/parser_spec.rb +64 -0
  38. data/spec/define/cli/runner_spec.rb +57 -0
  39. data/spec/define/defaults_spec.rb +24 -0
  40. data/spec/define/plan_spec.rb +77 -0
  41. data/spec/loader/env_spec.rb +32 -0
  42. data/spec/loader/yaml_dir_spec.rb +43 -0
  43. data/spec/loader/yaml_file_spec.rb +80 -0
  44. data/spec/loader_spec.rb +16 -0
  45. data/spec/properties_spec.rb +60 -0
  46. data/spec/simple_spec.rb +85 -0
  47. data/spec/spec_helper.rb +31 -0
  48. data/spec/version_spec.rb +8 -0
  49. metadata +207 -0
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+ require 'yacl/define/cli/options'
3
+
4
+ module Yacl::Spec::Define
5
+ class OptionsTest < ::Yacl::Define::Cli::Options
6
+ opt 'pipeline.dir', :long => 'pipeline-dir', :short => 'd', :description => "The pipeline directory we are using"
7
+ opt 'timelimit' , :long => 'time-limit', :short => 't', :description => "The amount of time to run for"
8
+ opt 'system' , :long => 'system', :short => 's', :description => "The system setting"
9
+ end
10
+
11
+ #class OptionsWithBannerTest < OptionsTest
12
+ #banner "MyApp version 4.2"
13
+ #end
14
+ end
15
+
16
+
17
+
18
+ describe Yacl::Define::Cli::Options do
19
+
20
+ it "has all the options listed in the class" do
21
+ Yacl::Spec::Define::OptionsTest.opt_list.size.must_equal 3
22
+ end
23
+
24
+ it "keeps the options in listed order" do
25
+ Yacl::Spec::Define::OptionsTest.opt_list.map { |o| o.property_name }.must_equal %w[ pipeline.dir timelimit system ]
26
+ end
27
+
28
+ it "implements each" do
29
+ ot = Yacl::Spec::Define::OptionsTest.new
30
+ gather = []
31
+ ot.each do |op|
32
+ gather << op.property_name
33
+ end
34
+ gather.must_equal %w[ pipeline.dir timelimit system ]
35
+
36
+ end
37
+
38
+ it "returns the properties of the options" do
39
+ ot = Yacl::Spec::Define::OptionsTest.new( 'pipeline-dir' => "/var/log/", "time-limit" => 40, "system" => "foo" )
40
+ props = ot.properties
41
+ props.pipeline.dir.must_equal '/var/log/'
42
+ props.timelimit.must_equal 40
43
+ props.system.must_equal 'foo'
44
+ end
45
+
46
+ end
47
+
@@ -0,0 +1,64 @@
1
+ require 'yacl/define/cli/parser'
2
+
3
+ module Yacl::Spec::Define
4
+ class OptionsForParserTest < ::Yacl::Define::Cli::Options
5
+ opt 'pipeline.dir', :long => 'pipeline-dir', :short => 'd', :description => "The pipeline directory we are using", :cast => :string
6
+ opt 'timelimit' , :long => 'time-limit', :short => 't', :description => "The amount of time to run for", :cast => :int
7
+ opt 'system' , :long => 'system', :short => 's', :description => "The system setting", :cast => :string
8
+ end
9
+
10
+ class ParserTest < ::Yacl::Define::Cli::Parser
11
+ options OptionsForParserTest
12
+ end
13
+
14
+ class ParserWithBannerTest < ::Yacl::Define::Cli::Parser
15
+ banner "MyApp version 4.2"
16
+ options OptionsForParserTest
17
+ end
18
+
19
+ class ParserWithOptions < ::Yacl::Define::Cli::Parser
20
+ banner "MyApp version 4.3"
21
+ opt 'pipeline.dir', :long => 'pipeline-dir', :short => 'd', :description => "The pipeline directory we are using", :cast => :string
22
+ opt 'timelimit' , :long => 'time-limit', :short => 't', :description => "The amount of time to run for", :cast => :int
23
+ opt 'system' , :long => 'system', :short => 's', :description => "The system setting", :cast => :string
24
+ end
25
+
26
+ end
27
+
28
+ describe Yacl::Define::Cli::Parser do
29
+
30
+ it "has a default banner" do
31
+ bt = Yacl::Spec::Define::ParserTest.new
32
+ bt.banner.must_match( /Usage.*Options:/m )
33
+ end
34
+
35
+ it "can set a banner" do
36
+ bt = Yacl::Spec::Define::ParserWithBannerTest.new
37
+ bt.banner.must_equal 'MyApp version 4.2'
38
+ end
39
+
40
+ it "can set the options class" do
41
+ Yacl::Spec::Define::ParserTest.options.must_equal Yacl::Spec::Define::OptionsForParserTest
42
+ end
43
+
44
+ it "creates a Properties instance from parsing the commandline" do
45
+ argv = [ '--pipeline-dir' , Dir.pwd, '--time-limit' , "42", '--system', 'out-of-this-world' ]
46
+ p = Yacl::Spec::Define::ParserTest.new( :argv => argv )
47
+ props = p.properties
48
+ props.pipeline.dir.must_equal Dir.pwd
49
+ props.timelimit.must_equal 42
50
+ props.system.must_equal 'out-of-this-world'
51
+ end
52
+
53
+ it "can define the commanline options internall without a separate Options class" do
54
+ argv = [ '--pipeline-dir' , Dir.pwd, '--time-limit' , "42", '--system', 'out-of-this-world' ]
55
+
56
+ p = Yacl::Spec::Define::ParserWithOptions.new( :argv => argv )
57
+ p.banner.must_equal 'MyApp version 4.3'
58
+ props = p.properties
59
+ props.pipeline.dir.must_equal Dir.pwd
60
+ props.timelimit.must_equal 42
61
+ props.system.must_equal 'out-of-this-world'
62
+ end
63
+
64
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+ require 'yacl/define/cli/runner'
3
+
4
+ module Yacl::Spec::Define::Cli
5
+ class OptionsForRunner < ::Yacl::Define::Cli::Options
6
+ opt 'pipeline.dir', :long => 'pipeline-dir', :short => 'd', :description => "The pipeline directory we are using", :cast => :string
7
+ opt 'timelimit' , :long => 'time-limit', :short => 't', :description => "The amount of time to run for", :cast => :int
8
+ opt 'system' , :long => 'system', :short => 's', :description => "The system setting", :cast => :string
9
+ opt 'config.dir' , :long => 'config-dir' , :short => 'c', :description => "The configuration directory we are using", :cast => :string
10
+ end
11
+
12
+ class ParserForRunner < ::Yacl::Define::Cli::Parser
13
+ options OptionsForRunner
14
+ end
15
+
16
+ class DefaultsForRunner < ::Yacl::Define::Defaults
17
+ default 'host.name', 'localhost'
18
+ default 'host.port', 80
19
+ end
20
+
21
+ class PlanForRunner < ::Yacl::Define::Plan
22
+ try ParserForRunner
23
+ try DefaultsForRunner
24
+ end
25
+
26
+ class Runner < ::Yacl::Define::Cli::Runner
27
+ plan PlanForRunner
28
+
29
+ def run
30
+ 42
31
+ end
32
+
33
+ end
34
+
35
+
36
+ end
37
+
38
+ describe Yacl::Define::Cli::Runner do
39
+
40
+ it "raises an error if no plan is defined" do
41
+ class BadRunner < ::Yacl::Define::Cli::Runner
42
+ plan nil
43
+ end
44
+
45
+ lambda { BadRunner.go }.must_raise ::Yacl::Define::Cli::Runner::Error
46
+ end
47
+
48
+ it "executes the run method defined in the Runner child class" do
49
+ r = Yacl::Spec::Define::Cli::Runner.go
50
+ r.must_equal 42
51
+ end
52
+
53
+ it "loads the propertes in the plan into the runner" do
54
+ r = Yacl::Spec::Define::Cli::Runner.new
55
+ r.properties.host.port.must_equal 80
56
+ end
57
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+ require 'yacl/define/defaults'
3
+
4
+ module Yacl::Spec::Define
5
+ class DefaultsTest < ::Yacl::Define::Defaults
6
+ default 'host.name', 'localhost'
7
+ default 'host.port', 80
8
+ end
9
+ end
10
+
11
+ describe Yacl::Define::Defaults do
12
+ before do
13
+ @dt = Yacl::Spec::Define::DefaultsTest.new
14
+ end
15
+
16
+ it "allows for instance level access to the defaults" do
17
+ @dt.host.name.must_equal 'localhost'
18
+ @dt.host.port.must_equal 80
19
+ end
20
+
21
+ #it "does not allow for the setting of default values" do
22
+ # lambda { @dt.set( 'host.foo', 'bar' ) }.must_raise Yacl::Define::Defaults::Error
23
+ #end
24
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+ require 'yacl/define/plan'
3
+
4
+ module Yacl::Spec::Define
5
+ class OptionsForPlan < ::Yacl::Define::Cli::Options
6
+ opt 'pipeline.dir', :long => 'pipeline-dir', :short => 'd', :description => "The pipeline directory we are using", :cast => :string
7
+ opt 'timelimit' , :long => 'time-limit', :short => 't', :description => "The amount of time to run for", :cast => :int
8
+ opt 'system' , :long => 'system', :short => 's', :description => "The system setting", :cast => :string
9
+ opt 'config.dir' , :long => 'config-dir' , :short => 'c', :description => "The configuration directory we are using", :cast => :string
10
+ end
11
+
12
+ class ParserForPlan < ::Yacl::Define::Cli::Parser
13
+ options OptionsForPlan
14
+ end
15
+
16
+ class DefaultsForPlan < ::Yacl::Define::Defaults
17
+ default 'host.name', 'localhost'
18
+ default 'host.port', 80
19
+ end
20
+
21
+ class Plan < ::Yacl::Define::Plan
22
+ try ParserForPlan
23
+ try Yacl::Loader::YamlDir, :parameter => 'config.dir'
24
+ try DefaultsForPlan
25
+ end
26
+
27
+
28
+ end
29
+
30
+ describe Yacl::Define::Plan do
31
+ before do
32
+ @config_dir = Yacl::Spec::Helpers.spec_dir( 'data/yaml_dir' )
33
+ @argv = [ '--pipeline-dir' , Dir.pwd,
34
+ '--time-limit' , "42",
35
+ '--system', 'out-of-this-world',
36
+ '--config-dir' , @config_dir
37
+ ]
38
+ end
39
+
40
+ it "loads properties from the try locations" do
41
+ p = Yacl::Spec::Define::Plan.new( :argv => @argv )
42
+ props = p.properties
43
+ props.host.name.must_equal 'localhost'
44
+ props.host.port.must_equal 80
45
+ props.system.must_equal 'out-of-this-world'
46
+ end
47
+
48
+ it "raises an error when there is an error in loading properties" do
49
+ lambda { Yacl::Spec::Define::Plan.new }.must_raise Yacl::Loader::YamlDir::Error
50
+ end
51
+
52
+ it "raises an error if you call #on_error and one is not defined" do
53
+ lambda { Yacl::Spec::Define::Plan.on_error }.must_raise ::Yacl::Define::Plan::Error
54
+ end
55
+
56
+ it "can define an on_error block" do
57
+ class PlanWithErrorBlock < Yacl::Define::Plan
58
+ class MyError < ::StandardError; end
59
+ try Yacl::Loader::YamlDir, :parameter => 'config.dir'
60
+ on_error do |exception|
61
+ raise MyError, "KABOOM!"
62
+ end
63
+ end
64
+
65
+ lambda { PlanWithErrorBlock.new }.must_raise PlanWithErrorBlock::MyError
66
+ end
67
+
68
+ it "can define an on_error callable" do
69
+ class PlanWithErrorCallable < Yacl::Define::Plan
70
+ class MyError < ::StandardError; end
71
+ try Yacl::Loader::YamlDir, :parameter => 'config.dir'
72
+ on_error Proc.new{ raise MyError, "from a proc!" }
73
+ end
74
+ lambda { PlanWithErrorCallable.new }.must_raise PlanWithErrorCallable::MyError
75
+ end
76
+
77
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Yacl::Loader::Env do
4
+ it "returns a config containing properties" do
5
+ env = { 'MY_APP_A' => 'foo', 'MY_APP_B' => 'bar' }
6
+ e = Yacl::Loader::Env.new( :env => env, :prefix => "MY_APP" )
7
+ p = e.properties
8
+ p.a.must_equal 'foo'
9
+ p.b.must_equal 'bar'
10
+ end
11
+
12
+ it "raises an error if no prefix is given" do
13
+ env = { 'MY_APP_A' => 'foo', 'MY_APP_B' => 'bar', "SOMETHING_ELSE" => 'baz' }
14
+ lambda { Yacl::Loader::Env.new( :env => env ) }.must_raise Yacl::Loader::Env::Error
15
+ end
16
+
17
+ it "uses only those keys with the given prefix and strips that prefix" do
18
+ env = { 'MY_APP_A' => 'foo', 'MY_APP_B' => 'bar', "SOMETHING_ELSE" => 'baz' }
19
+ e = Yacl::Loader::Env.new( :env => env, :prefix => "my.app" )
20
+ p = e.properties
21
+ p.a.must_equal 'foo'
22
+ p.b.must_equal 'bar'
23
+ p.has_key?('c').must_equal false
24
+ end
25
+
26
+ it "does okay if there are no keys" do
27
+ env = { 'MY_APP_A' => 'foo', 'MY_APP_B' => 'bar', "SOMETHING_ELSE" => 'baz' }
28
+ e = Yacl::Loader::Env.new( :env => env, :prefix => "WIBBLE_FOO_BAR" )
29
+ p = e.properties
30
+ p.has_key?('c').must_equal false
31
+ end
32
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe Yacl::Loader::YamlDir do
4
+ before do
5
+ @yaml_dir = Yacl::Spec::Helpers.spec_dir( 'data', 'yaml_dir' )
6
+ end
7
+
8
+ it "returns a config containing properties" do
9
+ e = Yacl::Loader::YamlDir.new( :path => @yaml_dir )
10
+ c = e.properties
11
+ c.httpserver.port.must_equal 4321
12
+ c.database.username.must_equal "myusername"
13
+ c.database.subpart.baz.must_equal 'wibble'
14
+ end
15
+
16
+ it "raises an error if the directory does not exist" do
17
+ lambda { Yacl::Loader::YamlDir.new( :path => "/does/not/exist" ).properties }.must_raise Yacl::Loader::YamlDir::Error
18
+ end
19
+
20
+ it "can lookup the directory in the passed through configuration if it is not given" do
21
+ cfg = Yacl::Properties.new( 'directory' => @yaml_dir )
22
+ e = Yacl::Loader::YamlDir.new( :properties => cfg, :parameter => 'directory' )
23
+ c = e.properties
24
+ c.httpserver.port.must_equal 4321
25
+ c.database.username.must_equal "myusername"
26
+ c.database.subpart.baz.must_equal 'wibble'
27
+ end
28
+
29
+ it "raises an error if no directory was given and it was unable to look it up in a configuration" do
30
+ lambda { Yacl::Loader::YamlDir.new( :parameter => "directory" ).properties }.must_raise Yacl::Loader::YamlDir::Error
31
+ end
32
+
33
+ it "raises an error if no directory was given and it was unable to look it up in a configuration" do
34
+ cfg = Yacl::Properties.new( 'directory' => @yaml_dir )
35
+ lambda { Yacl::Loader::YamlDir.new( :properties => cfg ).properties }.must_raise Yacl::Loader::YamlDir::Error
36
+ end
37
+
38
+ it "raises an error if no directory was given and the value in the configuration doesn't exist" do
39
+ cfg = Yacl::Properties.new( 'path' => @yaml_dir )
40
+ lambda { Yacl::Loader::YamlDir.new( :properties => cfg, :parameter => 'my.path' ).properties }.must_raise Yacl::Loader::YamlDir::Error
41
+ end
42
+
43
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ describe Yacl::Loader::YamlFile do
4
+ before do
5
+ @yaml_contents = <<_yml_
6
+ a: foo
7
+ b: bar
8
+ _yml_
9
+ @tmpdir = Dir.mktmpdir
10
+ @yaml_file = File.join( @tmpdir, "ytest.yml" )
11
+ Yacl::Spec::Helpers.tmpfile_with_contents( @yaml_file, @yaml_contents )
12
+
13
+ @scoped_file = File.join( @tmpdir, "yscoped.yml" )
14
+ Yacl::Spec::Helpers.tmpfile_with_contents( @scoped_file, <<_eob_ )
15
+ development:
16
+ a: foo
17
+ b: bar
18
+ production:
19
+ a: baz
20
+ b: wibble
21
+ _eob_
22
+
23
+
24
+ end
25
+
26
+ after do
27
+ FileUtils.rm_rf( @tmpdir ) if @tmpdir
28
+ end
29
+
30
+ it "returns a config containing properties" do
31
+ e = Yacl::Loader::YamlFile.new( :path => @yaml_file )
32
+ p = e.properties
33
+ p['a'].must_equal 'foo'
34
+ p.a.must_equal 'foo'
35
+ p['b'].must_equal 'bar'
36
+ p.b.must_equal 'bar'
37
+ end
38
+
39
+ it "can lookup the file in the passed through configuration if it is not given" do
40
+ cfg = Yacl::Properties.new( 'filename' => @yaml_file )
41
+ e = Yacl::Loader::YamlFile.new( :properties => cfg, :parameter => 'filename' )
42
+ p = e.properties
43
+ p.a.must_equal 'foo'
44
+ p.b.must_equal 'bar'
45
+ end
46
+
47
+
48
+ it "raises an error if the file does not exist" do
49
+ lambda { Yacl::Loader::YamlFile.new( :path => "/does/not/exist" ).properties }.must_raise Yacl::Loader::YamlFile::Error
50
+ end
51
+
52
+ it "raises an error if the file is not readable" do
53
+ File.chmod( 0000, @yaml_file )
54
+ lambda { Yacl::Loader::YamlFile.new( :path => @yaml_file ).properties }.must_raise Yacl::Loader::YamlFile::Error
55
+ File.chmod( 0400, @yaml_file )
56
+ end
57
+
58
+ it "raises an error if the file is not a top level hash" do
59
+ bad_file = File.join( @tmpdir, "notahash.yml" )
60
+ Yacl::Spec::Helpers.tmpfile_with_contents( bad_file, <<_eob_ )
61
+ - a: foo
62
+ - b: bar
63
+ _eob_
64
+ lambda { Yacl::Loader::YamlFile.new( :path => bad_file ).properties }.must_raise Yacl::Loader::YamlFile::Error
65
+ end
66
+
67
+ it "returns a scoped config containing properties" do
68
+ e = Yacl::Loader::YamlFile.new( :path => @scoped_file, :scope => "production" )
69
+ p = e.properties
70
+ p['a'].must_equal 'baz'
71
+ p.a.must_equal 'baz'
72
+ p['b'].must_equal 'wibble'
73
+ p.b.must_equal 'wibble'
74
+ end
75
+
76
+ it "raises an error if the scoped config does not contain the scoped key" do
77
+ lambda { Yacl::Loader::YamlFile.new( :path => @scoped_file, :scope => "badscope" ).properties }.must_raise Yacl::Loader::YamlFile::Error
78
+ end
79
+
80
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+ require 'yacl/loader'
3
+
4
+ describe 'Yacl::Loader' do
5
+ before do
6
+ @loader = Yacl::Loader.new( :foo => 'bar', :baz => 'wibble' )
7
+ end
8
+ it "is initialized with options" do
9
+ @loader.options[:foo].must_equal 'bar'
10
+ end
11
+
12
+ it "returns a Properties instance" do
13
+ p = @loader.properties
14
+ p.length.must_equal 0
15
+ end
16
+ end