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 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.
@@ -23,5 +23,4 @@ Gem::Specification.new do |gem|
23
23
  gem.add_development_dependency 'coveralls'
24
24
  gem.add_development_dependency 'rake'
25
25
  gem.add_development_dependency 'bundler'
26
- gem.add_development_dependency "rake"
27
26
  end
@@ -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 = Array.wrap(attributes).flatten
31
+ attributes = Helpers.to_a(attributes).flatten
37
32
  return @@#{options[:as]} if attributes.empty?
38
33
 
39
- @@#{options[:as]}.slice(attributes.map { |attr| attr.to_sym })
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
@@ -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.symbolize_keys
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.extract_options!
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])
@@ -8,12 +8,4 @@ class Object
8
8
  def present?
9
9
  !blank?
10
10
  end unless method_defined? :present?
11
-
12
- def try(*a, &b)
13
- if a.empty? && block_given?
14
- yield self
15
- else
16
- __send__(*a, &b)
17
- end
18
- end unless method_defined? :try?
19
11
  end
@@ -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
@@ -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 Array.wrap(false_values).flatten.include?(value) ? false : true
11
+ return Helpers.to_a(false_values).flatten.include?(value) ? false : true
12
12
  else
13
- return Array.wrap(true_values).flatten.include?(value) ? true : false
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)
@@ -3,7 +3,7 @@
3
3
  module Attrio
4
4
  module Version
5
5
  MAJOR = 0
6
- MINOR = 2
6
+ MINOR = 3
7
7
  PATCH = 0
8
8
  BUILD = nil
9
9
 
@@ -1,59 +1,75 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Attrio do
4
- describe 'model' do
5
- let(:model) do
6
- Class.new { include Attrio }
7
- end
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
- it 'should respond_to define_attributes' do
10
- model.respond_to?(:define_attributes).should be_true
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 'without options' do
14
- let(:model) do
15
- Class.new do
16
- include Attrio
23
+ context 'instance' do
24
+ subject { model.new }
17
25
 
18
- define_attributes do
19
- end
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
- it 'should respond_to attributes' do
24
- model.respond_to?(:attributes).should be_true
25
- end
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
- describe 'object' do
28
- let(:object) { model.new }
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 respond_to reset_attributes' do
31
- object.respond_to?(:reset_attributes).should be_true
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
- context 'with option :as' do
37
- let(:model) do
38
- Class.new do
39
- include Attrio
55
+ context 'Attrio included with :as parameter' do
56
+ let(:attributes_name) do
57
+ 'api_attributes'
58
+ end
40
59
 
41
- define_attributes :as => :model_attributes do
42
- end
43
- end
44
- end
60
+ subject { model }
45
61
 
46
- it 'should respond_to model_attributes' do
47
- model.respond_to?(:model_attributes).should be_true
48
- end
62
+ it { should respond_to(:define_attributes) }
63
+ it { should respond_to(:api_attributes) }
49
64
 
50
- describe 'object' do
51
- let(:object) { model.new }
65
+ context 'instance' do
66
+ subject { model.new }
52
67
 
53
- it 'should respond_to reset_model_attributes' do
54
- object.respond_to?(:reset_model_attributes).should be_true
55
- end
56
- end
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.2.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-27 00:00:00.000000000 Z
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: 2070292704708193097
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: 2070292704708193097
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
@@ -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
@@ -1,7 +0,0 @@
1
- # encoding: utf-8
2
-
3
- class NilClass
4
- def try(*args)
5
- nil
6
- end
7
- end
@@ -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