opto 1.4.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,57 @@
1
+ require_relative '../type'
2
+ require 'opto/extensions/snake_case'
3
+ require 'opto/extensions/hash_string_or_symbol_key'
4
+
5
+ if RUBY_VERSION < '2.1'
6
+ using Opto::Extension::SnakeCase
7
+ using Opto::Extension::HashStringOrSymbolKey
8
+ end
9
+
10
+ module Opto
11
+ module Types
12
+ # A boolean value.
13
+ #
14
+ # Options:
15
+ # :truthy an array of strings / values that are converted to True.
16
+ # :nil_is by default false
17
+ # :blank_is by default false too
18
+ # :as by default outputs a string. integer outputs a number, true_or_nil outputs true or nil. set to nil to just output whatever is in :true and :false
19
+ # :true says "true" by default when outputting a string
20
+ # :false says "false" by default when outputting a string
21
+ class Boolean < Opto::Type
22
+ using Opto::Extension::HashStringOrSymbolKey unless RUBY_VERSION < '2.1'
23
+
24
+ OPTIONS = {
25
+ truthy: ['true', 'yes', '1', 'on', 'enabled', 'enable'],
26
+ nil_is: false,
27
+ blank_is: false,
28
+ false: 'false',
29
+ true: 'true',
30
+ as: 'string'
31
+ }
32
+
33
+ sanitizer :to_bool do |value|
34
+ if value.nil?
35
+ options[:nil_is]
36
+ elsif value.kind_of?(TrueClass) || value.kind_of?(FalseClass)
37
+ value
38
+ elsif value.to_s.strip == ''
39
+ options[:blank_is]
40
+ else
41
+ options[:truthy].include?(value.to_s.strip.downcase)
42
+ end
43
+ end
44
+
45
+ sanitizer :output do |value|
46
+ case options[:as].to_s.strip.downcase
47
+ when 'integer'
48
+ value ? (options[:true].kind_of?(Fixnum) ? options[:true] : 1) : (options[:false].kind_of?(Fixnum) ? options[:false] : 0)
49
+ when 'boolean'
50
+ value
51
+ else
52
+ value ? options[:true] : options[:false]
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,107 @@
1
+ require_relative '../type'
2
+ require 'opto/extensions/snake_case'
3
+ require 'opto/extensions/hash_string_or_symbol_key'
4
+
5
+ if RUBY_VERSION < '2.1'
6
+ using Opto::Extension::SnakeCase
7
+ using Opto::Extension::HashStringOrSymbolKey
8
+ end
9
+
10
+ module Opto
11
+ module Types
12
+ # A list of possible values
13
+ #
14
+ # :options - a list of possible values for this enum
15
+ # :can_be_other - set to true if the value can be outside of the value list in options
16
+ # :in - when "can be other" is defined, this can be used to define an extra set of possible values
17
+ #
18
+ # @example Shorthand option list
19
+ # Opto::Option.new(
20
+ # name: 'foo',
21
+ # type: 'enum',
22
+ # options:
23
+ # - foo
24
+ # - bar
25
+ # - cat
26
+ # can_be_other: true
27
+ # )
28
+ #
29
+ # @example Detailed option list
30
+ # Opto::Option.new(
31
+ # name: 'foo',
32
+ # type: 'enum',
33
+ # options:
34
+ # - value: cat
35
+ # label: Cat
36
+ # description: A four legged ball of fur
37
+ # - value: dog
38
+ # label: Dog
39
+ # description: A friendly furry creature with a tail, says 'woof'
40
+ # )
41
+ class Enum < Opto::Type
42
+ using Opto::Extension::HashStringOrSymbolKey unless RUBY_VERSION < '2.1'
43
+
44
+ OPTIONS = {
45
+ options: [],
46
+ can_be_other: false,
47
+ in: []
48
+ }
49
+
50
+ def initialize(options={})
51
+ opts = normalize_opts(options.delete(:options))
52
+ super(options)
53
+ @options[:options] = opts
54
+ end
55
+
56
+ validator :options do |value|
57
+ if options[:options].nil? || options[:options].empty?
58
+ raise RuntimeError, "No options defined for enum"
59
+ elsif options[:options].map {|o| o[:value]}.uniq.size != options[:options].size
60
+ raise RuntimeError, "Duplicate values in enum option list"
61
+ end
62
+ end
63
+
64
+ validator :in do |value|
65
+ return nil if options[:can_be_other]
66
+ if options[:in] && !options[:in].empty?
67
+ "Value is not one of #{options[:in].join(', ')}" unless options[:in].include?(value)
68
+ else
69
+ "Value is not one of the options" unless options[:options].map { |o| o[:value] }.include?(value)
70
+ end
71
+ end
72
+
73
+ def normalize_opts(options)
74
+ case options
75
+ when Hash
76
+ options.each_with_object([]) do |(key, value), array|
77
+ array << { value: key, label: key, description: value }
78
+ end
79
+ when Array
80
+ case options.first
81
+ when Hash
82
+ options.each do |opt|
83
+ if opt[:value].nil? || opt[:description].nil?
84
+ raise TypeError, "Option definition requires value and description and can have label when using hash syntax"
85
+ end
86
+ end
87
+ options
88
+ when ::String, Fixnum
89
+ options.map do |opt|
90
+ { value: opt, description: opt, label: opt }
91
+ end
92
+ when NilClass
93
+ []
94
+ else
95
+ raise TypeError, "Invalid format for enum option list definition"
96
+ end
97
+ when NilClass
98
+ []
99
+ else
100
+ raise TypeError, "Invalid format for enum option list definition"
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+
@@ -0,0 +1,47 @@
1
+ require_relative '../type'
2
+ require 'opto/extensions/snake_case'
3
+ require 'opto/extensions/hash_string_or_symbol_key'
4
+
5
+ if RUBY_VERSION < '2.1'
6
+ using Opto::Extension::SnakeCase
7
+ using Opto::Extension::HashStringOrSymbolKey
8
+ end
9
+
10
+ module Opto
11
+ module Types
12
+ # A number.
13
+ #
14
+ # Options
15
+ # :min - minimum allowed value (default 0, can be negative)
16
+ # :max - maximum allowed value
17
+ # :nil_is_zero : set to true if you want to turn a null value into 0
18
+ class Integer < Opto::Type
19
+ using Opto::Extension::HashStringOrSymbolKey unless RUBY_VERSION < '2.1'
20
+
21
+ OPTIONS = {
22
+ min: 0,
23
+ max: nil,
24
+ nil_is_zero: false
25
+ }
26
+
27
+ sanitizer :to_i do |value|
28
+ value.nil? ? (options[:nil_is_zero] ? 0 : nil) : value.to_i
29
+ end
30
+
31
+ validator :min do |value|
32
+ return nil if value.nil?
33
+ if options[:min] && value < options[:min]
34
+ "Too small. Minimum value is #{options[:min_length]}, Value is #{value}."
35
+ end
36
+ end
37
+
38
+ validator :max do |value|
39
+ return nil if value.nil?
40
+ if options[:max] && value > options[:max]
41
+ "Too large. Maximum value is #{options[:max]}, Value is #{value}."
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+
@@ -0,0 +1,71 @@
1
+ require_relative '../type'
2
+ require 'base64'
3
+ require 'opto/extensions/snake_case'
4
+ require 'opto/extensions/hash_string_or_symbol_key'
5
+
6
+ if RUBY_VERSION < '2.1'
7
+ using Opto::Extension::SnakeCase
8
+ using Opto::Extension::HashStringOrSymbolKey
9
+ end
10
+
11
+ module Opto
12
+ module Types
13
+ # A string
14
+ #
15
+ # Options:
16
+ # - min_length: minimum lenght
17
+ # - max_length: maximum length
18
+ # - empty_is_nil: an empty string will be replaced with nil
19
+ # - encode_64: set to true if you want the final value to be base64 encoded representation
20
+ # - decode_64: set to true if your string is in base64 and you want to convert to plain text
21
+ # - upcase: set to true to upcase the string
22
+ # - downcase: set to true to downcase the string
23
+ # - strip: set to true to remove leading and trailing whitespace
24
+ # - chomp: set to true to remove trailing linefeed
25
+ # - capitalize: set to true to upcase the first letter
26
+ class String < Opto::Type
27
+ using Opto::Extension::HashStringOrSymbolKey unless RUBY_VERSION < '2.1'
28
+
29
+ TRANSFORMATIONS = [ :upcase, :downcase, :strip, :chomp, :capitalize ]
30
+
31
+ OPTIONS = {
32
+ min_length: nil,
33
+ max_length: nil,
34
+ empty_is_nil: true,
35
+ encode_64: false,
36
+ decode_64: false
37
+ }.merge(Hash[*TRANSFORMATIONS.flat_map {|tr| [tr, false]}])
38
+
39
+
40
+ sanitizer :encode_64 do |value|
41
+ (options[:encode_64] && value) ? Base64.encode64(value) : value
42
+ end
43
+
44
+ sanitizer :decode_64 do |value|
45
+ (options[:decode_64] && value) ? Base64.decode64(value) : value
46
+ end
47
+
48
+ TRANSFORMATIONS.each do |transform|
49
+ sanitizer transform do |value|
50
+ (options[transform] && value.respond_to?(transform)) ? value.send(transform) : value
51
+ end
52
+ end
53
+
54
+ sanitizer :empty_is_nil do |value|
55
+ (options[:empty_is_nil] && value.to_s.strip.empty?) ? nil : value.to_s
56
+ end
57
+
58
+ validator :min_length do |value|
59
+ if options[:min_length] && value.length < options[:min_length]
60
+ "Too short. Minimum length is #{options[:min_length]}, length is #{value.length}."
61
+ end
62
+ end
63
+
64
+ validator :max_length do |value|
65
+ if options[:max_length] && value.length > options[:max_length]
66
+ "Too long. Maximum length is #{options[:max_length]}, length is #{value.length}."
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,36 @@
1
+ require_relative '../type'
2
+ require 'uri'
3
+ require 'opto/extensions/snake_case'
4
+ require 'opto/extensions/hash_string_or_symbol_key'
5
+
6
+ if RUBY_VERSION < '2.1'
7
+ using Opto::Extension::SnakeCase
8
+ using Opto::Extension::HashStringOrSymbolKey
9
+ end
10
+
11
+ module Opto
12
+ module Types
13
+ # An uri/url.
14
+ #
15
+ # Options:
16
+ # schemes: an array of allowed schemes, such as ['http', 'https', 'file']
17
+ class Uri < Opto::Type
18
+ using Opto::Extension::HashStringOrSymbolKey unless RUBY_VERSION < '2.1'
19
+
20
+ OPTIONS = {
21
+ schemes: [ 'http', 'https' ]
22
+ }
23
+
24
+ validator :scheme do |value|
25
+ return nil if options[:schemes].nil? || options[:schemes].empty?
26
+ scheme = uri(value).scheme
27
+ return nil if options[:schemes].include?(scheme)
28
+ "Uri scheme '#{scheme}' not allowed, allowed schemes: #{options[:schemes].join(', ')}"
29
+ end
30
+
31
+ def uri(value)
32
+ URI.parse(value)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ module Opto
2
+ VERSION = "1.4.0"
3
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'opto/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "opto"
8
+ spec.version = Opto::VERSION
9
+ spec.authors = ["Kimmo Lehto"]
10
+ spec.email = ["info@kontena.io"]
11
+
12
+ spec.summary = "Create validatable and resolvable options from hashes"
13
+ spec.description = "Example: Opto.new(type: :string, name: 'foo, min_length: 20, from: { env: 'FOO' }).value"
14
+ spec.homepage = "https://github.com/kontena/opto"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "bin"
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.12"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec", "~> 3.0"
24
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: opto
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.4.0
5
+ platform: ruby
6
+ authors:
7
+ - Kimmo Lehto
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-11-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: 'Example: Opto.new(type: :string, name: ''foo, min_length: 20, from:
56
+ { env: ''FOO'' }).value'
57
+ email:
58
+ - info@kontena.io
59
+ executables:
60
+ - console
61
+ - setup
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - ".gitignore"
66
+ - ".rspec"
67
+ - ".travis.yml"
68
+ - Gemfile
69
+ - LICENSE.txt
70
+ - README.md
71
+ - Rakefile
72
+ - bin/console
73
+ - bin/setup
74
+ - lib/opto.rb
75
+ - lib/opto/extensions/hash_string_or_symbol_key.rb
76
+ - lib/opto/extensions/snake_case.rb
77
+ - lib/opto/group.rb
78
+ - lib/opto/option.rb
79
+ - lib/opto/resolver.rb
80
+ - lib/opto/resolvers/default.rb
81
+ - lib/opto/resolvers/environment_variable.rb
82
+ - lib/opto/resolvers/file_content.rb
83
+ - lib/opto/resolvers/random_number.rb
84
+ - lib/opto/resolvers/random_string.rb
85
+ - lib/opto/resolvers/random_uuid.rb
86
+ - lib/opto/setter.rb
87
+ - lib/opto/setters/environment_variable.rb
88
+ - lib/opto/type.rb
89
+ - lib/opto/types/boolean.rb
90
+ - lib/opto/types/enum.rb
91
+ - lib/opto/types/integer.rb
92
+ - lib/opto/types/string.rb
93
+ - lib/opto/types/uri.rb
94
+ - lib/opto/version.rb
95
+ - opto.gemspec
96
+ homepage: https://github.com/kontena/opto
97
+ licenses: []
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 2.4.5
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: Create validatable and resolvable options from hashes
119
+ test_files: []