greenpeace 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +111 -0
  3. data/lib/greenpeace/configuration/config.rb +102 -39
  4. data/lib/greenpeace/configuration/default.rb +48 -12
  5. data/lib/greenpeace/configuration/doc.rb +16 -13
  6. data/lib/greenpeace/configuration/key.rb +32 -27
  7. data/lib/greenpeace/configuration/option.rb +31 -21
  8. data/lib/greenpeace/configuration/requirement.rb +42 -18
  9. data/lib/greenpeace/configuration/type.rb +45 -40
  10. data/lib/greenpeace/environment.rb +4 -7
  11. data/lib/greenpeace/railtie.rb +9 -6
  12. data/lib/greenpeace/version.rb +2 -1
  13. data/lib/greenpeace.rb +67 -11
  14. data/spec/integration/simple_requirement_spec.rb +61 -0
  15. data/spec/spec_helper.rb +9 -0
  16. data/spec/unit/configuration/config_spec.rb +45 -0
  17. data/spec/unit/configuration/default_spec.rb +83 -0
  18. data/spec/unit/configuration/doc_spec.rb +40 -0
  19. data/spec/unit/configuration/key_spec.rb +54 -0
  20. data/spec/unit/configuration/option_spec.rb +45 -0
  21. data/spec/unit/configuration/requirement_spec.rb +85 -0
  22. data/spec/unit/configuration/type_spec.rb +96 -0
  23. data/spec/unit/environment_spec.rb +49 -0
  24. metadata +88 -76
  25. data/lib/greenpeace/configuration/environment.rb +0 -0
  26. data/test/dummy/Rakefile +0 -6
  27. data/test/dummy/app/assets/javascripts/application.js +0 -13
  28. data/test/dummy/app/assets/stylesheets/application.css +0 -15
  29. data/test/dummy/app/controllers/application_controller.rb +0 -5
  30. data/test/dummy/app/helpers/application_helper.rb +0 -2
  31. data/test/dummy/app/views/layouts/application.html.erb +0 -13
  32. data/test/dummy/bin/bundle +0 -3
  33. data/test/dummy/bin/rails +0 -4
  34. data/test/dummy/bin/rake +0 -4
  35. data/test/dummy/config/application.rb +0 -29
  36. data/test/dummy/config/boot.rb +0 -5
  37. data/test/dummy/config/environment.rb +0 -5
  38. data/test/dummy/config/environments/development.rb +0 -34
  39. data/test/dummy/config/environments/production.rb +0 -79
  40. data/test/dummy/config/environments/test.rb +0 -39
  41. data/test/dummy/config/greenpeace.rb +0 -6
  42. data/test/dummy/config/initializers/assets.rb +0 -8
  43. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  44. data/test/dummy/config/initializers/cookies_serializer.rb +0 -3
  45. data/test/dummy/config/initializers/filter_parameter_logging.rb +0 -4
  46. data/test/dummy/config/initializers/inflections.rb +0 -16
  47. data/test/dummy/config/initializers/mime_types.rb +0 -4
  48. data/test/dummy/config/initializers/session_store.rb +0 -3
  49. data/test/dummy/config/initializers/wrap_parameters.rb +0 -9
  50. data/test/dummy/config/locales/en.yml +0 -23
  51. data/test/dummy/config/routes.rb +0 -56
  52. data/test/dummy/config/secrets.yml +0 -22
  53. data/test/dummy/config.ru +0 -4
  54. data/test/dummy/public/404.html +0 -67
  55. data/test/dummy/public/422.html +0 -67
  56. data/test/dummy/public/500.html +0 -66
  57. data/test/dummy/public/favicon.ico +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4f258d970be062302715c418752c671084b05da0
4
- data.tar.gz: 2439df02a266bbcaed7523c3c054a62f9dd47ad5
3
+ metadata.gz: 1de449972c8785c764cf0f29f1d3776c3ccb4200
4
+ data.tar.gz: cbbe27c9e0ee871c4b40139b4f96de246d5cd12a
5
5
  SHA512:
6
- metadata.gz: b318de0b7cf80a8e49b8841cf96e97ad0e542f72e5967c233bfedb9f9d53ada29f675f669a2a861191a13a7669d6407bd2041d619bc05a42ba3c7549e2d915a6
7
- data.tar.gz: c593c461ef671e37075b7b53396c4d9afff7498abb5eb5bc442cd3982171816ed7d76f80cbd0dcddc61ccae6173268c0f976770f4e4c51d088c4f2e199d3534d
6
+ metadata.gz: 7e84ee0f0c2bb15dee1b8f585e0edb74621e2c5679e8f50beff7140d1a6d6f904718a0a826a98a1b97e141695c342f9189f9163fad1c0dda45b823c40a6d2905
7
+ data.tar.gz: a7a4dab7dc2a825c1063c7a29db8f43799dadfd158cb186da939afd0a9c10844e93f9d1e8e5f63f459357e8e7f88322c86745b99b743f538915cdd6b32550b33
data/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # Greenpeace
2
+
3
+ **Greenpeace** is a small environment checker for [12 factor
4
+ apps](http://12factor.net/config).
5
+
6
+ ## Why?
7
+
8
+ One of the factors in a [12 factor app](http://12factor.net) is configuration
9
+ management. The recommended approach is using environment variables to provide
10
+ configuration values to the application.
11
+
12
+ While the approach works great, we found some limitations and itches with it:
13
+
14
+ * There is no way to check if the environment has been correctly configured on
15
+ application startup. This delays deployment and configuration errors until
16
+ the moment the configuration key is required.
17
+
18
+ * Accessing configuration values through the ruby `ENV` hash is a bit
19
+ cumbersome, and does not provide typecasting.
20
+
21
+ There are some gems already out there that solve these, but none of them fixed
22
+ them how we wanted them to. See the similarities section bellow.
23
+
24
+ **Greenpeace** solves this in a simple, straightforward way. It is an
25
+ environment checker that checks that the environment contains all the required
26
+ values. It also exposes ENV values through a simple configuration API so you
27
+ can do `Greenpeace.env.port` instead of `ENV['PORT'].to_i`, applying
28
+ typecasting as required.
29
+
30
+ ## Usage
31
+
32
+ ### Installation
33
+
34
+ Add the gem to your Gemfile:
35
+
36
+ ~~~ruby
37
+ gem "greenpeace"
38
+ ~~~
39
+
40
+ Run bundle to install the engine:
41
+
42
+ ~~~
43
+ > bundle install
44
+ ~~~
45
+
46
+ ### Usage
47
+
48
+ You need to setup your environment in a ruby file of your liking. If you are
49
+ using rails, the gem automatically loads a file at `config/greenpeace.rb` where
50
+ you should configure your requirements using the provided API. Otherwise, put
51
+ those requirements somewhere and ensure they are run before starting your
52
+ application.
53
+
54
+ The API is declarative and quite simple to read and write:
55
+
56
+ ~~~ruby
57
+ Greenpeace.configure do |env|
58
+ # You can mark a key to be required for boot.
59
+ env.requires :database_url
60
+
61
+ # You can mark a key as an optional value, with a default if it is not
62
+ # defined.
63
+ env.may_have :google_analytics_account, default: "UA-xxxxx"
64
+
65
+ # You can mark required or optional keys to be converted to a type when
66
+ # reading the values. Valid types are :string and :int
67
+ env.requires :port, type: :int
68
+ env.may_have :api_timeout, type: :int, default: 30
69
+
70
+ # You can add an optional message which describes what the key is about, for
71
+ # documenting configuration options. This documentation string is used when
72
+ # raising an initialization exception if the key is not found, or if the
73
+ # type is not correct.
74
+ env.requires 'API_KEY', doc: "API key for the Frumboloizer service"
75
+ end
76
+ ~~~
77
+
78
+ Whenever the `configure` method is called (which is done automatically on
79
+ rails), Greenpeace will check the environment and raise exceptions if something
80
+ is not correctly configured. In addition to this, you can now access the
81
+ configured keys through a simple API:
82
+
83
+ ~~~ruby
84
+ if Greenpeace.env.use_google_analytics
85
+ # ...
86
+ end
87
+ ~~~
88
+
89
+ ## License
90
+
91
+ Copyright (C) 2014 Recompensa.mobi
92
+
93
+
94
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
95
+ this software and associated documentation files (the "Software"), to deal in
96
+ the Software without restriction, including without limitation the rights to
97
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
98
+ of the Software, and to permit persons to whom the Software is furnished to do
99
+ so, subject to the following conditions:
100
+
101
+ The above copyright notice and this permission notice shall be included in all
102
+ copies or substantial portions of the Software.
103
+
104
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
105
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
106
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
107
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
108
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
109
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
110
+ SOFTWARE.
111
+
@@ -1,46 +1,109 @@
1
- require "greenpeace/configuration/key"
2
- require "greenpeace/configuration/type"
3
- require "greenpeace/configuration/doc"
4
- require "greenpeace/configuration/default"
5
- require "greenpeace/configuration/requirement"
6
- require "greenpeace/configuration/option"
7
-
8
- class Greenpeace::Configuration::Config
9
- attr_reader :requirements
10
- attr_reader :options
11
-
12
- def initialize
13
- @requirements = []
14
- @options = []
15
- end
1
+ require 'greenpeace/configuration/key'
2
+ require 'greenpeace/configuration/type'
3
+ require 'greenpeace/configuration/doc'
4
+ require 'greenpeace/configuration/default'
5
+ require 'greenpeace/configuration/requirement'
6
+ require 'greenpeace/configuration/option'
16
7
 
17
- def requires(key, options={})
18
- requirement = Greenpeace::Configuration::Requirement.new(key, options)
19
- add_requirement(requirement)
20
- end
8
+ module Greenpeace
9
+ module Configuration
10
+ #
11
+ # = Config
12
+ #
13
+ # This class exposes the API methods that the end user can call on a
14
+ # `Greenpeace.configure` block.
15
+ #
16
+ # == Requirements
17
+ #
18
+ # Requirements are definition of environment keys which MUST be present on
19
+ # application startup. When a requirement is defined, if the environment
20
+ # key is not present and the current runtime mode does not have a specific
21
+ # default for the key an error is raised.
22
+ #
23
+ # The API is like the following:
24
+ #
25
+ # env.requires :key,
26
+ # type: TYPE,
27
+ # doc: DOC,
28
+ # defaults: DEFAULTS
29
+ #
30
+ # This defines a required value on ENV['KEY']. Everything after the key is
31
+ # absolutely optional:
32
+ #
33
+ # * TYPE: Type to convert the env value to. May be either :string or :int.
34
+ # :string by default.
35
+ #
36
+ # * DOC: Doc string which will be used when raising the exception if the
37
+ # environment value is not present. Extremely useful to make your
38
+ # application self-describing.
39
+ #
40
+ # * DEFAULTS: Hash which defines special defaults to use when the
41
+ # environment value is not present on a specific runtime mode. For example,
42
+ # if you want an entry to raise an error on production, but default to a
43
+ # given value on development, you declare a requirement like this:
44
+ #
45
+ # env.requires :port, type: :int, defaults: { development: 3000 }
46
+ #
47
+ # == Options
48
+ #
49
+ # Options are definition of environment keys which may or may not be
50
+ # present on application startup. Think of them as configurable optional
51
+ # values. When an option is defined, the environment value is used first,
52
+ # if any. If not, a default value is provided instead.
53
+ #
54
+ # The API is like this:
55
+ #
56
+ # env.may_have :key,
57
+ # type: TYPE,
58
+ # doc: DOC,
59
+ # default: DEFAULT,
60
+ # defaults: DEFAULTS
61
+ #
62
+ # The TYPE, DOC and DEFAULTS options work just like in the case of
63
+ # requirements. The main difference is in the DEFAULT value, which is used
64
+ # if no value is present in the environment, and no default is defined in
65
+ # the DEFAULTS entry for the current runtime mode. By default, if no value
66
+ # is present in the environment hash, no value is provided in the DEFAULT
67
+ # option and no value is provided in the DEFAULTS option for the current
68
+ # runtime environment, nil is returned.
69
+ class Config
70
+ attr_reader :settings
21
71
 
22
- def may_have(key, options={})
23
- option = Greenpeace::Configuration::Option.new(key, options)
24
- add_option(option)
25
- end
72
+ def initialize(environment = '')
73
+ @settings = []
74
+ @environment = environment
75
+ end
26
76
 
27
- private
28
- def add_requirement(requirement)
29
- if key_already_defined?(requirement.key)
30
- raise "Requirement key #{requirement.key} must be uniquely defined"
31
- end
32
- @requirements << requirement
33
- end
77
+ def requires(key, options = {})
78
+ requirement = Greenpeace::Configuration::Requirement.new(
79
+ key,
80
+ options,
81
+ @environment)
34
82
 
35
- def add_option(option)
36
- if key_already_defined?(option.key)
37
- raise "Option key #{option.key} must be uniquely defined"
38
- end
39
- @options << option
40
- end
83
+ add_setting_uniquely(requirement)
84
+ end
85
+
86
+ def may_have(key, options = {})
87
+ option = Greenpeace::Configuration::Option.new(
88
+ key,
89
+ options,
90
+ @environment)
91
+
92
+ add_setting_uniquely(option)
93
+ end
41
94
 
42
- def key_already_defined?(key)
43
- @requirements.any? {|item| item.key == key} ||
44
- @options.any? {|item| item.key == key}
95
+ private
96
+
97
+ def add_setting_uniquely(setting)
98
+ if key_already_defined?(setting.key)
99
+ fail "Duplicated configuration key #{setting.key}"
100
+ end
101
+ @settings << setting
102
+ end
103
+
104
+ def key_already_defined?(key)
105
+ @settings.any? { |setting| setting.key == key }
106
+ end
107
+ end
45
108
  end
46
109
  end
@@ -1,18 +1,54 @@
1
- module Greenpeace::Configuration
2
- class Default
3
- attr_reader :value
1
+ module Greenpeace
2
+ module Configuration
3
+ # Defines a default setting in a requirement or option
4
+ class Default
5
+ def initialize(type, options, key, plural_key)
6
+ @type = type
4
7
 
5
- def initialize(type, options, key)
6
- @type = type
7
- @value = options.has_key?(key) ? validate_default(options[key]) : nil
8
- end
8
+ @has_direct_value = options.key?(key)
9
+ @direct_value = extract_direct_value(options, key)
10
+
11
+ @has_environment_value = options.key?(plural_key)
12
+ @environment_values = extract_environment_value(options, plural_key)
13
+ end
14
+
15
+ def value(environment)
16
+ if @has_direct_value
17
+ @direct_value
18
+ elsif @has_environment_value
19
+ @environment_values[environment.to_s]
20
+ else
21
+ nil
22
+ end
23
+ end
24
+
25
+ def environment_value?(environment)
26
+ @has_environment_value && @environment_values[environment.to_s]
27
+ end
28
+
29
+ private
30
+
31
+ def validate_environment_hash(hash)
32
+ hash.keys.each_with_object({}) do |key, result|
33
+ result[key.to_s] = validate_default(hash[key])
34
+ end
35
+ end
36
+
37
+ def validate_default(value)
38
+ unless @type.valid_value?(value)
39
+ fail "Default value #{value}" \
40
+ " is not of the corresponding type #{@type}"
41
+ end
42
+ value
43
+ end
44
+
45
+ def extract_direct_value(options, key)
46
+ validate_default(options[key]) if @has_direct_value
47
+ end
9
48
 
10
- private
11
- def validate_default(value)
12
- unless @type.valid_value?(value)
13
- raise "Default value #{value} is not of the corresponding type #{@type}"
49
+ def extract_environment_value(options, plural_key)
50
+ validate_environment_hash(options[plural_key]) if @has_environment_value
14
51
  end
15
- value
16
52
  end
17
53
  end
18
54
  end
@@ -1,19 +1,22 @@
1
- module Greenpeace::Configuration
2
- class Doc
1
+ module Greenpeace
2
+ module Configuration
3
+ # Represents a doc configuration in a requirement
4
+ class Doc
5
+ def initialize(options, key)
6
+ @doc = options.key?(key) ? validate_doc(options[key]) : 'Undefined'
7
+ end
3
8
 
4
- def initialize(options, key)
5
- @doc = options.has_key?(key) ? validate_doc(options[key]) : "Undefined"
6
- end
9
+ def to_s
10
+ @doc
11
+ end
7
12
 
8
- def to_s
9
- @doc
10
- end
13
+ private
11
14
 
12
- private
13
- def validate_doc(doc)
14
- raise "Doc cannot be nil" if doc.nil?
15
- raise "Doc must be a string" unless doc.is_a?(String)
16
- doc
15
+ def validate_doc(doc)
16
+ fail 'Doc cannot be nil' if doc.nil?
17
+ fail 'Doc must be a string' unless doc.is_a?(String)
18
+ doc
19
+ end
17
20
  end
18
21
  end
19
22
  end
@@ -1,36 +1,41 @@
1
- module Greenpeace::Configuration
2
- class Key
3
- def initialize(key)
4
- @key = validate_key(key)
5
- end
1
+ module Greenpeace
2
+ module Configuration
3
+ # Represents an environment key in a configuration entry
4
+ class Key
5
+ def initialize(key)
6
+ @key = validate_key(key)
7
+ end
6
8
 
7
- def ==(other)
8
- other.raw_key == raw_key
9
- end
9
+ def ==(other)
10
+ other.key == @key
11
+ end
10
12
 
11
- def to_s
12
- @key
13
- end
13
+ def to_s
14
+ @key.to_s
15
+ end
14
16
 
15
- def identifier
16
- @key.downcase
17
- end
17
+ def identifier
18
+ @key.to_s
19
+ end
18
20
 
19
- def env_identifier
20
- @key
21
- end
21
+ def env_identifier
22
+ @key.to_s.upcase
23
+ end
22
24
 
23
- protected
24
- def raw_key
25
- @key
26
- end
25
+ protected
26
+
27
+ attr_reader :key
28
+
29
+ private
27
30
 
28
- private
29
- def validate_key(key)
30
- raise "Key cannot be nil" if key.nil?
31
- raise "Key #{key} must be a string, but was a #{key.class}" unless key.is_a? String
32
- raise "Key cannot be blank" if key.empty?
33
- key
31
+ def validate_key(key)
32
+ fail 'Key cannot be nil' if key.nil?
33
+ unless key.is_a? Symbol
34
+ fail "Key #{key} must be a symbol, but was a #{key.class}"
35
+ end
36
+ fail 'Key cannot be blank' if key.empty?
37
+ key
38
+ end
34
39
  end
35
40
  end
36
41
  end
@@ -1,27 +1,37 @@
1
- module Greenpeace::Configuration
2
- class Option
3
- attr_reader :key
4
- attr_reader :type
5
- attr_reader :doc
6
- attr_reader :default
1
+ require 'greenpeace/configuration/key'
2
+ require 'greenpeace/configuration/type'
3
+ require 'greenpeace/configuration/doc'
4
+ require 'greenpeace/configuration/default'
7
5
 
8
- def initialize(key, options)
9
- @key = Greenpeace::Configuration::Key.new(key)
10
- @type = Greenpeace::Configuration::Type.new(options, :type)
11
- @doc = Greenpeace::Configuration::Doc.new(options, :doc)
12
- @default = Greenpeace::Configuration::Default.new(@type, options, :default)
13
- end
6
+ module Greenpeace
7
+ module Configuration
8
+ # Represents an environment option
9
+ class Option
10
+ attr_reader :key
14
11
 
15
- def identifier
16
- @key.identifier
17
- end
12
+ def initialize(key, options, environment)
13
+ @environment = environment
14
+ @key = Greenpeace::Configuration::Key.new(key)
15
+ @type = Greenpeace::Configuration::Type.new(options, :type)
16
+ @doc = Greenpeace::Configuration::Doc.new(options, :doc)
17
+ @default = Greenpeace::Configuration::Default.new(
18
+ @type,
19
+ options,
20
+ :default,
21
+ :defaults)
22
+ end
23
+
24
+ def identifier
25
+ @key.identifier
26
+ end
18
27
 
19
- def value
20
- value = ENV[@key.env_identifier]
21
- if value.nil? or value.empty?
22
- @default.value
23
- else
24
- @type.convert(value)
28
+ def value
29
+ value = ENV[@key.env_identifier]
30
+ if value.nil? || value.empty?
31
+ @default.value(@environment)
32
+ else
33
+ @type.convert(value)
34
+ end
25
35
  end
26
36
  end
27
37
  end
@@ -1,26 +1,50 @@
1
- module Greenpeace::Configuration
2
- class Requirement
3
- attr_reader :key
4
- attr_reader :type
5
- attr_reader :doc
1
+ require 'greenpeace/configuration/key'
2
+ require 'greenpeace/configuration/type'
3
+ require 'greenpeace/configuration/doc'
4
+ require 'greenpeace/configuration/default'
6
5
 
7
- def initialize(key, options)
8
- @key = Greenpeace::Configuration::Key.new(key)
9
- @type = Greenpeace::Configuration::Type.new(options, :type)
10
- @doc = Greenpeace::Configuration::Doc.new(options, :doc)
11
- end
6
+ module Greenpeace
7
+ module Configuration
8
+ # Represents an environment requirement
9
+ class Requirement
10
+ attr_reader :key
12
11
 
13
- def identifier
14
- @key.identifier
15
- end
12
+ def initialize(key, options, environment)
13
+ @environment = environment
14
+ @key = Greenpeace::Configuration::Key.new(key)
15
+ @type = Greenpeace::Configuration::Type.new(options, :type)
16
+ @doc = Greenpeace::Configuration::Doc.new(options, :doc)
17
+ @default = Greenpeace::Configuration::Default.new(
18
+ @type,
19
+ options,
20
+ nil,
21
+ :defaults)
22
+ end
23
+
24
+ def identifier
25
+ @key.identifier
26
+ end
27
+
28
+ def value
29
+ value = ENV[@key.env_identifier]
16
30
 
17
- def value
18
- value = ENV[@key.env_identifier]
19
- if value.nil? or value.empty?
20
- raise "Environment key [#{@key}] is missing. The key documentation: #{@doc}"
31
+ if value.nil? || value.empty?
32
+ try_with_environment_values
33
+ else
34
+ @type.convert(value)
35
+ end
21
36
  end
22
37
 
23
- @type.convert(value)
38
+ private
39
+
40
+ def try_with_environment_values
41
+ if @default.environment_value?(@environment)
42
+ @default.value(@environment)
43
+ else
44
+ fail "Environment key [#{@key}] is missing." \
45
+ "The key documentation: #{@doc}"
46
+ end
47
+ end
24
48
  end
25
49
  end
26
50
  end
@@ -1,51 +1,56 @@
1
- module Greenpeace::Configuration
2
- class Type
3
- TYPES = {
4
- string: {
5
- converter: :to_s,
6
- typeclass: String,
7
- regexp: /.*/
8
- },
9
-
10
- int: {
11
- converter: :to_i,
12
- typeclass: Integer,
13
- regexp: /\d+/
14
- },
15
- }
16
-
17
- def initialize(options, key)
18
- @type = options.has_key?(key) ? validate_type(options[key]) : :string
19
- end
1
+ module Greenpeace
2
+ module Configuration
3
+ # Represents a type entry in a requirement or option. In charge of
4
+ # validating values and converting them to the appropriate type.
5
+ class Type
6
+ TYPES = {
7
+ string: {
8
+ converter: :to_s,
9
+ typeclass: String,
10
+ regexp: /.*/
11
+ },
12
+
13
+ int: {
14
+ converter: :to_i,
15
+ typeclass: Integer,
16
+ regexp: /^\d+$/
17
+ }
18
+ }
19
+
20
+ def initialize(options, key)
21
+ @type = options.key?(key) ? validate_type(options[key]) : :string
22
+ end
20
23
 
21
- def valid_value?(value)
22
- value.nil? or value.is_a?(type_descriptor[:typeclass])
23
- end
24
+ def valid_value?(value)
25
+ value.nil? || value.is_a?(type_descriptor[:typeclass])
26
+ end
27
+
28
+ def convert(value)
29
+ unless value.nil? || value.match(type_descriptor[:regexp])
30
+ fail "Value #{value} is supposed to be a #{@type}"
31
+ end
24
32
 
25
- def convert(value)
26
- unless value.match(type_descriptor[:regexp])
27
- raise "Value #{value} is supposed to be a #{@type}"
33
+ value.send(type_descriptor[:converter])
28
34
  end
29
35
 
30
- value.send(type_descriptor[:converter])
31
- end
36
+ def to_s
37
+ @type.to_s
38
+ end
32
39
 
33
- def to_s
34
- @type.to_s
35
- end
40
+ private
36
41
 
37
- private
38
- def type_descriptor
39
- TYPES[@type]
40
- end
42
+ def type_descriptor
43
+ TYPES[@type]
44
+ end
41
45
 
42
- def validate_type(type)
43
- raise "Type cannot be nil" if type.nil?
44
- raise "Type must be a symbol" unless type.is_a?(Symbol)
45
- unless TYPES.has_key?(type)
46
- raise "Type #{type} must be one of #{TYPES.keys.join(", ")}"
46
+ def validate_type(type)
47
+ fail 'Type cannot be nil' if type.nil?
48
+ fail 'Type must be a symbol' unless type.is_a?(Symbol)
49
+ unless TYPES.key?(type)
50
+ fail "Type #{type} must be one of #{TYPES.keys.join(', ')}"
51
+ end
52
+ type
47
53
  end
48
- type
49
54
  end
50
55
  end
51
56
  end