rasti-types 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2a37c435c609128695ba34ff046716f3692dce4b5a0d785365f39c44a887921b
4
+ data.tar.gz: a3a9c0d4bfd480922fc412c992c3849afa577a8b53495d4c91e6b71dfd334364
5
+ SHA512:
6
+ metadata.gz: 8bf152f0c673cf017632cb967f9d48ec29a89a689198e3f4df5b87dcb05af6d1aa1ae286658b9420e744b07028e543d6acf74b454cc2c58762f8de9127d40078
7
+ data.tar.gz: a75a4e0c14e8c4a6b42c36f04e3ff63b00d7fdc5952080bbb23ec448fd8639e915e44c7c86cc134ddf75d94bb31bdfc0467c9953561dd1954950086ef525c4a8
data/.coveralls.yml ADDED
@@ -0,0 +1,2 @@
1
+ service_name: travis-ci
2
+ repo_token: 6l6zgQk9lPJXsTdAKOA9HCGwnqRTpqY8L
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ rasti-types
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.5.7
data/.travis.yml ADDED
@@ -0,0 +1,20 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.0
5
+ - 2.1
6
+ - 2.2
7
+ - 2.3
8
+ - 2.4
9
+ - 2.5
10
+ - 2.6
11
+ - 2.7
12
+ - jruby-9.2.9.0
13
+ - ruby-head
14
+ - jruby-head
15
+
16
+ matrix:
17
+ fast_finish: true
18
+ allow_failures:
19
+ - rvm: ruby-head
20
+ - rvm: jruby-head
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rasti-form.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Gabriel Naiman
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # Rasti::Types
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/rasti-types.svg)](https://rubygems.org/gems/rasti-types)
4
+ [![Build Status](https://travis-ci.org/gabynaiman/rasti-types.svg?branch=master)](https://travis-ci.org/gabynaiman/rasti-types)
5
+ [![Coverage Status](https://coveralls.io/repos/github/gabynaiman/rasti-types/badge.svg?branch=master)](https://coveralls.io/github/gabynaiman/rasti-types?branch=master)
6
+ [![Code Climate](https://codeclimate.com/github/gabynaiman/rasti-types.svg)](https://codeclimate.com/github/gabynaiman/rasti-types)
7
+
8
+ Type casting
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem 'rasti-types'
16
+ ```
17
+
18
+ And then execute:
19
+
20
+ $ bundle
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install rasti-types
25
+
26
+ ## Usage
27
+
28
+ ```ruby
29
+ T = Rasti::Types
30
+ ```
31
+
32
+ ### Type casting
33
+
34
+ ```ruby
35
+ T::Integer.cast '10' # => 10
36
+ T::Integer.cast '10.5' # => 10
37
+ T::Integer.cast 'text' # => Rasti::Types::CastError: Invalid cast: 'text' -> Rasti::Types::Integer
38
+
39
+ T::Boolean.cast 'true' # => true
40
+ T::Boolean.cast 'FALSE' # => false
41
+ T::Boolean.cast 'text' # => Rasti::Types::CastError: Invalid cast: 'text' -> Rasti::Types::Boolean
42
+
43
+ T::Time['%Y-%m-%d'].cast '2016-10-22' # => 2016-10-22 00:00:00 -0300
44
+ T::Time['%Y-%m-%d'].cast '2016-10' # => Rasti::Types::CastError: Invalid cast: '2016-10' -> Rasti::Types::Time['%Y-%m-%d']
45
+
46
+ T::Array[T::Symbol].cast [1, 'test', :sym] # => [:"1", :test, :sym]
47
+ ```
48
+
49
+ ### Built-in types
50
+
51
+ - Array
52
+ - Boolean
53
+ - Enum
54
+ - Float
55
+ - Hash
56
+ - Integer
57
+ - IO
58
+ - Model
59
+ - Regexp
60
+ - String
61
+ - Symbol
62
+ - Time
63
+ - UUID
64
+
65
+ ### Plugable types
66
+
67
+ ```ruby
68
+ class UpcaseString
69
+ class << self
70
+
71
+ extend Castable
72
+
73
+ private
74
+
75
+ def valid?(value)
76
+ valid.is_a?(String)
77
+ end
78
+
79
+ def transform(value)
80
+ value.upcase
81
+ end
82
+
83
+ end
84
+ end
85
+ ```
86
+
87
+ ## Contributing
88
+
89
+ Bug reports and pull requests are welcome on GitHub at https://github.com/gabynaiman/rasti-types.
90
+
91
+
92
+ ## License
93
+
94
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
95
+
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:spec) do |t|
5
+ t.libs << 'spec'
6
+ t.libs << 'lib'
7
+ t.pattern = ENV['DIR'] ? File.join(ENV['DIR'], '**', '*_spec.rb') : 'spec/**/*_spec.rb'
8
+ t.verbose = false
9
+ t.warning = false
10
+ t.loader = nil if ENV['TEST']
11
+ ENV['TEST'], ENV['LINE'] = ENV['TEST'].split(':') if ENV['TEST'] && !ENV['LINE']
12
+ t.options = ''
13
+ t.options << "--name=/#{ENV['NAME']}/ " if ENV['NAME']
14
+ t.options << "-l #{ENV['LINE']} " if ENV['LINE'] && ENV['TEST']
15
+ end
16
+
17
+ task default: :spec
18
+
19
+ desc 'Pry console'
20
+ task :console do
21
+ require 'rasti-types'
22
+ require 'pry'
23
+ ARGV.clear
24
+ Pry.start
25
+ end
@@ -0,0 +1 @@
1
+ require_relative 'rasti/types'
@@ -0,0 +1,12 @@
1
+ require 'multi_require'
2
+
3
+ module Rasti
4
+ module Types
5
+
6
+ extend MultiRequire
7
+
8
+ require_relative 'types/castable'
9
+ require_relative_pattern 'types/*'
10
+
11
+ end
12
+ end
@@ -0,0 +1,54 @@
1
+ module Rasti
2
+ module Types
3
+ class Array
4
+
5
+ include Castable
6
+
7
+ attr_reader :type
8
+
9
+ def self.[](type)
10
+ new type
11
+ end
12
+
13
+ def to_s
14
+ "#{self.class}[#{type}]"
15
+ end
16
+ alias_method :inspect, :to_s
17
+
18
+ private
19
+
20
+ def initialize(type)
21
+ @type = type
22
+ end
23
+
24
+ def valid?(value)
25
+ value.is_a? ::Array
26
+ end
27
+
28
+ def transform(value)
29
+ result = []
30
+ errors = {}
31
+
32
+ value.each_with_index do |e,i|
33
+ index = i + 1
34
+ begin
35
+ result << type.cast(e)
36
+
37
+ rescue CompoundError => ex
38
+ ex.errors.each do |key, messages|
39
+ errors["#{index}.#{key}"] = messages
40
+ end
41
+
42
+ rescue => ex
43
+ errors[index] = [ex.message]
44
+ end
45
+ end
46
+
47
+ raise MultiCastError.new(self, value, errors) unless errors.empty?
48
+
49
+ result
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,40 @@
1
+ module Rasti
2
+ module Types
3
+ class Boolean
4
+ class << self
5
+
6
+ include Castable
7
+
8
+ TRUE_FORMAT = /^t(rue)?$/i
9
+ FALSE_FORMAT = /^f(alse)?$/i
10
+
11
+ private
12
+
13
+ def valid?(value)
14
+ boolean?(value) || valid_string?(value)
15
+ end
16
+
17
+ def transform(value)
18
+ boolean?(value) ? value : true_string?(value)
19
+ end
20
+
21
+ def boolean?(value)
22
+ value == true || value == false
23
+ end
24
+
25
+ def valid_string?(value)
26
+ value.is_a?(::String) && (true_string?(value) || false_string?(value))
27
+ end
28
+
29
+ def true_string?(value)
30
+ !!value.match(TRUE_FORMAT)
31
+ end
32
+
33
+ def false_string?(value)
34
+ !!value.match(FALSE_FORMAT)
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,25 @@
1
+ module Rasti
2
+ module Types
3
+ module Castable
4
+
5
+ def cast(value)
6
+ if valid? value
7
+ transform! value
8
+ else
9
+ raise CastError.new self, value
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def transform!(value)
16
+ transform value
17
+ rescue CompoundError => ex
18
+ raise ex
19
+ rescue
20
+ raise CastError.new self, value
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,46 @@
1
+ module Rasti
2
+ module Types
3
+ class Enum
4
+
5
+ include Castable
6
+
7
+ attr_reader :values
8
+
9
+ def self.[](*values)
10
+ new values
11
+ end
12
+
13
+ def to_s
14
+ "#{self.class}[#{values.map(&:inspect).join(', ')}]"
15
+ end
16
+ alias_method :inspect, :to_s
17
+
18
+ private
19
+
20
+ def initialize(values)
21
+ @values = values.map(&:to_s)
22
+ define_getters
23
+ end
24
+
25
+ def valid?(value)
26
+ values.include? String.cast(value)
27
+ rescue
28
+ false
29
+ end
30
+
31
+ def transform(value)
32
+ String.cast value
33
+ end
34
+
35
+ def define_getters
36
+ values.each do |value|
37
+ getter_name = value.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z])([A-Z])/, '\1_\2').downcase
38
+ define_singleton_method getter_name do
39
+ value
40
+ end
41
+ end
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,66 @@
1
+ module Rasti
2
+ module Types
3
+
4
+ class Error < StandardError
5
+ end
6
+
7
+ class CastError < Error
8
+
9
+ attr_reader :type, :value
10
+
11
+ def initialize(type, value)
12
+ @type = type
13
+ @value = value
14
+
15
+ super "Invalid cast: #{display_value} -> #{type}"
16
+ end
17
+
18
+ private
19
+
20
+ def display_value
21
+ value.is_a?(::String) ? "'#{value}'" : value.inspect
22
+ end
23
+
24
+ end
25
+
26
+ class CompoundError < Error
27
+
28
+ attr_reader :errors
29
+
30
+ def initialize(errors)
31
+ @errors = errors
32
+ super "#{message_title}\n#{message_lines}"
33
+ end
34
+
35
+ private
36
+
37
+ def message_title
38
+ 'Errors:'
39
+ end
40
+
41
+ def message_lines
42
+ errors.map { |k,v| "- #{k}: #{v}" }.join("\n")
43
+ end
44
+
45
+ end
46
+
47
+ class MultiCastError < CompoundError
48
+
49
+ attr_reader :type, :value
50
+
51
+ def initialize(type, value, errors)
52
+ @type = type
53
+ @value = value
54
+ super errors
55
+ end
56
+
57
+ private
58
+
59
+ def message_title
60
+ 'Cast errors:'
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+ end