attribute_extensions 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.
@@ -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 progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in attribute_extensions.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Delwyn de Villiers
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.
@@ -0,0 +1,29 @@
1
+ # AttributeExtensions
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'attribute_extensions'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install attribute_extensions
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'attribute_extensions/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "attribute_extensions"
8
+ gem.version = AttributeExtensions::VERSION
9
+ gem.authors = ["Delwyn de Villiers"]
10
+ gem.email = ["delwyn.d@gmail.com"]
11
+ gem.description = %q[Attribute Extenstions]
12
+ gem.summary = %q[Define attributes with default values and typecasting]
13
+ gem.homepage = "https://github.com/delwyn/attribute_extensions"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency 'activesupport', '~> 3.2.0'
21
+
22
+ gem.add_development_dependency 'rspec', '~> 2.12.0'
23
+ end
@@ -0,0 +1,33 @@
1
+ require 'active_support/core_ext'
2
+
3
+ require 'attribute_extensions/typecasting'
4
+ require 'attribute_extensions/version'
5
+
6
+ module AttributeExtensions
7
+
8
+ def self.included(base)
9
+ base.send(:extend, ClassMethods)
10
+ end
11
+
12
+ module ClassMethods
13
+ include Typecasting
14
+
15
+ def attribute_reader(name, options={})
16
+ define_method(name.to_sym) do
17
+ instance_variable_set("@#{name}", options[:default]) unless instance_variable_defined?("@#{name}")
18
+ instance_variable_get("@#{name}")
19
+ end
20
+ end
21
+
22
+ def attribute_writer(name, options={})
23
+ define_method("#{name}=") do |value|
24
+ instance_variable_set("@#{name}", self.class.typecast(value, options[:type]))
25
+ end
26
+ end
27
+
28
+ def attribute(name, options={})
29
+ attribute_reader(name, options)
30
+ attribute_writer(name, options)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,22 @@
1
+ require 'attribute_extensions/typecasting/boolean_typecaster'
2
+ require 'attribute_extensions/typecasting/default_typecaster'
3
+ require 'attribute_extensions/typecasting/integer_typecaster'
4
+ require 'attribute_extensions/typecasting/symbol_typecaster'
5
+
6
+ module AttributeExtensions
7
+ module Typecasting
8
+
9
+ def typecast(value, type)
10
+ if type.respond_to?(:call)
11
+ type.call(value)
12
+ else
13
+ typecaster_for(type).call(value)
14
+ end
15
+ end
16
+
17
+ def typecaster_for(type)
18
+ type = :default if type.nil?
19
+ "AttributeExtensions::Typecasting::#{type.to_s.camelize}Typecaster".constantize.new
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ module AttributeExtensions
2
+ module Typecasting
3
+ class BooleanTypecaster
4
+ TRUE_VALUES = [true, 1, "1", "t", "T", "true", "TRUE", "on", "ON"]
5
+
6
+ def call(value)
7
+ if value.is_a?(String) && value !~ /[^[:space:]]/
8
+ nil
9
+ else
10
+ TRUE_VALUES.include?(value)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ module AttributeExtensions
2
+ module Typecasting
3
+ class DefaultTypecaster
4
+ def call(value)
5
+ value
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module AttributeExtensions
2
+ module Typecasting
3
+ class IntegerTypecaster
4
+ def call(value)
5
+ value.to_i if value.respond_to?(:to_i)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module AttributeExtensions
2
+ module Typecasting
3
+ class SymbolTypecaster
4
+ def call(value)
5
+ value.to_sym if value.respond_to?(:to_sym)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module AttributeExtensions
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+
3
+ describe AttributeExtensions do
4
+ subject { model.new }
5
+
6
+ describe 'attribute_reader' do
7
+ let(:model) {
8
+ Class.new do
9
+ include AttributeExtensions
10
+
11
+ attribute_reader :color
12
+ attribute_reader :make, default: nil
13
+ attribute_reader :gears, default: 5
14
+ attribute_reader :diesel, default: false
15
+ attribute_reader :petrol, default: true
16
+ end
17
+ }
18
+
19
+ its(:color) { should eq nil}
20
+ its(:make) { should eq nil}
21
+ its(:gears) { should eq 5 }
22
+ its(:diesel) { should be_false }
23
+ its(:petrol) { should be_true }
24
+ end
25
+
26
+ describe 'attribute_writer' do
27
+ let(:model) {
28
+ Class.new do
29
+ include AttributeExtensions
30
+
31
+ attribute_writer :color
32
+ attribute_writer :make, type: :symbol
33
+ attribute_writer :doors, type: :integer
34
+ attribute_writer :diesel, type: :boolean
35
+ attribute_writer :tires, type: Proc.new { |value| "proc: #{value}" }
36
+
37
+ attr_reader :color, :make, :doors, :tires, :diesel
38
+ end
39
+ }
40
+
41
+ it 'calls typecast with the value and type' do
42
+ subject.class.should_receive(:typecast).with(1, :integer)
43
+ subject.doors = 1
44
+ end
45
+
46
+ context 'integer' do
47
+ before { subject.doors = '4' }
48
+
49
+ its(:doors) { should eq 4 }
50
+ end
51
+
52
+ context 'symbol' do
53
+ before { subject.make = 'audi' }
54
+
55
+ its(:make) { should eq :audi }
56
+ end
57
+
58
+ context 'proc' do
59
+ before { subject.tires = 'tires' }
60
+
61
+ its(:tires) { should eq "proc: tires"}
62
+ end
63
+
64
+ context 'without type' do
65
+ before { subject.color = 'blue' }
66
+
67
+ its(:color) { should eq 'blue' }
68
+ end
69
+
70
+ describe 'boolean' do
71
+ context 'when truthy' do
72
+ before { subject.diesel = 't' }
73
+ its(:diesel) { should be_true }
74
+ end
75
+
76
+ context 'when falsy' do
77
+ before { subject.diesel = 'f' }
78
+ its(:diesel) { should be_false }
79
+ end
80
+ end
81
+ end
82
+
83
+ describe 'attribute' do
84
+ let(:model) {
85
+ Class.new do
86
+ include AttributeExtensions
87
+ end
88
+ }
89
+
90
+ it 'creates a reader and a writer' do
91
+ options = { type: :integer, default: 1 }
92
+ model.should_receive(:attribute_reader).with(:name, options)
93
+ model.should_receive(:attribute_writer).with(:name, options)
94
+ model.send(:attribute, :name, options)
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,12 @@
1
+ require 'attribute_extensions'
2
+
3
+ root = File.expand_path('../../', __FILE__)
4
+
5
+ Dir["#{root}/spec/support/**/*.rb"].each { |file| require file }
6
+
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.run_all_when_everything_filtered = true
10
+ config.filter_run :focus
11
+ config.order = 'random'
12
+ end
@@ -0,0 +1,19 @@
1
+ class Car
2
+ include AttributeExtensions
3
+
4
+ # Attributes
5
+ attribute :color
6
+ attribute :automatic, type: :boolean
7
+ attribute :tires, type: Proc.new { |value| "proc: #{value}" }
8
+
9
+ # Attribute Readers
10
+ attribute_reader :gears, default: 5
11
+ attribute_reader :diesel, default: false
12
+ attribute_reader :petrol, default: true
13
+
14
+ # Attribute Writers
15
+ attribute_writer :doors, type: :integer
16
+ attribute_writer :make, type: :symbol
17
+
18
+ attr_reader :doors, :make
19
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe AttributeExtensions::Typecasting::BooleanTypecaster do
4
+ it 'returns nil when blank string' do
5
+ subject.call('').should be_nil
6
+ end
7
+
8
+ it 'returns false when value is not in TRUE_VALUES' do
9
+ subject.call('falsy').should be_false
10
+ end
11
+
12
+ it 'returns true when value is in TRUE_VALUES' do
13
+ expect{
14
+ stub_const("AttributeExtensions::Typecasting::BooleanTypecaster::TRUE_VALUES", ['truthy'])
15
+ }.to change{ subject.call('truthy') }.from(false).to(true)
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe AttributeExtensions::Typecasting::DefaultTypecaster do
4
+ it 'returns the value' do
5
+ subject.call('value').should eq 'value'
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe AttributeExtensions::Typecasting::IntegerTypecaster do
4
+ it 'returns the value.to_i if value responds to :to_i' do
5
+ subject.call('1').should eq 1
6
+ end
7
+
8
+ it 'returns nil if value does not respond to :to_i' do
9
+ subject.call(true).should be_nil
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe AttributeExtensions::Typecasting::SymbolTypecaster do
4
+ it 'returns the value.to_sym if value responds to :to_sym' do
5
+ subject.call('sym').should eq :sym
6
+ end
7
+
8
+ it 'returns nil if value does not respond to :to_sym' do
9
+ subject.call(true).should be_nil
10
+ end
11
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe AttributeExtensions::Typecasting do
4
+ let(:model) {
5
+ Class.new do
6
+ include AttributeExtensions
7
+ end
8
+ }
9
+
10
+ describe 'typecast' do
11
+ context 'when type responds_to :call' do
12
+ it 'sends :call with the value to the type' do
13
+ proc = double
14
+ proc.stub(:call).with(1).and_return 'some value'
15
+ model.send(:typecast, 1, proc).should eq 'some value'
16
+ end
17
+ end
18
+
19
+ context 'when type does not respond to call' do
20
+ let(:typecaster) { AttributeExtensions::Typecasting::IntegerTypecaster.new }
21
+
22
+ it 'sends :call with the value to the typecaster' do
23
+ typecaster.should_receive(:call).with('1')
24
+ model.should_receive(:typecaster_for).with(:boolean).and_return(typecaster)
25
+ model.send(:typecast, '1', :boolean)
26
+ end
27
+ end
28
+ end
29
+
30
+ describe 'typecaster_for' do
31
+ it 'returns a new instance of DefaultTypecaster when type is nil' do
32
+ model.typecaster_for(nil).should be_an_instance_of(AttributeExtensions::Typecasting::DefaultTypecaster)
33
+ end
34
+
35
+ it 'returns a new instance of Typecaster that matches the type' do
36
+ class AttributeExtensions::Typecasting::SomeTypeTypecaster; end
37
+
38
+ model.typecaster_for(:some_type).should be_an_instance_of(AttributeExtensions::Typecasting::SomeTypeTypecaster)
39
+ end
40
+ end
41
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: attribute_extensions
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Delwyn de Villiers
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 3.2.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: 3.2.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 2.12.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: 2.12.0
46
+ description: Attribute Extenstions
47
+ email:
48
+ - delwyn.d@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - .rspec
55
+ - Gemfile
56
+ - LICENSE.txt
57
+ - README.md
58
+ - Rakefile
59
+ - attribute_extensions.gemspec
60
+ - lib/attribute_extensions.rb
61
+ - lib/attribute_extensions/typecasting.rb
62
+ - lib/attribute_extensions/typecasting/boolean_typecaster.rb
63
+ - lib/attribute_extensions/typecasting/default_typecaster.rb
64
+ - lib/attribute_extensions/typecasting/integer_typecaster.rb
65
+ - lib/attribute_extensions/typecasting/symbol_typecaster.rb
66
+ - lib/attribute_extensions/version.rb
67
+ - spec/attribute_extensions_spec.rb
68
+ - spec/spec_helper.rb
69
+ - spec/support/models.rb
70
+ - spec/typecasting/boolean_typecaster_spec.rb
71
+ - spec/typecasting/default_typecaster_spec.rb
72
+ - spec/typecasting/integer_typecaster_spec.rb
73
+ - spec/typecasting/symbol_typecaster_spec.rb
74
+ - spec/typecasting_spec.rb
75
+ homepage: https://github.com/delwyn/attribute_extensions
76
+ licenses: []
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 1.8.24
96
+ signing_key:
97
+ specification_version: 3
98
+ summary: Define attributes with default values and typecasting
99
+ test_files:
100
+ - spec/attribute_extensions_spec.rb
101
+ - spec/spec_helper.rb
102
+ - spec/support/models.rb
103
+ - spec/typecasting/boolean_typecaster_spec.rb
104
+ - spec/typecasting/default_typecaster_spec.rb
105
+ - spec/typecasting/integer_typecaster_spec.rb
106
+ - spec/typecasting/symbol_typecaster_spec.rb
107
+ - spec/typecasting_spec.rb