safe_type 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b5ef8f748c40fe69ebe9bde7525c18a26227e922
4
- data.tar.gz: c5dbba13dd9778bc27048e4f78ad80f1a81d8c80
3
+ metadata.gz: 129ba2fcf5b08749ade1b5a843a39939875dc9ee
4
+ data.tar.gz: 356f3af3411031f961ff7c5716dc7a631227db65
5
5
  SHA512:
6
- metadata.gz: 18b50e3f3ffdd15d9fb98242ddd44e32777c9f7add2a9c3539177c5226e2934092a98832f284107fa9168a06ba84fcc899a05dd4c8147beb5850857c140816f3
7
- data.tar.gz: b4a87e49171f26eff48bda4bee778780fd67c5b3e45e199ddf6f4d5fbd1189db5405ae8fccb52a512cf55ec2a7007dbb18a3b0b0bed030462c2ee97e413dbb52
6
+ metadata.gz: 314e4f6f4ca111a75c0fe924b63952e48efdce36d75947ee0811f488b98829734a1c2ab69562bc66bd68f093858ad6f397733001328236e40948e38c56169e26
7
+ data.tar.gz: 77727ad30bb03c10fea5db6cf380176d9eb69bb828dc1855239ada81a6d8c840726b77918a41d5973975c4b93f084c1baa0e96e080f3ddac47d4ee07f16c0c3b
@@ -0,0 +1,11 @@
1
+ .bundle
2
+ Gemfile.lock
3
+ *.gem
4
+ coverage
5
+ tags
6
+
7
+ # Swap
8
+ [._]*.s[a-v][a-z]
9
+ [._]*.sw[a-p]
10
+ [._]s[a-v][a-z]
11
+ [._]sw[a-p]
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require ./spec/spec_helper
@@ -0,0 +1,18 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.2.9
5
+ - 2.3.5
6
+ - 2.4.3
7
+ - 2.5.0
8
+
9
+ bundler_args: --binstubs
10
+
11
+ before_install:
12
+ - gem install bundler --no-doc
13
+
14
+ script:
15
+ - bundle exec rake
16
+
17
+ after_success:
18
+ - bundle exec codeclimate-test-reporter
@@ -0,0 +1,22 @@
1
+ # Contributing
2
+
3
+ ## Issues
4
+ If there are any issues, feel free to create an issue on the GitHub repository issue page.
5
+
6
+ ## Development
7
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
8
+
9
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to rubygems.org.
10
+
11
+ If there are issues with running the `bin` files, try to `chmod +x bin/*` the files first.
12
+
13
+ ## Contribution Guidelines
14
+ 1. Make commits that are logically well isolated and have descriptive commit messages.
15
+
16
+ 2. Make sure that there are tests in the [spec](./spec) directory for the code you wrote.
17
+
18
+ 3. Make sure that changes to public methods or interfaces are documented in this README.
19
+
20
+ 4. Run tests and address any errors.
21
+
22
+ 5. Open a pull request with a descriptive message that describes the change, the need, and verification that the change is tested.
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem "codeclimate-test-reporter", require: false
7
+ gem 'simplecov', require: false
8
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Chan Zuckerberg Initiative
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,18 +1,16 @@
1
1
  ---
2
- SafeType
2
+ safe_type
3
3
  ---
4
4
  [![Gem Version](https://badge.fury.io/rb/safe_type.svg)](https://badge.fury.io/rb/safe_type)
5
5
  [![Build Status](https://travis-ci.org/chanzuckerberg/safe_type.svg?branch=master)](https://travis-ci.org/chanzuckerberg/safe_type)
6
6
  [![Maintainability](https://api.codeclimate.com/v1/badges/7fbc9a4038b86ef639e1/maintainability)](https://codeclimate.com/github/chanzuckerberg/safe_type/maintainability)
7
7
  [![Test Coverage](https://api.codeclimate.com/v1/badges/7fbc9a4038b86ef639e1/test_coverage)](https://codeclimate.com/github/chanzuckerberg/safe_type/test_coverage)
8
8
 
9
- While working with environment variables, routing parameters, API responses,
10
- or other Hash-like objects require parsing,
11
- we often need type coercion to assure expected behaviors.
9
+ While working with environment variables, routing parameters, network responses, or other Hash-like objects that require parsing, we often need type coercion to assure expected behaviors.
12
10
 
13
- ***SafeType*** provides an intuitive type coercion interface and type enhancement.
11
+ ***safe_type*** provides an intuitive type coercion interface and type enhancement.
14
12
 
15
- # Install
13
+ # Installation
16
14
 
17
15
  We can install `safe_type` using `gem install`:
18
16
 
@@ -69,7 +67,7 @@ SafeType::coerce!(params, rules)
69
67
  params["course_id"] # => 101
70
68
  params["start_date"] # => <Date: 2018-10-01 ((2458393j,0s,0n),+0s,2299161j)>
71
69
  ```
72
- ## JSON Response
70
+ ## Ruby Hashes
73
71
  ```ruby
74
72
  json = {
75
73
  "names" => ["Alice", "Bob", "Chris"],
@@ -99,7 +97,7 @@ SafeType::coerce!(json, {
99
97
  ]
100
98
  })
101
99
  ```
102
- ## Http Response
100
+ ## Network Responses
103
101
  ```ruby
104
102
  class ResponseType; end
105
103
 
@@ -129,7 +127,7 @@ The parameters are
129
127
  - `required` indicates whether empty values are allowed
130
128
 
131
129
  ## `strict` vs `default`
132
- The primitive types in *SafeType* provide `default` and `strict` mode, which are
130
+ The primitive types in `SafeType` provide `default` and `strict` mode, which are
133
131
  - `SafeType::Boolean`
134
132
  - `SafeType::Date`
135
133
  - `SafeType::DateTime`
@@ -139,13 +137,13 @@ The primitive types in *SafeType* provide `default` and `strict` mode, which are
139
137
  - `SafeType::Symbol`
140
138
  - `SafeType::Time`
141
139
 
142
- Under the hood, they are all just SafeType rules.
140
+ Under the hood, they are all just `Rule` classes with parameters:
143
141
  - `default`: a rule with default value specified
144
142
  - `strict`: a rule with `required: true`, so no empty values are allowed, or it throws `EmptyValueError`
145
143
 
146
- ## Apply the rules
147
- As we've seen in the use cases, we can call `coerce` to apply a set of `SafeType::Rule`s.
148
- Rules can be bundled together as elements in an array or values in a hash.
144
+ ## Apply the Rules
145
+ As we've seen in the use cases, we can call `coerce` to apply a set of `SafeType::Rule` classes.
146
+ `Rule` classes can be bundled together as elements in an array or values in a hash.
149
147
 
150
148
  ### `coerce` vs `coerce!`
151
149
  - `SafeType::coerce` returns a new object, corresponding to the rules. The unspecified fields will not be included in the new object.
@@ -162,7 +160,7 @@ Note those two examples are equivalent:
162
160
  SafeType::coerce(ENV["PORT"], SafeType::Integer.default(3000))
163
161
  SafeType::Integer.default(3000).coerce(ENV["PORT"])
164
162
  ```
165
- For the *SafeType* primitive types, applying the rule on the class itself will use the default rule.
163
+ For the `SafeType` primitive types, applying the rule on the class itself will use the default rule.
166
164
 
167
165
  ## Customized Types
168
166
  We can inherit from a `SafeType::Rule` to create a customized type.
@@ -174,15 +172,13 @@ We can override following methods if needed:
174
172
  - Override `handle_exceptions` to change the behavior of exceptions handling (e.g: send to the logger, or no exception)
175
173
  - Override `default` or `strict` to modify the default and strict rule.
176
174
 
177
- # Prior Art
178
- This gem was inspired by [rails_param](https://github.com/nicolasblanco/rails_param)
179
- and [dry-types](https://github.com/dry-rb/dry-types). `dry-types` has a complex interface.
180
- Also it does not support in place coercion, and it will be complicated to `ENV` since the design of its
181
- `Hash Schemas`. `rails_param` relies on Rails and it is only for the `params`.
182
- Therefore, `safe_type` was created. It integrated some ideas from both gems,
183
- and it was designed specifically for type checking to provide an clean and easy-to-use interface.
184
- It should be useful when working with any string or hash where the values are coming from an external source,
185
- such as `ENV` variables, rails `params`, or API calls.
186
-
187
- ## License
188
- `safe_type` is released under an MIT license.
175
+ ## Prior Art
176
+ `safe_type` pulls heavy inspiration from [dry-types](https://github.com/dry-rb/dry-types) and [rails_param](https://github.com/nicolasblanco/rails_param).
177
+ The interface in `safe_type` looks similar to the interface in `dry-types`, however, `safe_type` supports some additional features such as in-place coercion
178
+ using the Ruby bang method interface and the ability to define schemas using Ruby hashes and arrays. `safe_type` also borrows some concepts from `rails_param`,
179
+ but with some fundamental differences such as `safe_type` not relying on Rails and `safe_type` not having a focus on only typing Rails controller parameters.
180
+ The goal of `safe_type` is to provide a good tradeoff between complexity and flexibility by enabling type checking through a clean and easy-to-use interface.
181
+ `safe_type` should be useful when working with any string or hash where the values could be ambiguously typed, such as `ENV` variables, rails `params`, or network responses..
182
+
183
+ # License
184
+ The `safe_type` project is licensed and available open source under the terms of the [MIT license](http://opensource.org/licenses/MIT).
@@ -0,0 +1,7 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ RSpec::Core::RakeTask.new(:spec) do |t|
4
+ t.rspec_opts = ['--format progress']
5
+ end
6
+
7
+ task :default => :spec
@@ -12,12 +12,12 @@ require 'safe_type/primitive/time'
12
12
 
13
13
  module SafeType
14
14
  class << self
15
- def coerce(input, rule)
16
- return rule.coerce(input) if rule.is_a?(SafeType::Rule)
15
+ def coerce(input, rule, coerce_key=nil)
16
+ return rule.coerce(input, coerce_key) if rule.is_a?(SafeType::Rule)
17
17
  if rule.class == ::Hash
18
18
  result = {}
19
19
  rule.each do |key, val|
20
- result[key] = coerce(input[key], val)
20
+ result[key] = coerce(input[key], val, key)
21
21
  end
22
22
  return result
23
23
  end
@@ -26,7 +26,7 @@ module SafeType
26
26
  result = ::Array.new(input.length)
27
27
  i = 0
28
28
  while i < input.length
29
- result[i] = coerce(input[i], rule[i % rule.length])
29
+ result[i] = coerce(input[i], rule[i % rule.length], i)
30
30
  i += 1
31
31
  end
32
32
  return result
@@ -40,7 +40,7 @@ module SafeType
40
40
  if val.class == ::Hash
41
41
  coerce!(input[key], val)
42
42
  else
43
- input[key] = coerce(input[key], val)
43
+ input[key] = coerce(input[key], val, key)
44
44
  end
45
45
  end
46
46
  return nil
@@ -48,7 +48,7 @@ module SafeType
48
48
  if rule.class == ::Array
49
49
  i = 0
50
50
  while i < input.length
51
- input[i] = coerce(input[i], rule[i % rule.length])
51
+ input[i] = coerce(input[i], rule[i % rule.length], i)
52
52
  i += 1
53
53
  end
54
54
  return nil
@@ -1,27 +1,52 @@
1
1
  module SafeType
2
- class Error < StandardError; end
3
2
 
4
- class CoercionError < Error
5
- def initialize(message="unable to transform into the requested type")
6
- super
3
+ class CoercionError < StandardError
4
+ attr_reader :key
5
+ attr_reader :value
6
+ attr_reader :desired_type
7
+
8
+ def initialize(value, desired_type, key=nil)
9
+ super("Could not coerce " + (key.nil? ? '' : "key (#{key}) with ") +
10
+ "value (#{value.inspect}) of type (#{value.class}) to desired type (#{desired_type})")
11
+
12
+ @key = key
13
+ @value = value
14
+ @desired_type = desired_type
7
15
  end
8
16
  end
9
17
 
10
- class ValidationError < Error
11
- def initialize(message="failed to validate")
12
- super
18
+ class ValidationError < StandardError
19
+ attr_reader :key
20
+ attr_reader :value
21
+ attr_reader :desired_type
22
+
23
+ def initialize(value, desired_type, key=nil)
24
+ super("Validation for " + (key.nil? ? '' : "key (#{key}) with ") +
25
+ "value (#{value.inspect}) of " +
26
+ "type (#{value.class}) to desired type (#{desired_type}) has failed")
27
+
28
+ @key = key
29
+ @value = value
30
+ @desired_type = desired_type
13
31
  end
14
32
  end
15
33
 
16
- class EmptyValueError < Error
17
- def initialize(message="the value should not be empty")
18
- super
34
+ class EmptyValueError < StandardError
35
+ attr_reader :key
36
+ attr_reader :desired_type
37
+
38
+ def initialize(desired_type, key=nil)
39
+ super("Expected a " + (key.nil? ? '' : "key (#{key}) with ") +
40
+ "value of desired type (#{desired_type}), but received a nil value")
41
+
42
+ @key = key
43
+ @desired_type = desired_type
19
44
  end
20
45
  end
21
46
 
22
47
  class InvalidRuleError < ArgumentError
23
- def initialize(message="invalid coercion rule")
24
- super
48
+ def initialize()
49
+ super("Coercion rule does not exist or is not valid")
25
50
  end
26
51
  end
27
52
  end
@@ -24,10 +24,6 @@ module SafeType
24
24
  input
25
25
  end
26
26
 
27
- def handle_exceptions(e)
28
- raise SafeType::CoercionError
29
- end
30
-
31
27
  def self.coerce(input)
32
28
  default.coerce(input)
33
29
  end
@@ -40,19 +36,19 @@ module SafeType
40
36
  new(required: true)
41
37
  end
42
38
 
43
- def coerce(input)
44
- raise SafeType::EmptyValueError if input.nil? && @required
39
+ def coerce(input, key=nil)
40
+ raise SafeType::EmptyValueError.new(@type, key) if input.nil? && @required
45
41
  input = before(input)
46
42
  input = Converter.to_type(input, @type)
47
- raise SafeType::ValidationError unless is_valid?(input)
43
+ raise SafeType::ValidationError.new(input, @type, key) unless is_valid?(input)
48
44
  result = after(input)
49
- raise SafeType::EmptyValueError if result.nil? && @required
45
+ raise SafeType::EmptyValueError.new(@type, key) if result.nil? && @required
50
46
  return @default if result.nil?
51
- raise SafeType::CoercionError unless result.is_a?(@type)
47
+ raise SafeType::CoercionError.new(result, @type, key) unless result.is_a?(@type)
52
48
  result
53
- rescue TypeError, ArgumentError, NoMethodError => e
49
+ rescue TypeError, ArgumentError, NoMethodError
54
50
  return @default if input.nil? && !@required
55
- handle_exceptions(e)
51
+ raise SafeType::CoercionError.new(input, @type, key)
56
52
  end
57
53
  end
58
54
  end
@@ -0,0 +1,22 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'safe_type'
3
+ s.version = '1.1.0'
4
+ s.date = '2018-07-18'
5
+ s.summary = 'Type Coercion & Type Enhancement'
6
+ s.description = %q{
7
+ Type Coercion & Type Enhancement
8
+ }
9
+ s.authors = ['Donald Dong', 'Edmund Loo', 'Jacob Gable']
10
+ s.email = ['mail@ddong.me', 'edmundloo@outlook.com', 'jgable@chanzuckerberg.com']
11
+ s.homepage = 'https://github.com/chanzuckerberg/safe_type'
12
+ s.license = 'MIT'
13
+ s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } - ['bin/console', 'bin/setup']
14
+
15
+ s.rdoc_options = ['--charset=UTF-8']
16
+ s.require_paths = ['lib']
17
+
18
+ s.add_development_dependency 'bundler', '~> 1.16'
19
+ s.add_development_dependency 'rake', '~> 12.3'
20
+ s.add_development_dependency 'rspec', '~> 3.8'
21
+
22
+ end
metadata CHANGED
@@ -1,50 +1,76 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: safe_type
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Donald Dong
8
+ - Edmund Loo
9
+ - Jacob Gable
8
10
  autorequire:
9
11
  bindir: bin
10
12
  cert_chain: []
11
13
  date: 2018-07-18 00:00:00.000000000 Z
12
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: bundler
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "~>"
20
+ - !ruby/object:Gem::Version
21
+ version: '1.16'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: '1.16'
13
29
  - !ruby/object:Gem::Dependency
14
30
  name: rake
15
31
  requirement: !ruby/object:Gem::Requirement
16
32
  requirements:
17
- - - ">="
33
+ - - "~>"
18
34
  - !ruby/object:Gem::Version
19
- version: '0'
35
+ version: '12.3'
20
36
  type: :development
21
37
  prerelease: false
22
38
  version_requirements: !ruby/object:Gem::Requirement
23
39
  requirements:
24
- - - ">="
40
+ - - "~>"
25
41
  - !ruby/object:Gem::Version
26
- version: '0'
42
+ version: '12.3'
27
43
  - !ruby/object:Gem::Dependency
28
44
  name: rspec
29
45
  requirement: !ruby/object:Gem::Requirement
30
46
  requirements:
31
- - - ">="
47
+ - - "~>"
32
48
  - !ruby/object:Gem::Version
33
- version: '0'
49
+ version: '3.8'
34
50
  type: :development
35
51
  prerelease: false
36
52
  version_requirements: !ruby/object:Gem::Requirement
37
53
  requirements:
38
- - - ">="
54
+ - - "~>"
39
55
  - !ruby/object:Gem::Version
40
- version: '0'
41
- description: " \n Type coercion & Type Enhancement\n "
42
- email: mail@ddong.me
56
+ version: '3.8'
57
+ description: " \n Type Coercion & Type Enhancement\n "
58
+ email:
59
+ - mail@ddong.me
60
+ - edmundloo@outlook.com
61
+ - jgable@chanzuckerberg.com
43
62
  executables: []
44
63
  extensions: []
45
64
  extra_rdoc_files: []
46
65
  files:
66
+ - ".gitignore"
67
+ - ".rspec"
68
+ - ".travis.yml"
69
+ - CONTRIBUTING.md
70
+ - Gemfile
71
+ - LICENSE
47
72
  - README.md
73
+ - Rakefile
48
74
  - lib/safe_type.rb
49
75
  - lib/safe_type/converter.rb
50
76
  - lib/safe_type/errors.rb
@@ -59,6 +85,7 @@ files:
59
85
  - lib/safe_type/primitive/symbol.rb
60
86
  - lib/safe_type/primitive/time.rb
61
87
  - lib/safe_type/rule.rb
88
+ - safe_type.gemspec
62
89
  homepage: https://github.com/chanzuckerberg/safe_type
63
90
  licenses:
64
91
  - MIT
@@ -80,8 +107,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
107
  version: '0'
81
108
  requirements: []
82
109
  rubyforge_project:
83
- rubygems_version: 2.5.2.1
110
+ rubygems_version: 2.6.14
84
111
  signing_key:
85
112
  specification_version: 4
86
- summary: Type coercion & Type Enhancement
113
+ summary: Type Coercion & Type Enhancement
87
114
  test_files: []