strongly_typed 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
data/.yardopts ADDED
@@ -0,0 +1,5 @@
1
+ --readme README.md
2
+ --title 'StronglyTyped API Documentation'
3
+ --charset utf-8
4
+ -
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in strongly_typed.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Leo Gallucci
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,114 @@
1
+ # StronglyTyped
2
+
3
+ [![Build Status](https://travis-ci.org/elgalu/strongly_typed.png)](https://travis-ci.org/elgalu/strongly_typed)
4
+ [![Dependency Status](https://gemnasium.com/elgalu/strongly_typed.png)](https://gemnasium.com/elgalu/strongly_typed)
5
+ [![Code Climate](https://codeclimate.com/github/elgalu/strongly_typed.png)](https://codeclimate.com/github/elgalu/strongly_typed)
6
+
7
+ This gem provides similar functionality as ruby core [Struct][] but instead of [inheritance][] i used [mixins][] and even wrote [something][blog on mixins] about my reasons to do so.
8
+
9
+ Same as [Struct][], it is a convenient way to bundle a number of attributes together using accessor methods with added features like basic type validation and type conversions when [possible][specs on conversions].
10
+
11
+ ## Similar Tools
12
+
13
+ From ruby core you have [Struct][] and from stdlib [OpenStruct][].
14
+
15
+ Check [virtus][] if you are looking for a full featured attributes settings for your Ruby Objects that requires complex automatic coercions among other things.
16
+
17
+ If you are looking for a nestable, coercible, Hash-like data structure take a look at [structure][] gem.
18
+
19
+ ## Reasons
20
+
21
+ I took some ideas from others gems like [virtus][] and [structure][] but since none of them provided exactly what i needed then decided to create this gem for my sample gem project [dolarblue][] and the blog [post][blog on dolarblue] i wrote about it.
22
+
23
+ ## Installation
24
+
25
+ Add this line to your application's Gemfile:
26
+
27
+ gem 'strongly_typed'
28
+
29
+ And then execute:
30
+
31
+ $ bundle
32
+
33
+ Or install it yourself as:
34
+
35
+ $ gem install strongly_typed
36
+
37
+ ## Usage
38
+
39
+ Include `StronglyTyped::Model` on you ruby objects then call `attribute()` to define them.
40
+
41
+ ```ruby
42
+ require 'strongly_typed'
43
+
44
+ class Person
45
+ include StronglyTyped::Model
46
+
47
+ attribute :id, Integer
48
+ attribute :slug, String
49
+ end
50
+ ```
51
+
52
+ Instance initialization with a hash, a.k.a [named parameters][]
53
+
54
+ ```ruby
55
+ leo = Person.new(id: 1, slug: 'elgalu')
56
+ #=> #<Person:0x00c98 @id=1, @slug="elgalu">
57
+ leo.id #=> 1
58
+ leo.slug #=> "elgalu"
59
+ ```
60
+
61
+ Can also trust in the parameters order
62
+
63
+ ```ruby
64
+ leo = Person.new(1, 'elgalu')
65
+ #=> #<Person:0x00c99 @id=1, @slug="elgalu">
66
+ leo.id #=> 1
67
+ leo.slug #=> "elgalu"
68
+ ```
69
+
70
+ Hash Order doesn't matter
71
+
72
+ ```ruby
73
+ ana = Person.new(slug: 'anapau', id: 2)
74
+ #=> #<Person:0x00d33 @id=2, @slug="anapau">
75
+ ana.id #=> 2
76
+ ana.slug #=> "anapau"
77
+ ```
78
+
79
+ TypeError is raised when improper type
80
+
81
+ ```ruby
82
+ ted = Person.new(id: nil, slug: 'teddy')
83
+ #=> TypeError: can't convert nil into Integer
84
+ ```
85
+
86
+ Check the [full API here][]
87
+
88
+ ## Contributing
89
+
90
+ 1. Fork it
91
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
92
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
93
+ 4. Push to the branch (`git push origin my-new-feature`)
94
+ 5. Create new Pull Request
95
+
96
+ ### TODO
97
+ + Improve generic TypeError to also show attribute name, expected type and value received instead
98
+ + On coercible.rb require gem 'double' to avoid requiring 'date' when user doesn't need that
99
+ + Add :default => 'value' to attribute() to support defaults
100
+ + Add :required => true/false to attribute() so an ArgumentError is raised when not provided
101
+
102
+
103
+ [strongly_typed]: https://github.com/elgalu/strongly_typed
104
+ [full API here]: http://rubydoc.info/gems/strongly_typed/frames/file/README.md
105
+ [specs on conversions]: https://github.com/elgalu/strongly_typed/blob/master/spec/coercible_spec.rb
106
+ [blog on dolarblue]: http://elgalu.github.com/2013/tools-for-creating-your-first-ruby-gem-dolarblue/
107
+ [blog on mixins]: http://elgalu.github.com/2013/when-to-use-ruby-inheritance-versus-mixins/
108
+ [named parameters]: http://en.wikipedia.org/wiki/Named_parameter
109
+ [Struct]: http://www.ruby-doc.org/core-1.9.3/Struct.html
110
+ [OpenStruct]: http://ruby-doc.org/stdlib-1.9.3/libdoc/ostruct/rdoc/OpenStruct.html
111
+ [structure]: https://github.com/hakanensari/structure
112
+ [virtus]: https://github.com/solnic/virtus
113
+ [inheritance]: http://en.wikipedia.org/wiki/Inheritance_(computer_science)
114
+ [mixins]: http://en.wikipedia.org/wiki/Mixin
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,94 @@
1
+ module StronglyTyped
2
+ module Attributes
3
+
4
+ # Create attribute accesors for the included class
5
+ # Also validations and coercions for the type specified
6
+ #
7
+ # @param [Symbol] name the accessor name
8
+ # @param [Class] type the class type to use for validations and coercions
9
+ #
10
+ # @example
11
+ # class Person
12
+ # include StronglyTyped::Model
13
+ #
14
+ # attribute :id, Integer
15
+ # attribute :slug, String
16
+ # end
17
+ #
18
+ # Person.new(id: 1, slug: 'elgalu')
19
+ # #=> #<Person:0x00c98 @id=1, @slug="elgalu">
20
+ # leo.id #=> 1
21
+ # leo.slug #=> "elgalu"
22
+ def attribute(name, type=Object)
23
+ name = name.to_sym #normalize
24
+
25
+ raise NameError, "attribute `#{name}` already created" if members.include?(name)
26
+ raise TypeError, "second argument, type, must be a Class but got `#{type.inspect}` insted" unless type.is_a?(Class)
27
+ raise TypeError, "directly converting to Bignum is not supported, use Integer instead" if type == Bignum
28
+
29
+ new_attribute(name, type)
30
+ end
31
+
32
+ # Memoized hash storing keys for names & values for types pairs
33
+ #
34
+ # @return [Hash] attributes
35
+ def attributes
36
+ @attributes ||= {}
37
+ end
38
+
39
+ # Returns the attribute names created with attribute()
40
+ #
41
+ # @return [Array<Symbol>] all the attribute names
42
+ def members
43
+ attributes.keys
44
+ end
45
+
46
+ private
47
+
48
+ # Add new attribute for the tiny object modeled
49
+ #
50
+ # @param [Symbol] name the attribute name
51
+ # @param [Class] type the class type
52
+ #
53
+ # @private
54
+ def new_attribute(name, type)
55
+ attributes[name] = type
56
+ define_attr_reader(name)
57
+ define_attr_writer(name, type)
58
+ name
59
+ end
60
+
61
+ # Define attr_reader method for the new attribute
62
+ #
63
+ # @param [Symbol] name the attribute name
64
+ #
65
+ # @private
66
+ def define_attr_reader(name)
67
+ define_method(name) do
68
+ instance_variable_get("@#{name}")
69
+ end
70
+ end
71
+
72
+ # Define attr_writer method for the new attribute
73
+ # with the added feature of validations and coercions.
74
+ #
75
+ # @param [Symbol] name the attribute name
76
+ # @param [Class] type the class type
77
+ #
78
+ # @raise [TypeError] if unable to coerce the value
79
+ #
80
+ # @private
81
+ def define_attr_writer(name, type)
82
+ define_method("#{name}=") do |value|
83
+ unless value.kind_of?(type)
84
+ value = coerce(value, to: type)
85
+ unless value.kind_of?(type)
86
+ raise TypeError, "Attribute `#{name}` only accepts `#{type}` but got `#{value}`:`#{value.class}` instead"
87
+ end
88
+ end
89
+ instance_variable_set("@#{name}", value)
90
+ end
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,64 @@
1
+ require 'boolean_class'
2
+ require 'date'
3
+
4
+ module StronglyTyped
5
+ module Coercible
6
+ LOCAL_OFFSET = Float(Time.now.gmt_offset) / Float(3600)
7
+
8
+ # Coerce (convert) a value to some specified type
9
+ #
10
+ # @param [Object] value the value to coerce
11
+ # @param [Hash] opts the conversion options
12
+ # @option opts [Class, Module] :to the type to convert to
13
+ #
14
+ # @return [Object] the converted value into the specified type
15
+ #
16
+ # @example
17
+ # include StronglyTyped::Coercible
18
+ #
19
+ # coerce 100, to: Float #=> 100.0
20
+ # coerce 100 #=> ArgumentError: Needs option :to => Class/Module
21
+ # coerce 100, to: String #=> "100"
22
+ # coerce 100, to: Boolean #=> true
23
+ # coerce 100, to: Symbol #=> TypeError: can't convert `100:Fixnum` to `Symbol`
24
+ #
25
+ # @raise [ArgumentError] if :to => Class option was not provided correctly
26
+ # @raise [TypeError] if unable to perform the coersion
27
+ def coerce(value, opts={})
28
+ raise ArgumentError, "Needs option :to => Class/Module" unless opts.has_key?(:to) && ( opts[:to].is_a?(Class) || opts[:to].is_a?(Module) )
29
+ type = opts[:to]
30
+
31
+ case
32
+ # Direct conversions
33
+ when type <= String then String(value)
34
+ when type <= Boolean then Boolean(value)
35
+ when type == Bignum then raise TypeError, "directly converting to Bignum is not supported, use Integer instead"
36
+ when type <= Integer then Integer(value)
37
+ when type <= Float then Float(value)
38
+ when type <= Rational then Rational(value)
39
+ when type <= Complex then Complex(value)
40
+ # Symbol
41
+ when type <= Symbol && value.respond_to?(:to_sym)
42
+ value.to_sym
43
+ # Dates and Times
44
+ when type <= Time && value.is_a?(Numeric)
45
+ Time.at(value)
46
+ when type <= Time && value.is_a?(String)
47
+ DateTime.parse(value).new_offset(LOCAL_OFFSET/24).to_time
48
+ when type <= DateTime && value.respond_to?(:to_datetime)
49
+ value.to_datetime.new_offset(LOCAL_OFFSET/24)
50
+ when type <= DateTime && value.is_a?(String)
51
+ DateTime.parse(value).new_offset(LOCAL_OFFSET/24)
52
+ when type <= DateTime && value.is_a?(Integer)
53
+ DateTime.parse(value.to_s).new_offset(LOCAL_OFFSET/24)
54
+ # Important: DateTime < Date so the order in this case statement matters
55
+ when type <= Date && value.is_a?(String)
56
+ Date.parse(value)
57
+ when type <= Date && value.is_a?(Integer)
58
+ Date.parse(value.to_s)
59
+ else
60
+ raise TypeError, "can't convert `#{value}:#{value.class}` to `#{type}`"
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,99 @@
1
+ require "strongly_typed/attributes"
2
+
3
+ module StronglyTyped
4
+ module Model
5
+ # @private
6
+ def self.included(klass)
7
+ klass.class_eval do
8
+ extend StronglyTyped::Attributes
9
+ include StronglyTyped::Coercible
10
+ end
11
+ end
12
+
13
+ # Entity constructor delegator
14
+ #
15
+ # @overload initialize(hsh)
16
+ # Accepts key values from a hash
17
+ # @param (see #initialize_from_hash)
18
+ # @overload initialize(values)
19
+ # Accepts values from ordered arguments
20
+ # @param (see #initialize_from_ordered_args)
21
+ #
22
+ # @example (see #initialize_from_hash)
23
+ # @example (see #initialize_from_ordered_args)
24
+ #
25
+ # @raise (see #initialize_from_hash)
26
+ # @raise (see #initialize_from_ordered_args)
27
+ def initialize(*args)
28
+ raise ArgumentError, "Need arguments to build your tiny model" if args.empty?
29
+
30
+ if args.size == 1 && args.first.kind_of?(Hash)
31
+ initialize_from_hash(args)
32
+ else
33
+ initialize_from_ordered_args(args)
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ # Entity constructor from a Hash
40
+ #
41
+ # @param [Hash] hsh hash of values
42
+ #
43
+ # @example
44
+ # class Person
45
+ # include StronglyTyped::Model
46
+ #
47
+ # attribute :id, Integer
48
+ # attribute :slug, String
49
+ # end
50
+ #
51
+ # Person.new(id: 1, slug: 'elgalu')
52
+ # #=> #<Person:0x00c98 @id=1, @slug="elgalu">
53
+ # leo.id #=> 1
54
+ # leo.slug #=> "elgalu"
55
+ #
56
+ # @raise [NameError] if tries to assign a non-existing member
57
+ #
58
+ # @private
59
+ def initialize_from_hash(hsh)
60
+ hsh.first.each_pair do |k, v|
61
+ if self.class.members.include?(k.to_sym)
62
+ self.public_send("#{k}=", v)
63
+ else
64
+ raise NameError, "Trying to assign non-existing member #{k}=#{v}"
65
+ end
66
+ end
67
+ end
68
+
69
+ # Entity constructor from ordered params
70
+ #
71
+ # @param [Array] values ordered values
72
+ #
73
+ # @example
74
+ # class Person
75
+ # include StronglyTyped::Model
76
+ #
77
+ # attribute :id, Integer
78
+ # attribute :slug, String
79
+ # end
80
+ #
81
+ # Person.new(1, 'elgalu')
82
+ # #=> #<Person:0x00c99 @id=1, @slug="elgalu">
83
+ # leo.id #=> 1
84
+ # leo.slug #=> "elgalu"
85
+ #
86
+ # @raise [ArgumentError] if the arity doesn't match
87
+ #
88
+ # @private
89
+ def initialize_from_ordered_args(values)
90
+ raise ArgumentError, "wrong number of arguments(#{values.size} for #{self.class.members.size})" if values.size > self.class.members.size
91
+
92
+ values.each_with_index do |v, i|
93
+ # instance_variable_set("@#{self.class.members[i]}", v)
94
+ self.public_send("#{self.class.members[i]}=", v)
95
+ end
96
+ end
97
+
98
+ end
99
+ end
@@ -0,0 +1,3 @@
1
+ module StronglyTyped
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,6 @@
1
+ require 'boolean_class'
2
+
3
+ require "strongly_typed/version"
4
+ require "strongly_typed/coercible"
5
+ require "strongly_typed/attributes"
6
+ require "strongly_typed/model"
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+ require 'date'
3
+
4
+ describe StronglyTyped::Coercible, '#coerce' do
5
+
6
+ it 'converts some Fixnum to anything' do
7
+ coerce(100, to: Float).should eql(100.0)
8
+ coerce(100, to: String).should eql("100")
9
+ coerce(100, to: Boolean).should be(true)
10
+ coerce(100, to: Rational).should eql(Rational(100/1))
11
+ coerce(100, to: Complex).should eql(Complex(100, 0))
12
+ coerce(100, to: Time).should be_kind_of(Time)
13
+ coerce(100, to: Date).should be_kind_of(Date)
14
+ end
15
+
16
+ it 'raises exception when trying to convert some Numeric to Symbol' do
17
+ expect { coerce(100, to: Symbol) }.to raise_error(TypeError)
18
+ expect { coerce(100.0, to: Symbol) }.to raise_error(TypeError)
19
+ expect { coerce(9**20, to: Symbol) }.to raise_error(TypeError)
20
+ expect { coerce(Rational(100/1), to: Symbol) }.to raise_error(TypeError)
21
+ expect { coerce(Complex(100, 0), to: Symbol) }.to raise_error(TypeError)
22
+ end
23
+
24
+ it 'converts some Float to anything' do
25
+ coerce(100.0, to: Integer).should eql(100)
26
+ coerce(100.0, to: String).should eql("100.0")
27
+ coerce(100.0, to: Boolean).should be(true)
28
+ coerce(100.5, to: Rational).should eql(Rational(201.0/2))
29
+ coerce(100.5, to: Complex).should eql(Complex(100.5, 0))
30
+ coerce(100.0, to: Time).should be_kind_of(Time)
31
+ end
32
+
33
+ it 'raises exception when trying to convert some Numeric to Date' do
34
+ expect { coerce(1, to: Date) }.to raise_error(ArgumentError)
35
+ expect { coerce(1.0, to: Date) }.to raise_error(TypeError)
36
+ expect { coerce(9**20, to: Date) }.to raise_error(ArgumentError)
37
+ expect { coerce(Rational(100/1), to: Date) }.to raise_error(TypeError)
38
+ expect { coerce(Complex(100, 0), to: Date) }.to raise_error(TypeError)
39
+ end
40
+
41
+ it 'converts dates' do
42
+ coerce(Date.new(2013, 02, 21), to: DateTime).should == DateTime.new(2013, 02, 21)
43
+ coerce(Time.new(2013,02,20,10,20,30,0), to: DateTime).should == DateTime.new(2013,02,20,10,20,30,0)
44
+ coerce(20130221, to: DateTime).should == DateTime.new(2013, 02, 21)
45
+ end
46
+
47
+ it 'raises exception when trying to convert some Numeric to DateTime' do
48
+ expect { coerce(1, to: DateTime) }.to raise_error(ArgumentError)
49
+ expect { coerce(1.0, to: DateTime) }.to raise_error(TypeError)
50
+ expect { coerce(9**20, to: DateTime) }.to raise_error(ArgumentError)
51
+ expect { coerce(Rational(100/1), to: DateTime) }.to raise_error(TypeError)
52
+ expect { coerce(Complex(100, 0), to: DateTime) }.to raise_error(TypeError)
53
+ end
54
+
55
+ it 'raises exception when trying to convert to non primitive types' do
56
+ expect { coerce(1, to: Object) }.to raise_error(TypeError)
57
+ expect { coerce(1.0, to: Module) }.to raise_error(TypeError)
58
+ expect { coerce("str", to: Class) }.to raise_error(TypeError)
59
+ end
60
+
61
+ it 'raises exception when trying to convert to Bignum' do
62
+ expect { coerce(100, to: Bignum) }.to raise_error(TypeError)
63
+ end
64
+
65
+ end
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+ require 'date'
3
+
4
+ describe StronglyTyped::Model do
5
+
6
+ before(:all) do
7
+ class Person
8
+ include StronglyTyped::Model
9
+
10
+ attribute :id, Integer
11
+ attribute :name, String
12
+ attribute :slug, Symbol
13
+ attribute :dob, Date
14
+ attribute :last_seen, DateTime
15
+ end
16
+ end
17
+
18
+ context 'when a Person is correctly created on initialize' do
19
+ let(:leo) { Person.new id: 1122,
20
+ name: 'Leo Gallucci',
21
+ slug: :elgalu,
22
+ dob: '1981-05-28',
23
+ last_seen: Time.new(2013,02,21,06,37,40,0)
24
+ }
25
+
26
+ it 'should have the correct attribute accessors, values and types' do
27
+ leo.id.should == Integer(1122)
28
+ leo.name.should == "Leo Gallucci"
29
+ leo.slug.should == :elgalu
30
+ leo.dob.should == Date.new(1981, 05, 28)
31
+ leo.last_seen.should == DateTime.new(2013,02,21,06,37,40,0)
32
+ end
33
+ end
34
+
35
+ context 'when a Person is initially created with no initial values' do
36
+ subject { Person.new }
37
+
38
+ it 'should raise ArgumentError' do
39
+ expect { subject }.to raise_error(ArgumentError)
40
+ end
41
+ end
42
+
43
+ context 'when a Person is initially created with almost no values' do
44
+ let(:leo) { Person.new(id: 1) }
45
+
46
+ context 'and their members are initialized later on' do
47
+ it 'is possible to initialize it with correct types' do
48
+ leo.id = 1122
49
+ leo.name = 'Leo Gallucci'
50
+ leo.slug = :elgalu
51
+ leo.dob = Date.new(1981, 05, 28)
52
+ leo.last_seen = DateTime.new(2013,02,21,06,37,40,0)
53
+ # assert:
54
+ leo.id.should == Integer(1122)
55
+ leo.name.should == "Leo Gallucci"
56
+ leo.slug.should == :elgalu
57
+ leo.dob.should == Date.new(1981, 05, 28)
58
+ leo.last_seen.should == DateTime.new(2013,02,21,06,37,40,0)
59
+ end
60
+
61
+ it 'is possible to intialize it with similar types that will be automatically converted' do
62
+ leo.id = "1122"
63
+ leo.name = 'Leo Gallucci'
64
+ leo.slug = "elgalu"
65
+ leo.dob = 19810528
66
+ leo.last_seen = Time.new(2013,02,21,06,37,40,0)
67
+ # assert:
68
+ leo.id.should == 1122
69
+ leo.name.should == "Leo Gallucci"
70
+ leo.slug.should == :elgalu
71
+ leo.dob.should == Date.new(1981, 05, 28)
72
+ leo.last_seen.should == DateTime.new(2013,02,21,06,37,40,0)
73
+ end
74
+ end
75
+ end
76
+
77
+ context 'when a Person is tried to initialized with invalid types' do
78
+ it 'raise TypeError when try to coerce nil to Integer' do
79
+ expect { Person.new id: nil }.to raise_error(TypeError)
80
+ end
81
+
82
+ it 'raise TypeError when try to coerce 100 to Symbol' do
83
+ expect { Person.new slug: 100 }.to raise_error(TypeError)
84
+ end
85
+
86
+ it 'raise TypeError when try to coerce nil to Date' do
87
+ expect { Person.new dob: nil }.to raise_error(TypeError)
88
+ end
89
+
90
+ it 'raise TypeError when try to coerce nil to DateTime' do
91
+ expect { Person.new id: nil }.to raise_error(TypeError)
92
+ end
93
+ end
94
+
95
+ end
@@ -0,0 +1,18 @@
1
+ require 'strongly_typed'
2
+ require 'simplecov'
3
+
4
+ # Require this file using `require "spec_helper"` within each of your specs
5
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
6
+ RSpec.configure do |config|
7
+ config.treat_symbols_as_metadata_keys_with_true_values = true
8
+ config.run_all_when_everything_filtered = true
9
+ config.filter_run :focus
10
+
11
+ # Run specs in random order to surface order dependencies.
12
+ config.order = 'random'
13
+
14
+ # Make coerce() possible at the Example level
15
+ config.include StronglyTyped::Coercible
16
+ end
17
+
18
+ SimpleCov.start
@@ -0,0 +1,41 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'strongly_typed/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+
8
+ gem.platform = Gem::Platform::RUBY
9
+ gem.name = "strongly_typed"
10
+ gem.version = StronglyTyped::VERSION
11
+ gem.summary = %q{Simple type validation for plain ruby object attributes that performs conversions when possible.}
12
+ gem.description = <<-DESC
13
+ A simple type validation tool for plain ruby object attributes that performs conversions when possible.
14
+ Similar to ruby-core Struct but i didn't like using inheritance for something more appropriate for mixins.
15
+ Check virtus gem if you are looking for a full featured attributes settings for your Ruby Objects that requires complex automatic coercions among other things.
16
+ If you are looking for a nestable, coercible, Hash-like data structure take a look at structure gem.
17
+ DESC
18
+
19
+ gem.required_ruby_version = '>= 1.9.3'
20
+ gem.required_rubygems_version = '>= 1.8.11'
21
+
22
+ gem.license = 'MIT'
23
+
24
+ gem.authors = ["Leo Gallucci"]
25
+ gem.email = ["elgalu3@gmail.com"]
26
+ gem.homepage = "https://github.com/elgalu/strongly_typed"
27
+
28
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
29
+ gem.files = `git ls-files`.split($/)
30
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
31
+ gem.require_paths = ["lib"]
32
+
33
+ gem.add_runtime_dependency "boolean_class", "~> 0.0"
34
+
35
+ gem.add_development_dependency "rake", "~> 10.0"
36
+ gem.add_development_dependency "rspec", "~> 2.12"
37
+ gem.add_development_dependency "redcarpet", "~> 2.2"
38
+ gem.add_development_dependency "yard", "~> 0.8"
39
+ gem.add_development_dependency "simplecov", "~> 0.7"
40
+
41
+ end
metadata ADDED
@@ -0,0 +1,176 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: strongly_typed
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Leo Gallucci
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: boolean_class
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '0.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '0.0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '10.0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '10.0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '2.12'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.12'
62
+ - !ruby/object:Gem::Dependency
63
+ name: redcarpet
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '2.2'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '2.2'
78
+ - !ruby/object:Gem::Dependency
79
+ name: yard
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: '0.8'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '0.8'
94
+ - !ruby/object:Gem::Dependency
95
+ name: simplecov
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: '0.7'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: '0.7'
110
+ description: ! 'A simple type validation tool for plain ruby object attributes that
111
+ performs conversions when possible.
112
+
113
+ Similar to ruby-core Struct but i didn''t like using inheritance for something more
114
+ appropriate for mixins.
115
+
116
+ Check virtus gem if you are looking for a full featured attributes settings for
117
+ your Ruby Objects that requires complex automatic coercions among other things.
118
+
119
+ If you are looking for a nestable, coercible, Hash-like data structure take a look
120
+ at structure gem.
121
+
122
+ '
123
+ email:
124
+ - elgalu3@gmail.com
125
+ executables: []
126
+ extensions: []
127
+ extra_rdoc_files: []
128
+ files:
129
+ - .gitignore
130
+ - .rspec
131
+ - .travis.yml
132
+ - .yardopts
133
+ - Gemfile
134
+ - LICENSE.txt
135
+ - README.md
136
+ - Rakefile
137
+ - lib/strongly_typed.rb
138
+ - lib/strongly_typed/attributes.rb
139
+ - lib/strongly_typed/coercible.rb
140
+ - lib/strongly_typed/model.rb
141
+ - lib/strongly_typed/version.rb
142
+ - spec/coercible_spec.rb
143
+ - spec/model_spec.rb
144
+ - spec/spec_helper.rb
145
+ - strongly_typed.gemspec
146
+ homepage: https://github.com/elgalu/strongly_typed
147
+ licenses:
148
+ - MIT
149
+ post_install_message:
150
+ rdoc_options: []
151
+ require_paths:
152
+ - lib
153
+ required_ruby_version: !ruby/object:Gem::Requirement
154
+ none: false
155
+ requirements:
156
+ - - ! '>='
157
+ - !ruby/object:Gem::Version
158
+ version: 1.9.3
159
+ required_rubygems_version: !ruby/object:Gem::Requirement
160
+ none: false
161
+ requirements:
162
+ - - ! '>='
163
+ - !ruby/object:Gem::Version
164
+ version: 1.8.11
165
+ requirements: []
166
+ rubyforge_project:
167
+ rubygems_version: 1.8.25
168
+ signing_key:
169
+ specification_version: 3
170
+ summary: Simple type validation for plain ruby object attributes that performs conversions
171
+ when possible.
172
+ test_files:
173
+ - spec/coercible_spec.rb
174
+ - spec/model_spec.rb
175
+ - spec/spec_helper.rb
176
+ has_rdoc: