attrio 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +10 -1
- data/attrio.gemspec +0 -1
- data/lib/attrio.rb +9 -13
- data/lib/attrio/attribute.rb +1 -1
- data/lib/attrio/attributes_parser.rb +1 -1
- data/lib/attrio/core_ext/object.rb +0 -8
- data/lib/attrio/helpers.rb +26 -0
- data/lib/attrio/types/boolean.rb +3 -3
- data/lib/attrio/version.rb +1 -1
- data/spec/attrio/attrio_spec.rb +54 -38
- metadata +5 -25
- data/lib/attrio/core_ext/array.rb +0 -23
- data/lib/attrio/core_ext/class.rb +0 -55
- data/lib/attrio/core_ext/hash.rb +0 -38
- data/lib/attrio/core_ext/nil_object.rb +0 -7
- data/lib/attrio/core_ext/time.rb +0 -17
data/README.md
CHANGED
@@ -37,7 +37,7 @@ class User
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
```
|
40
|
-
|
40
|
+
### Accessing attributes
|
41
41
|
By default Attrio defines `#attributes` accessor which contains `Hash` with attributes names as keys and instances of `Attrio::Attribute` as values. Each instance of `Attrio::Attribute` contains following information:
|
42
42
|
* type
|
43
43
|
* writer method name
|
@@ -55,6 +55,15 @@ user.attributes
|
|
55
55
|
# :age => #<Attrio::Attribute:0x007fc44e8d4c98 @object=#<User:0x007fc44e8b2b48>, @name="age", @type=Attrio::Types::Integer, @options={}, @writer_method_name="age=", @writer_visibility=:public, @instance_variable_name="@age", @reader_method_name="age", @reader_visibility=:public>,
|
56
56
|
# :birthday = >#<Attrio::Attribute:0x007fc44e8e2e38 @object=#<User:0x007fc44e8b2b48>, @name="birthday", @type=Attrio::Types::DateTime, @options={}, @writer_method_name="birthday=", @writer_visibility=:public, @instance_variable_name="@birthday", @reader_method_name="birthday", @reader_visibility=:public>
|
57
57
|
# }
|
58
|
+
user.attributes.keys
|
59
|
+
# => [:name, :age, :birthday]
|
60
|
+
```
|
61
|
+
|
62
|
+
Attributes can be filtered.
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
user.attributes([:name, :age, :not_existing_attribute]).keys
|
66
|
+
# => [:name, :age]
|
58
67
|
```
|
59
68
|
|
60
69
|
Accessor name can be easily overridden by passing `:as` option to `define_attributes` block.
|
data/attrio.gemspec
CHANGED
data/lib/attrio.rb
CHANGED
@@ -2,21 +2,17 @@
|
|
2
2
|
|
3
3
|
require 'attrio/version'
|
4
4
|
|
5
|
-
require 'attrio/core_ext/array'
|
6
|
-
require 'attrio/core_ext/class'
|
7
|
-
require 'attrio/core_ext/hash'
|
8
|
-
require 'attrio/core_ext/nil_object'
|
9
5
|
require 'attrio/core_ext/object'
|
10
6
|
require 'attrio/core_ext/string'
|
11
|
-
require 'attrio/core_ext/time'
|
12
7
|
|
13
8
|
module Attrio
|
14
|
-
autoload :AttributesParser, 'attrio/attributes_parser'
|
9
|
+
autoload :AttributesParser, 'attrio/attributes_parser'
|
15
10
|
autoload :Initialize, 'attrio/initialize'
|
16
11
|
autoload :Inspect, 'attrio/inspect'
|
17
12
|
autoload :Reset, 'attrio/reset'
|
13
|
+
autoload :Helpers, 'attrio/helpers'
|
18
14
|
|
19
|
-
def self.included(base)
|
15
|
+
def self.included(base)
|
20
16
|
base.send :include, Attrio::Initialize
|
21
17
|
base.send :include, Attrio::Inspect
|
22
18
|
base.send :include, Attrio::Reset
|
@@ -27,16 +23,16 @@ module Attrio
|
|
27
23
|
module ClassMethods
|
28
24
|
def define_attributes(options = {}, &block)
|
29
25
|
options[:as] ||= :attributes
|
30
|
-
|
31
|
-
# cattr_accessor options[:as].to_sym
|
26
|
+
|
32
27
|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
33
28
|
@@#{options[:as]} ||= {}
|
34
29
|
|
35
30
|
def self.#{options[:as]}(attributes = [])
|
36
|
-
attributes =
|
31
|
+
attributes = Helpers.to_a(attributes).flatten
|
37
32
|
return @@#{options[:as]} if attributes.empty?
|
38
33
|
|
39
|
-
@@#{options[:as]}.
|
34
|
+
attributes = @@#{options[:as]}.keys & attributes
|
35
|
+
@@#{options[:as]}.select{ |k,v| attributes.include?(k) }
|
40
36
|
end
|
41
37
|
|
42
38
|
def #{options[:as]}(attributes = [])
|
@@ -53,7 +49,7 @@ module Attrio
|
|
53
49
|
|
54
50
|
def const_missing(name)
|
55
51
|
Attrio::AttributesParser.cast_type(name) || super
|
56
|
-
end
|
52
|
+
end
|
57
53
|
end
|
58
54
|
|
59
55
|
autoload :Attribute, 'attrio/attribute'
|
@@ -74,4 +70,4 @@ module Attrio
|
|
74
70
|
autoload :Symbol, 'attrio/types/symbol'
|
75
71
|
autoload :Time, 'attrio/types/time'
|
76
72
|
end
|
77
|
-
end
|
73
|
+
end
|
data/lib/attrio/attribute.rb
CHANGED
@@ -5,7 +5,7 @@ module Attrio
|
|
5
5
|
attr_reader :klass, :name, :type, :options
|
6
6
|
|
7
7
|
def initialize(klass, name, type, options)
|
8
|
-
@klass = klass; @name = name; @type = type; @options = options
|
8
|
+
@klass = klass; @name = name; @type = type; @options = Helpers.symbolize_hash_keys(options)
|
9
9
|
end
|
10
10
|
|
11
11
|
def reader_method_name
|
@@ -14,7 +14,7 @@ module Attrio
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def attr(*args)
|
17
|
-
attribute_options = args.
|
17
|
+
attribute_options = (args.last.kind_of?(Hash) ? args.pop : Hash.new)
|
18
18
|
attribute_name = args[0].to_s
|
19
19
|
|
20
20
|
type = self.class.cast_type(attribute_options.delete(:type) || args[1])
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Attrio
|
2
|
+
module Helpers
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def to_a(object)
|
6
|
+
if object.nil?
|
7
|
+
[]
|
8
|
+
elsif object.respond_to?(:to_ary)
|
9
|
+
object.to_ary || [object]
|
10
|
+
else
|
11
|
+
[object]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# note that returning hash without symbolizing anything
|
16
|
+
# does not cause this to fail
|
17
|
+
def symbolize_hash_keys(hash)
|
18
|
+
hash.inject({}) do |new_hash, (key, value)|
|
19
|
+
new_hash[(key.to_sym rescue key) || key] = value
|
20
|
+
new_hash
|
21
|
+
end
|
22
|
+
hash
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/attrio/types/boolean.rb
CHANGED
@@ -8,10 +8,10 @@ module Attrio
|
|
8
8
|
false_values = options[:false] || options[:false_values]
|
9
9
|
|
10
10
|
if false_values.present?
|
11
|
-
return
|
11
|
+
return Helpers.to_a(false_values).flatten.include?(value) ? false : true
|
12
12
|
else
|
13
|
-
return
|
14
|
-
end
|
13
|
+
return Helpers.to_a(true_values).flatten.include?(value) ? true : false
|
14
|
+
end
|
15
15
|
end
|
16
16
|
|
17
17
|
def self.typecasted?(value)
|
data/lib/attrio/version.rb
CHANGED
data/spec/attrio/attrio_spec.rb
CHANGED
@@ -1,59 +1,75 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Attrio do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
describe Attrio do
|
4
|
+
let(:model) do
|
5
|
+
attributes_name = self.respond_to?(:attributes_name) ? self.attributes_name : 'attributes'
|
6
|
+
Class.new do
|
7
|
+
include Attrio
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
define_attributes :as => attributes_name do
|
10
|
+
attr :name, String
|
11
|
+
attr :age, Integer
|
12
|
+
attr :created_at, DateTime, :default => proc{ Time.now }
|
13
|
+
end
|
11
14
|
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'Attrio included with default parameters' do
|
18
|
+
subject { model }
|
19
|
+
|
20
|
+
it { should respond_to(:define_attributes) }
|
21
|
+
it { should respond_to(:attributes) }
|
12
22
|
|
13
|
-
context '
|
14
|
-
|
15
|
-
Class.new do
|
16
|
-
include Attrio
|
23
|
+
context 'instance' do
|
24
|
+
subject { model.new }
|
17
25
|
|
18
|
-
|
19
|
-
|
26
|
+
it { should be }
|
27
|
+
it { should respond_to(:reset_attributes) }
|
28
|
+
it { should respond_to(:attributes) }
|
29
|
+
|
30
|
+
context '#attributes' do
|
31
|
+
it 'should be a kind of Hash' do
|
32
|
+
subject.attributes.should be_a_kind_of Hash
|
20
33
|
end
|
21
|
-
end
|
22
34
|
|
23
|
-
|
24
|
-
|
25
|
-
|
35
|
+
it 'should be present' do
|
36
|
+
subject.attributes.should be_present
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should return a full set of attributes' do
|
40
|
+
subject.attributes.keys.should match_array([:name, :age, :created_at])
|
41
|
+
end
|
26
42
|
|
27
|
-
|
28
|
-
|
43
|
+
it 'should return a filtered set of attributes' do
|
44
|
+
subject.attributes(:name).keys.should match_array([:name])
|
45
|
+
subject.attributes([:name]).keys.should match_array([:name])
|
46
|
+
end
|
29
47
|
|
30
|
-
it 'should
|
31
|
-
|
48
|
+
it 'should return a blank set of attributes for not existing filter' do
|
49
|
+
subject.attributes([:not_existing_attribute]).keys.should match_array([])
|
32
50
|
end
|
33
51
|
end
|
34
52
|
end
|
53
|
+
end
|
35
54
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
55
|
+
context 'Attrio included with :as parameter' do
|
56
|
+
let(:attributes_name) do
|
57
|
+
'api_attributes'
|
58
|
+
end
|
40
59
|
|
41
|
-
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
60
|
+
subject { model }
|
45
61
|
|
46
|
-
|
47
|
-
|
48
|
-
end
|
62
|
+
it { should respond_to(:define_attributes) }
|
63
|
+
it { should respond_to(:api_attributes) }
|
49
64
|
|
50
|
-
|
51
|
-
|
65
|
+
context 'instance' do
|
66
|
+
subject { model.new }
|
52
67
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
68
|
+
it { should be }
|
69
|
+
it { should respond_to(:reset_api_attributes) }
|
70
|
+
it { should respond_to(:api_attributes) }
|
71
|
+
|
72
|
+
its(:api_attributes){ should be_present }
|
57
73
|
end
|
58
74
|
end
|
59
75
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: attrio
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-05
|
13
|
+
date: 2013-06-05 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rspec
|
@@ -108,22 +108,6 @@ dependencies:
|
|
108
108
|
- - ! '>='
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: rake
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
none: false
|
115
|
-
requirements:
|
116
|
-
- - ! '>='
|
117
|
-
- !ruby/object:Gem::Version
|
118
|
-
version: '0'
|
119
|
-
type: :development
|
120
|
-
prerelease: false
|
121
|
-
version_requirements: !ruby/object:Gem::Requirement
|
122
|
-
none: false
|
123
|
-
requirements:
|
124
|
-
- - ! '>='
|
125
|
-
- !ruby/object:Gem::Version
|
126
|
-
version: '0'
|
127
111
|
description:
|
128
112
|
email: hello@jetrockets.ru
|
129
113
|
executables: []
|
@@ -144,18 +128,14 @@ files:
|
|
144
128
|
- lib/attrio/builders/accessor_builder.rb
|
145
129
|
- lib/attrio/builders/reader_builder.rb
|
146
130
|
- lib/attrio/builders/writer_builder.rb
|
147
|
-
- lib/attrio/core_ext/array.rb
|
148
|
-
- lib/attrio/core_ext/class.rb
|
149
|
-
- lib/attrio/core_ext/hash.rb
|
150
|
-
- lib/attrio/core_ext/nil_object.rb
|
151
131
|
- lib/attrio/core_ext/object.rb
|
152
132
|
- lib/attrio/core_ext/string.rb
|
153
|
-
- lib/attrio/core_ext/time.rb
|
154
133
|
- lib/attrio/default_value.rb
|
155
134
|
- lib/attrio/default_value/base.rb
|
156
135
|
- lib/attrio/default_value/callable.rb
|
157
136
|
- lib/attrio/default_value/clonable.rb
|
158
137
|
- lib/attrio/default_value/symbol.rb
|
138
|
+
- lib/attrio/helpers.rb
|
159
139
|
- lib/attrio/initialize.rb
|
160
140
|
- lib/attrio/inspect.rb
|
161
141
|
- lib/attrio/reset.rb
|
@@ -200,7 +180,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
200
180
|
version: '0'
|
201
181
|
segments:
|
202
182
|
- 0
|
203
|
-
hash:
|
183
|
+
hash: -2263639350992331288
|
204
184
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
205
185
|
none: false
|
206
186
|
requirements:
|
@@ -209,7 +189,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
209
189
|
version: '0'
|
210
190
|
segments:
|
211
191
|
- 0
|
212
|
-
hash:
|
192
|
+
hash: -2263639350992331288
|
213
193
|
requirements: []
|
214
194
|
rubyforge_project:
|
215
195
|
rubygems_version: 1.8.24
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
class Array # :nodoc:
|
4
|
-
def extract_options!
|
5
|
-
if last.is_a?(Hash) && last.extractable_options?
|
6
|
-
pop
|
7
|
-
else
|
8
|
-
{}
|
9
|
-
end
|
10
|
-
end unless method_defined?(:extract_options!)
|
11
|
-
|
12
|
-
class << self
|
13
|
-
def wrap(object)
|
14
|
-
if object.nil?
|
15
|
-
[]
|
16
|
-
elsif object.respond_to?(:to_ary)
|
17
|
-
object.to_ary || [object]
|
18
|
-
else
|
19
|
-
[object]
|
20
|
-
end
|
21
|
-
end unless method_defined?(:wrap)
|
22
|
-
end
|
23
|
-
end
|
@@ -1,55 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
class Class
|
4
|
-
def cattr_reader(*syms)
|
5
|
-
options = syms.extract_options!
|
6
|
-
syms.each do |sym|
|
7
|
-
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
8
|
-
unless defined? @@#{sym}
|
9
|
-
@@#{sym} = nil
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.#{sym}
|
13
|
-
@@#{sym}
|
14
|
-
end
|
15
|
-
EOS
|
16
|
-
|
17
|
-
unless options[:instance_reader] == false || options[:instance_accessor] == false
|
18
|
-
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
19
|
-
def #{sym}
|
20
|
-
@@#{sym}
|
21
|
-
end
|
22
|
-
EOS
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end unless method_defined?(:cattr_reader)
|
26
|
-
|
27
|
-
def cattr_writer(*syms)
|
28
|
-
options = syms.extract_options!
|
29
|
-
syms.each do |sym|
|
30
|
-
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
31
|
-
unless defined? @@#{sym}
|
32
|
-
@@#{sym} = nil
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.#{sym}=(obj)
|
36
|
-
@@#{sym} = obj
|
37
|
-
end
|
38
|
-
EOS
|
39
|
-
|
40
|
-
unless options[:instance_writer] == false || options[:instance_accessor] == false
|
41
|
-
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
42
|
-
def #{sym}=(obj)
|
43
|
-
@@#{sym} = obj
|
44
|
-
end
|
45
|
-
EOS
|
46
|
-
end
|
47
|
-
self.send("#{sym}=", yield) if block_given?
|
48
|
-
end
|
49
|
-
end unless method_defined?(:cattr_writer)
|
50
|
-
|
51
|
-
def cattr_accessor(*syms, &blk)
|
52
|
-
cattr_reader(*syms)
|
53
|
-
cattr_writer(*syms, &blk)
|
54
|
-
end unless method_defined?(:cattr_accessor)
|
55
|
-
end
|
data/lib/attrio/core_ext/hash.rb
DELETED
@@ -1,38 +0,0 @@
|
|
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
|
-
|
32
|
-
def slice(*keys)
|
33
|
-
keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
|
34
|
-
hash = self.class.new
|
35
|
-
keys.each { |k| hash[k] = self[k] if has_key?(k) }
|
36
|
-
hash
|
37
|
-
end unless method_defined?(:slice?)
|
38
|
-
end # Hash
|
data/lib/attrio/core_ext/time.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
class Time # :nodoc:
|
4
|
-
class << self
|
5
|
-
def strptime(date, format, now=self.now)
|
6
|
-
d = Date._strptime(date, format)
|
7
|
-
raise ArgumentError, "invalid strptime format - `#{format}'" unless d
|
8
|
-
if seconds = d[:seconds]
|
9
|
-
Time.at(seconds)
|
10
|
-
else
|
11
|
-
year = d[:year]
|
12
|
-
year = yield(year) if year && block_given?
|
13
|
-
make_time(year, d[:mon], d[:mday], d[:hour], d[:min], d[:sec], d[:sec_fraction], d[:zone], now)
|
14
|
-
end
|
15
|
-
end unless method_defined?(:strptime)
|
16
|
-
end
|
17
|
-
end
|