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.
Files changed (41) hide show
  1. data/.gitignore +17 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +49 -0
  7. data/Rakefile +8 -0
  8. data/attrio.gemspec +27 -0
  9. data/lib/attrio.rb +62 -0
  10. data/lib/attrio/attribute.rb +68 -0
  11. data/lib/attrio/attributes_parser.rb +56 -0
  12. data/lib/attrio/builders/accessor_builder.rb +24 -0
  13. data/lib/attrio/builders/reader_builder.rb +23 -0
  14. data/lib/attrio/builders/writer_builder.rb +24 -0
  15. data/lib/attrio/core_ext/array.rb +23 -0
  16. data/lib/attrio/core_ext/class.rb +55 -0
  17. data/lib/attrio/core_ext/hash.rb +31 -0
  18. data/lib/attrio/core_ext/object.rb +11 -0
  19. data/lib/attrio/core_ext/string.rb +15 -0
  20. data/lib/attrio/initialize.rb +29 -0
  21. data/lib/attrio/inspect.rb +33 -0
  22. data/lib/attrio/reset.rb +21 -0
  23. data/lib/attrio/types/base.rb +29 -0
  24. data/lib/attrio/types/boolean.rb +26 -0
  25. data/lib/attrio/types/date.rb +19 -0
  26. data/lib/attrio/types/date_time.rb +19 -0
  27. data/lib/attrio/types/float.rb +19 -0
  28. data/lib/attrio/types/integer.rb +19 -0
  29. data/lib/attrio/types/symbol.rb +19 -0
  30. data/lib/attrio/types/time.rb +19 -0
  31. data/lib/attrio/version.rb +12 -0
  32. data/spec/attrio/initialize_spec.rb +29 -0
  33. data/spec/attrio/types/boolean_spec.rb +101 -0
  34. data/spec/attrio/types/date_spec.rb +80 -0
  35. data/spec/attrio/types/date_time_spec.rb +80 -0
  36. data/spec/attrio/types/float_spec.rb +38 -0
  37. data/spec/attrio/types/integer_spec.rb +38 -0
  38. data/spec/attrio/types/symbol_spec.rb +41 -0
  39. data/spec/attrio/types/time_spec.rb +80 -0
  40. data/spec/spec_helper.rb +31 -0
  41. 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,11 @@
1
+ # encoding: utf-8
2
+
3
+ class Object
4
+ def blank?
5
+ !self
6
+ end unless method_defined? :blank?
7
+
8
+ def present?
9
+ !blank?
10
+ end unless method_defined? :present?
11
+ end
@@ -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
@@ -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,12 @@
1
+ # encoding: utf-8
2
+
3
+ module Attrio
4
+ module Version
5
+ MAJOR = 0
6
+ MINOR = 1
7
+ PATCH = 0
8
+ BUILD = nil
9
+
10
+ STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
11
+ end
12
+ 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