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.
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/attribute_extensions.gemspec +23 -0
- data/lib/attribute_extensions.rb +33 -0
- data/lib/attribute_extensions/typecasting.rb +22 -0
- data/lib/attribute_extensions/typecasting/boolean_typecaster.rb +15 -0
- data/lib/attribute_extensions/typecasting/default_typecaster.rb +9 -0
- data/lib/attribute_extensions/typecasting/integer_typecaster.rb +9 -0
- data/lib/attribute_extensions/typecasting/symbol_typecaster.rb +9 -0
- data/lib/attribute_extensions/version.rb +3 -0
- data/spec/attribute_extensions_spec.rb +97 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/models.rb +19 -0
- data/spec/typecasting/boolean_typecaster_spec.rb +17 -0
- data/spec/typecasting/default_typecaster_spec.rb +7 -0
- data/spec/typecasting/integer_typecaster_spec.rb +11 -0
- data/spec/typecasting/symbol_typecaster_spec.rb +11 -0
- data/spec/typecasting_spec.rb +41 -0
- metadata +107 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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
|
data/Rakefile
ADDED
@@ -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,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
|
data/spec/spec_helper.rb
ADDED
@@ -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,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
|