strongly_typed 0.0.1

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.
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: