attrio 0.1.0
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/.travis.yml +8 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +49 -0
- data/Rakefile +8 -0
- data/attrio.gemspec +27 -0
- data/lib/attrio.rb +62 -0
- data/lib/attrio/attribute.rb +68 -0
- data/lib/attrio/attributes_parser.rb +56 -0
- data/lib/attrio/builders/accessor_builder.rb +24 -0
- data/lib/attrio/builders/reader_builder.rb +23 -0
- data/lib/attrio/builders/writer_builder.rb +24 -0
- data/lib/attrio/core_ext/array.rb +23 -0
- data/lib/attrio/core_ext/class.rb +55 -0
- data/lib/attrio/core_ext/hash.rb +31 -0
- data/lib/attrio/core_ext/object.rb +11 -0
- data/lib/attrio/core_ext/string.rb +15 -0
- data/lib/attrio/initialize.rb +29 -0
- data/lib/attrio/inspect.rb +33 -0
- data/lib/attrio/reset.rb +21 -0
- data/lib/attrio/types/base.rb +29 -0
- data/lib/attrio/types/boolean.rb +26 -0
- data/lib/attrio/types/date.rb +19 -0
- data/lib/attrio/types/date_time.rb +19 -0
- data/lib/attrio/types/float.rb +19 -0
- data/lib/attrio/types/integer.rb +19 -0
- data/lib/attrio/types/symbol.rb +19 -0
- data/lib/attrio/types/time.rb +19 -0
- data/lib/attrio/version.rb +12 -0
- data/spec/attrio/initialize_spec.rb +29 -0
- data/spec/attrio/types/boolean_spec.rb +101 -0
- data/spec/attrio/types/date_spec.rb +80 -0
- data/spec/attrio/types/date_time_spec.rb +80 -0
- data/spec/attrio/types/float_spec.rb +38 -0
- data/spec/attrio/types/integer_spec.rb +38 -0
- data/spec/attrio/types/symbol_spec.rb +41 -0
- data/spec/attrio/types/time_spec.rb +80 -0
- data/spec/spec_helper.rb +31 -0
- metadata +177 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Hash # :nodoc:
|
4
|
+
def symbolize_keys # :nodoc:
|
5
|
+
inject({}) do |hash, (key, value)|
|
6
|
+
hash[(key.to_sym rescue key) || key] = value
|
7
|
+
hash
|
8
|
+
end
|
9
|
+
end unless method_defined?(:symbolize_keys)
|
10
|
+
|
11
|
+
def symbolize_keys! # :nodoc:
|
12
|
+
hash = symbolize_keys
|
13
|
+
hash.each do |key, val|
|
14
|
+
hash[key] = case val
|
15
|
+
when Hash
|
16
|
+
val.symbolize_keys!
|
17
|
+
when Array
|
18
|
+
val.map do |item|
|
19
|
+
item.is_a?(Hash) ? item.symbolize_keys! : item
|
20
|
+
end
|
21
|
+
else
|
22
|
+
val
|
23
|
+
end
|
24
|
+
end
|
25
|
+
return hash
|
26
|
+
end unless method_defined?(:symbolize_keys!)
|
27
|
+
|
28
|
+
def extractable_options?
|
29
|
+
instance_of?(Hash)
|
30
|
+
end unless method_defined?(:extractable_options?)
|
31
|
+
end # Hash
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class String # :nodoc:
|
4
|
+
def underscore
|
5
|
+
self.gsub(/::/, '/').
|
6
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
7
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
8
|
+
tr("-", "_").
|
9
|
+
downcase
|
10
|
+
end unless method_defined?(:underscore)
|
11
|
+
|
12
|
+
def camelize
|
13
|
+
self.split(/[^a-z0-9]/i).map{|w| w.capitalize}.join
|
14
|
+
end unless method_defined?(:camelize)
|
15
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Attrio
|
4
|
+
module Initialize
|
5
|
+
def self.included(base)
|
6
|
+
base.send(:extend, Attrio::Initialize::ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def define_attrio_new(as)
|
11
|
+
# define_singleton_method(:new) do |*args, &block|
|
12
|
+
# obj = self.allocate
|
13
|
+
# obj.send "reset_#{as}!"
|
14
|
+
# obj.send :initialize, *args, &block
|
15
|
+
# obj
|
16
|
+
# end
|
17
|
+
|
18
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
19
|
+
def self.new(*args, &block)
|
20
|
+
obj = self.allocate
|
21
|
+
obj.send "reset_#{as}!"
|
22
|
+
obj.send :initialize, *args, &block
|
23
|
+
obj
|
24
|
+
end
|
25
|
+
EOS
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Attrio
|
4
|
+
module Inspect
|
5
|
+
def self.included(base)
|
6
|
+
base.send(:extend, Attrio::Inspect::ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def define_attrio_inspect(as)
|
11
|
+
define_method(:inspect) do
|
12
|
+
inspection = self.send(as.to_s).map { |key, attribute|
|
13
|
+
self.inspect_attribute(key, attribute.instance_variable_name)
|
14
|
+
}.compact.join(', ')
|
15
|
+
|
16
|
+
"#<#{self.class} #{inspection}>"
|
17
|
+
end
|
18
|
+
|
19
|
+
define_method(:inspect_attribute) do |attribute_name, instance_variable_name|
|
20
|
+
value = instance_variable_get(instance_variable_name.to_s)
|
21
|
+
|
22
|
+
if value.is_a?(String) && value.length > 50
|
23
|
+
"#{attribute_name.to_s}[#{value.size}]: " + "#{value[0..50]}...".inspect
|
24
|
+
elsif value.is_a?(Array) && value.length > 5
|
25
|
+
"#{attribute_name.to_s}[#{value.size}]: " + "#{value[0..5]}...".inspect
|
26
|
+
else
|
27
|
+
"#{attribute_name.to_s}: " + value.inspect
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/attrio/reset.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Attrio
|
4
|
+
module Reset
|
5
|
+
def self.included(base)
|
6
|
+
base.send(:extend, Attrio::Reset::ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def define_attrio_reset(as)
|
11
|
+
define_method "reset_#{as.to_s}" do
|
12
|
+
self.send(as.to_s).values.each do |attribute|
|
13
|
+
self.send(attribute.writer_method_name, attribute.default_value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
self.send(:alias_method, "reset_#{as.to_s}!", "reset_#{as.to_s}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Attrio
|
4
|
+
module Types
|
5
|
+
class Base < Object
|
6
|
+
private_class_method :new
|
7
|
+
|
8
|
+
def self.typecast(value, options = {})
|
9
|
+
raise NotImplementedError
|
10
|
+
end
|
11
|
+
|
12
|
+
def self._typecast(value, options = {})
|
13
|
+
self.typecasted?(value) ? value : self.typecast(value, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.typecasted?(value)
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.default_reader_aliases(method_name)
|
21
|
+
[]
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.default_writer_aliases(method_name)
|
25
|
+
[]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Attrio
|
4
|
+
module Types
|
5
|
+
class Boolean < Base
|
6
|
+
def self.typecast(value, options = {})
|
7
|
+
true_values = options[:true] || options[:true_values] || ['yes', '1', 1, 'true']
|
8
|
+
false_values = options[:false] || options[:false_values]
|
9
|
+
|
10
|
+
if false_values.present?
|
11
|
+
return Array.wrap(false_values).flatten.include?(value) ? false : true
|
12
|
+
else
|
13
|
+
return Array.wrap(true_values).flatten.include?(value) ? true : false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.typecasted?(value)
|
18
|
+
value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.default_reader_aliases(method_name)
|
22
|
+
super.push("#{method_name}?").flatten.uniq
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Attrio
|
4
|
+
module Types
|
5
|
+
class Date < Base
|
6
|
+
def self.typecast(value, options = {})
|
7
|
+
begin
|
8
|
+
options[:format].present? ? ::Date.strptime(value, options[:format]) : ::Date.parse(value)
|
9
|
+
rescue ArgumentError => e
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.typecasted?(value)
|
15
|
+
value.is_a? ::Date
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Attrio
|
4
|
+
module Types
|
5
|
+
class DateTime < Base
|
6
|
+
def self.typecast(value, options = {})
|
7
|
+
begin
|
8
|
+
options[:format].present? ? ::DateTime.strptime(value, options[:format]) : ::DateTime.parse(value)
|
9
|
+
rescue ArgumentError => e
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.typecasted?(value)
|
15
|
+
value.is_a? ::DateTime
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Attrio
|
4
|
+
module Types
|
5
|
+
class Float < Base
|
6
|
+
def self.typecast(value, options = {})
|
7
|
+
begin
|
8
|
+
value.to_f
|
9
|
+
rescue NoMethodError => e
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.typecasted?(value)
|
15
|
+
value.is_a? ::Float
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Attrio
|
4
|
+
module Types
|
5
|
+
class Integer < Base
|
6
|
+
def self.typecast(value, options = {})
|
7
|
+
begin
|
8
|
+
value.to_i
|
9
|
+
rescue NoMethodError => e
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.typecasted?(value)
|
15
|
+
value.is_a? ::Integer
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Attrio
|
4
|
+
module Types
|
5
|
+
class Symbol < Base
|
6
|
+
def self.typecast(value, options = {})
|
7
|
+
begin
|
8
|
+
value.underscore.to_sym
|
9
|
+
rescue NoMethodError => e
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.typecasted?(value)
|
15
|
+
value.is_a? ::Symbol
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Attrio
|
4
|
+
module Types
|
5
|
+
class Time < Base
|
6
|
+
def self.typecast(value, options = {})
|
7
|
+
begin
|
8
|
+
options[:format].present? ? ::Time.strptime(value, options[:format]) : ::Time.parse(value)
|
9
|
+
rescue ArgumentError => e
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.typecasted?(value)
|
15
|
+
value.is_a? ::Time
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Attrio::Initialize do
|
4
|
+
let(:model) do
|
5
|
+
Class.new do
|
6
|
+
include Attrio
|
7
|
+
|
8
|
+
define_attributes do
|
9
|
+
attr :without_default, Integer
|
10
|
+
attr :with_default, String, :default => "default"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:object){ model.new }
|
16
|
+
|
17
|
+
context "without 'default' option" do
|
18
|
+
it "should set variable to nil" do
|
19
|
+
object.without_default.should be_nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "with 'default' option" do
|
24
|
+
it "should set variable to default value" do
|
25
|
+
object.with_default.should == 'default'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Attrio::Types::Boolean do
|
4
|
+
context 'standard casting conventions' do
|
5
|
+
let(:model) do
|
6
|
+
Class.new do
|
7
|
+
include Attrio
|
8
|
+
|
9
|
+
define_attributes do
|
10
|
+
attr :boolean_attribute, Boolean
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:object){ model.new }
|
16
|
+
|
17
|
+
context 'not typecasted assignment' do
|
18
|
+
it 'should cast "true"' do
|
19
|
+
object.boolean_attribute = 'true'
|
20
|
+
object.boolean_attribute?.should be_true
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should cast "1"' do
|
24
|
+
object.boolean_attribute = '1'
|
25
|
+
object.boolean_attribute?.should be_true
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should cast 1' do
|
29
|
+
object.boolean_attribute = 1
|
30
|
+
object.boolean_attribute?.should be_true
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should cast "yes"' do
|
34
|
+
object.boolean_attribute = 'yes'
|
35
|
+
object.boolean_attribute?.should be_true
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should cast "false"' do
|
39
|
+
object.boolean_attribute = 'false'
|
40
|
+
object.boolean_attribute?.should be_false
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should cast "0"' do
|
44
|
+
object.boolean_attribute = '0'
|
45
|
+
object.boolean_attribute?.should be_false
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should cast 0' do
|
49
|
+
object.boolean_attribute = 0
|
50
|
+
object.boolean_attribute?.should be_false
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should cast "no"' do
|
54
|
+
object.boolean_attribute = 'no'
|
55
|
+
object.boolean_attribute?.should be_false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'typecasted assignment' do
|
60
|
+
it 'should assign <TrueClass> and do not typecast' do
|
61
|
+
object.boolean_attribute = true
|
62
|
+
object.boolean_attribute?.should be_true
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should assign <FalseClass> and do not typecast' do
|
66
|
+
object.boolean_attribute = false
|
67
|
+
object.boolean_attribute?.should be_false
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'overriden TrueClass casting with "yeah" value' do
|
73
|
+
let(:model) do
|
74
|
+
Class.new do
|
75
|
+
include Attrio
|
76
|
+
|
77
|
+
define_attributes do
|
78
|
+
attr :boolean_attribute, Boolean, :true => ['yeah']
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
let(:object){ model.new }
|
84
|
+
|
85
|
+
it 'should cast "yeah" as TrueClass' do
|
86
|
+
object.boolean_attribute = 'yeah'
|
87
|
+
object.boolean_attribute?.should be_true
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should cast anything else as FalseClass' do
|
91
|
+
object.boolean_attribute = 'yes'
|
92
|
+
object.boolean_attribute?.should be_false
|
93
|
+
|
94
|
+
object.boolean_attribute = 1
|
95
|
+
object.boolean_attribute?.should be_false
|
96
|
+
|
97
|
+
object.boolean_attribute = 0
|
98
|
+
object.boolean_attribute?.should be_false
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|