yacl 1.0.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.
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