findable 0.0.3 → 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.
- checksums.yaml +4 -4
- data/.travis.yml +4 -8
- data/Gemfile +1 -1
- data/README.md +66 -14
- data/Rakefile +6 -1
- data/findable.gemspec +3 -2
- data/lib/findable.rb +2 -3
- data/lib/findable/associations.rb +52 -0
- data/lib/findable/associations/active_record_ext.rb +87 -0
- data/lib/findable/associations/utils.rb +25 -0
- data/lib/findable/base.rb +140 -80
- data/lib/findable/configuration.rb +3 -1
- data/lib/findable/errors.rb +8 -2
- data/lib/findable/query.rb +91 -0
- data/lib/findable/query/connection.rb +26 -0
- data/lib/findable/query/namespace.rb +34 -0
- data/lib/findable/railtie.rb +1 -1
- data/lib/findable/seed.rb +34 -38
- data/lib/findable/version.rb +1 -1
- data/lib/generators/findable/install_generator.rb +5 -0
- data/lib/generators/findable/templates/findable.rb +5 -2
- data/lib/generators/findable/templates/seeds.rb +1 -4
- data/spec/spec_helper.rb +1 -13
- data/spec/support/initialize_redis.rb +2 -0
- metadata +13 -10
- data/lib/findable/association.rb +0 -71
- data/lib/findable/connection.rb +0 -25
- data/lib/findable/namespace.rb +0 -23
- data/lib/findable/recordable.rb +0 -93
- data/lib/findable/serializer.rb +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3d9b4cc9d5f9ac6949eaa3db65f5dc97359a8450
|
4
|
+
data.tar.gz: 3ca9a61d80ea9b8d74f5a4e2df78082f07083012
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68ab5f2cb1c8b3fc443ff1b4d1c8199a07634d3fb7ad08a7587247c0ee9836d711f596e2286566f82e59c9dccb2d815098e84e95adc665b0c878d3a721461a1c
|
7
|
+
data.tar.gz: fdd8b84c3f07ded341d312cc6bd1117f2f64b61f5dc40865fa36877852c27b17562b6b2ff3bcb086344ec7e61317e01f058300c53d745b635e17658363793060
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
[](https://coveralls.io/r/i2bskn/findable)
|
6
6
|
[](https://codeclimate.com/github/i2bskn/findable)
|
7
7
|
|
8
|
-
Redis wrapper with API like ActiveRecord.
|
8
|
+
Redis wrapper with API like ActiveRecord.
|
9
9
|
|
10
10
|
## Installation
|
11
11
|
|
@@ -17,27 +17,79 @@ gem "findable"
|
|
17
17
|
|
18
18
|
And then execute:
|
19
19
|
|
20
|
-
|
20
|
+
```
|
21
|
+
$ bundle
|
22
|
+
```
|
23
|
+
|
24
|
+
Setup config file and seed script:
|
25
|
+
|
26
|
+
```
|
27
|
+
$ rails generate findable:install
|
28
|
+
```
|
29
|
+
|
30
|
+
Added following files:
|
31
|
+
|
32
|
+
- `config/initializers/findable.rb`
|
33
|
+
- Config file for Findable.
|
34
|
+
- `db/findable_seeds.rb`
|
35
|
+
- Seed script for Findable.
|
36
|
+
- `db/findable_seeds/.keep`
|
37
|
+
- Directory for storing seed files of Findable.
|
21
38
|
|
22
39
|
## Usage
|
23
40
|
|
41
|
+
Create seed file if static data.
|
42
|
+
|
43
|
+
Example `db/findable_seeds/tags.yml`:
|
44
|
+
|
45
|
+
```yaml
|
46
|
+
data1:
|
47
|
+
id: 1
|
48
|
+
name: Ruby
|
49
|
+
```
|
50
|
+
|
51
|
+
Create model.
|
52
|
+
|
53
|
+
Example `app/models/tag.rb`:
|
54
|
+
|
24
55
|
```ruby
|
25
|
-
class
|
26
|
-
has_many :person
|
56
|
+
class Tag < Findable::Base
|
27
57
|
end
|
58
|
+
```
|
28
59
|
|
29
|
-
|
30
|
-
fields :name, :email, :gender, :company_id
|
31
|
-
belongs_to :company
|
32
|
-
end
|
60
|
+
Execute seed script if you create seed files.
|
33
61
|
|
34
|
-
|
35
|
-
|
36
|
-
|
62
|
+
```
|
63
|
+
$ rake findable:seeds
|
64
|
+
```
|
65
|
+
|
66
|
+
Manipulate data with API like ActiveRecord.
|
67
|
+
|
68
|
+
```
|
69
|
+
$ rails console
|
70
|
+
pry(main)> Tag.find(1)
|
71
|
+
=> #<Tag:0x00000108068430 @_attributes={:id=>1, :name=>"Ruby"}>
|
72
|
+
pry(main)> golang = Tag.create(name: "Go")
|
73
|
+
=> #<Tag:0x00000107ff6420 @_attributes={:name=>"Go", :id=>2}>
|
74
|
+
pry(main)> Tag.all.each {|tag| p tag.name }
|
75
|
+
"Ruby"
|
76
|
+
"Go"
|
77
|
+
=> [#<Tag:0x00000107f49568 @_attributes={:id=>1, :name=>"Ruby"}>, #<Tag:0x00000107f492e8 @_attributes={:name=>"Go", :id=>2}>]
|
78
|
+
```
|
79
|
+
|
80
|
+
## Associations
|
81
|
+
|
82
|
+
It is possible to use the `belongs_to` and `has_one` and `has_many`.
|
83
|
+
Mutually can refer to objects of ActiveRecord and Findable.
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
class Article < ActiveRecord::Base
|
87
|
+
include Findable::Associations::ActiveRecordExt
|
88
|
+
belongs_to :tag
|
89
|
+
end
|
37
90
|
|
38
|
-
|
39
|
-
|
40
|
-
puts person.name
|
91
|
+
class Tag < Findable::Base
|
92
|
+
has_many :articles
|
41
93
|
end
|
42
94
|
```
|
43
95
|
|
data/Rakefile
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require "rspec/core/rake_task"
|
3
3
|
|
4
|
+
task :default => :spec
|
5
|
+
|
4
6
|
desc "Run all specs"
|
5
7
|
RSpec::Core::RakeTask.new(:spec) do |spec|
|
6
8
|
spec.pattern = FileList['spec/**/*_spec.rb']
|
7
9
|
end
|
8
10
|
|
9
|
-
|
11
|
+
desc "Console with library"
|
12
|
+
task :console do
|
13
|
+
sh "pry -I lib -r bundler/setup -r findable"
|
14
|
+
end
|
10
15
|
|
data/findable.gemspec
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
lib = File.expand_path('../lib', __FILE__)
|
2
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
4
|
require 'findable/version'
|
@@ -19,13 +20,13 @@ Gem::Specification.new do |spec|
|
|
19
20
|
|
20
21
|
spec.add_dependency "activesupport"
|
21
22
|
spec.add_dependency "activemodel"
|
22
|
-
spec.add_dependency "redis"
|
23
|
+
spec.add_dependency "redis-objects"
|
23
24
|
spec.add_dependency "oj"
|
24
25
|
|
25
26
|
spec.add_development_dependency "bundler"
|
26
27
|
spec.add_development_dependency "rake"
|
27
28
|
spec.add_development_dependency "rspec-rails"
|
28
|
-
spec.add_development_dependency "
|
29
|
+
spec.add_development_dependency "pry"
|
29
30
|
spec.add_development_dependency "coveralls"
|
30
31
|
end
|
31
32
|
|
data/lib/findable.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
require "active_support"
|
2
2
|
require "active_model"
|
3
|
-
require "redis"
|
3
|
+
require "redis/objects"
|
4
4
|
require "oj"
|
5
5
|
|
6
6
|
require "findable/version"
|
7
7
|
require "findable/errors"
|
8
8
|
require "findable/configuration"
|
9
|
-
require "findable/
|
10
|
-
require "findable/association"
|
9
|
+
require "findable/query"
|
11
10
|
require "findable/base"
|
12
11
|
require "findable/railtie" if defined?(Rails)
|
13
12
|
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require "findable/associations/utils"
|
2
|
+
|
3
|
+
module Findable
|
4
|
+
module Associations
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def has_many(*args)
|
9
|
+
name, options = Utils.parse_args(args)
|
10
|
+
model = Utils.model_for(name, collection: true, **options)
|
11
|
+
foreign_key = options[:foreign_key].presence || model_name.name.foreign_key
|
12
|
+
|
13
|
+
define_method(name) do
|
14
|
+
model.where(foreign_key => public_send(self.class.primary_key))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def has_one(*args)
|
19
|
+
name, options = Utils.parse_args(args)
|
20
|
+
model = Utils.model_for(name, **options)
|
21
|
+
foreign_key = options[:foreign_key].presence || model_name.name.foreign_key
|
22
|
+
|
23
|
+
define_method(name) do
|
24
|
+
model.find_by(foreign_key => public_send(self.class.primary_key))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def belongs_to(*args)
|
29
|
+
name, options = Utils.parse_args(args)
|
30
|
+
model = Utils.model_for(name, safe: true, **options)
|
31
|
+
foreign_key = options[:foreign_key].presence || name.to_s.foreign_key
|
32
|
+
|
33
|
+
if options[:polymorphic]
|
34
|
+
define_method(name) do
|
35
|
+
public_send("#{name}_type").constantize.find(public_send(foreign_key))
|
36
|
+
end
|
37
|
+
else
|
38
|
+
define_field(foreign_key)
|
39
|
+
|
40
|
+
define_method(name) do
|
41
|
+
model.find_by(model.primary_key => public_send(foreign_key))
|
42
|
+
end
|
43
|
+
|
44
|
+
define_method("#{name}=") do |value|
|
45
|
+
attributes[foreign_key.to_sym] = value ? value.public_send(model.primary_key) : nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Findable
|
2
|
+
module Associations
|
3
|
+
module ActiveRecordExt
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def has_many(*args)
|
8
|
+
name, options = Utils.parse_args(args)
|
9
|
+
model = Utils.model_for(name, collection: true, safe: true, **options)
|
10
|
+
|
11
|
+
if model && model < Findable::Base
|
12
|
+
foreign_key = options[:foreign_key].presence || model_name.name.foreign_key
|
13
|
+
|
14
|
+
define_method(name) do
|
15
|
+
model.where(foreign_key => public_send(self.class.primary_key))
|
16
|
+
end
|
17
|
+
|
18
|
+
reflection = ActiveRecord::Reflection.create(
|
19
|
+
:has_many,
|
20
|
+
name.to_sym,
|
21
|
+
nil,
|
22
|
+
options,
|
23
|
+
self,
|
24
|
+
)
|
25
|
+
ActiveRecord::Reflection.add_reflection(self, name.to_sym, reflection)
|
26
|
+
else
|
27
|
+
super
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def has_one(*args)
|
32
|
+
name, options = Utils.parse_args(args)
|
33
|
+
model = Utils.model_for(name, safe: true, **options)
|
34
|
+
|
35
|
+
if model && model < Findable::Base
|
36
|
+
foreign_key = options[:foreign_key].presence || model_name.name.foreign_key
|
37
|
+
|
38
|
+
define_method(name) do
|
39
|
+
model.find_by(foreign_key => public_send(self.class.primary_key))
|
40
|
+
end
|
41
|
+
|
42
|
+
reflection = ActiveRecord::Reflection.create(
|
43
|
+
:has_one,
|
44
|
+
name.to_sym,
|
45
|
+
nil,
|
46
|
+
options,
|
47
|
+
self,
|
48
|
+
)
|
49
|
+
ActiveRecord::Reflection.add_reflection(self, name.to_sym, reflection)
|
50
|
+
else
|
51
|
+
super
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def belongs_to(*args)
|
56
|
+
name, options = Utils.parse_args(args)
|
57
|
+
model = Utils.model_for(name, safe: true, **options)
|
58
|
+
|
59
|
+
if model && model < Findable::Base
|
60
|
+
foreign_key = options[:foreign_key].presence || name.to_s.foreign_key
|
61
|
+
|
62
|
+
define_method(name) do
|
63
|
+
model.find_by(model.primary_key => public_send(foreign_key))
|
64
|
+
end
|
65
|
+
|
66
|
+
define_method("#{name}=") do |value|
|
67
|
+
value = value ? value.public_send(model.primary_key) : nil
|
68
|
+
public_send("#{foreign_key}=", value)
|
69
|
+
end
|
70
|
+
|
71
|
+
reflection = ActiveRecord::Reflection.create(
|
72
|
+
:belongs_to,
|
73
|
+
name.to_sym,
|
74
|
+
nil,
|
75
|
+
options,
|
76
|
+
self,
|
77
|
+
)
|
78
|
+
ActiveRecord::Reflection.add_reflection(self, name.to_sym, reflection)
|
79
|
+
else
|
80
|
+
super
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Findable
|
2
|
+
module Associations
|
3
|
+
class Utils
|
4
|
+
def self.model_for(name, options = {})
|
5
|
+
unless model_name = options[:class_name].presence
|
6
|
+
name = options[:collection] ? name.to_s.singularize : name.to_s
|
7
|
+
model_name = name.camelize
|
8
|
+
end
|
9
|
+
|
10
|
+
if options[:safe]
|
11
|
+
model_name.try(:safe_constantize)
|
12
|
+
else
|
13
|
+
model_name.constantize
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.parse_args(args)
|
18
|
+
copied = args.dup
|
19
|
+
options = copied.extract_options!
|
20
|
+
[copied.first, options]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
data/lib/findable/base.rb
CHANGED
@@ -1,81 +1,95 @@
|
|
1
|
-
require "findable/
|
2
|
-
require "findable/connection"
|
3
|
-
require "findable/namespace"
|
1
|
+
require "findable/associations"
|
4
2
|
|
5
3
|
module Findable
|
6
4
|
class Base
|
7
5
|
include ActiveModel::Model
|
8
|
-
include
|
9
|
-
|
10
|
-
include
|
11
|
-
|
6
|
+
include ActiveModel::AttributeMethods
|
7
|
+
|
8
|
+
include Associations
|
9
|
+
|
10
|
+
attribute_method_suffix "="
|
11
|
+
attribute_method_suffix "?"
|
12
12
|
|
13
13
|
class << self
|
14
|
+
## field definitions
|
15
|
+
|
16
|
+
def define_field(attr, options = {})
|
17
|
+
unless public_method_defined?(attr)
|
18
|
+
options.symbolize_keys!
|
19
|
+
define_attribute_methods attr
|
20
|
+
define_method(attr) { attributes[attr.to_sym] }
|
21
|
+
column_names << attr.to_sym
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
## ActiveRecord like APIs
|
26
|
+
|
14
27
|
def primary_key
|
15
28
|
"id"
|
16
29
|
end
|
17
30
|
|
18
31
|
def column_names
|
19
|
-
|
32
|
+
@_column_names ||= [:id]
|
20
33
|
end
|
21
34
|
|
22
35
|
def all
|
23
|
-
|
36
|
+
data.map {|val| new(val) }
|
24
37
|
end
|
25
38
|
|
26
|
-
def find(
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
when values.empty? then nil
|
32
|
-
when ids.is_a?(Array) then values.map {|val| new(val)}
|
33
|
-
else new(values.first)
|
39
|
+
def find(ids)
|
40
|
+
if values = find_by_ids(ids).compact.presence
|
41
|
+
ids.is_a?(Array) ? values.map {|val| new(val) } : new(values.first)
|
42
|
+
else
|
43
|
+
raise RecordNotFound.new(self, id: ids)
|
34
44
|
end
|
35
45
|
end
|
36
46
|
|
37
|
-
def find_by(
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
47
|
+
def find_by(conditions)
|
48
|
+
if conditions.is_a?(Hash)
|
49
|
+
conditions.symbolize_keys!
|
50
|
+
if id = conditions.delete(:id)
|
51
|
+
values = find_by_ids(id).compact
|
52
|
+
case
|
53
|
+
when values.empty? then nil
|
54
|
+
when conditions.empty? then new(values.first)
|
55
|
+
else
|
56
|
+
value = values.detect {|val|
|
57
|
+
record = new(val)
|
58
|
+
conditions.all? {|k, v| record.public_send(k) == v }
|
59
|
+
}
|
60
|
+
value ? new(value) : nil
|
61
|
+
end
|
62
|
+
else
|
63
|
+
all.detect {|r|
|
64
|
+
conditions.all? {|k, v| r.public_send(k) == v }
|
65
|
+
}
|
66
|
+
end
|
48
67
|
else
|
49
|
-
|
50
|
-
|
51
|
-
return record if params.all? {|k,v| record.send(k) == v }
|
52
|
-
}
|
68
|
+
values = find_by_ids(conditions).compact
|
69
|
+
values.empty? ? nil : new(values.first)
|
53
70
|
end
|
54
71
|
end
|
55
72
|
|
56
|
-
def
|
57
|
-
|
73
|
+
def find_by!(conditions)
|
74
|
+
find_by(conditions.dup) || (raise RecordNotFound.new(self, conditions))
|
58
75
|
end
|
59
76
|
|
60
|
-
def
|
61
|
-
if
|
62
|
-
|
63
|
-
|
77
|
+
def where(conditions)
|
78
|
+
if id = conditions.delete(:id)
|
79
|
+
values = find_by_ids(id).compact
|
80
|
+
if conditions.empty?
|
81
|
+
values.map {|val| new(val) }
|
82
|
+
else
|
83
|
+
values.map {|val|
|
84
|
+
record = new(val)
|
85
|
+
conditions.all? {|k, v| record.public_send(k) == v } ? record : nil
|
86
|
+
}.compact
|
87
|
+
end
|
64
88
|
else
|
65
|
-
|
89
|
+
all.select {|r|
|
90
|
+
conditions.all? {|k, v| r.public_send(k) == v }
|
91
|
+
}
|
66
92
|
end
|
67
|
-
|
68
|
-
redis.hexists data_key, _id
|
69
|
-
end
|
70
|
-
|
71
|
-
delegate :first, :last, to: :all
|
72
|
-
|
73
|
-
def count
|
74
|
-
redis.hlen(data_key)
|
75
|
-
end
|
76
|
-
|
77
|
-
def ids
|
78
|
-
redis.hkeys(data_key).map(&:to_i)
|
79
93
|
end
|
80
94
|
|
81
95
|
def create(attrs = {})
|
@@ -83,53 +97,99 @@ module Findable
|
|
83
97
|
record.save
|
84
98
|
record
|
85
99
|
end
|
100
|
+
alias_method :create!, :create
|
86
101
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
102
|
+
[:first, :last].each do |m|
|
103
|
+
define_method(m) do
|
104
|
+
value = self.data.public_send(m)
|
105
|
+
value ? new(value) : nil
|
106
|
+
end
|
91
107
|
end
|
92
108
|
|
93
|
-
|
94
|
-
|
95
|
-
|
109
|
+
## Query APIs
|
110
|
+
|
111
|
+
delegate :find_by_ids, :data, to: :query
|
112
|
+
delegate :count, :ids, :delete_all, to: :query
|
96
113
|
alias_method :destroy_all, :delete_all
|
97
114
|
|
98
|
-
def
|
99
|
-
|
115
|
+
def exists?(obj)
|
116
|
+
if _id = id_from(obj)
|
117
|
+
query.exists?(data_key, _id)
|
118
|
+
else
|
119
|
+
false
|
120
|
+
end
|
100
121
|
end
|
101
122
|
|
102
|
-
def
|
103
|
-
|
104
|
-
record.id ||= auto_incremented_id
|
105
|
-
obj << record.id
|
106
|
-
obj << record.to_json
|
107
|
-
}
|
108
|
-
redis.hmset(data_key, *data)
|
123
|
+
def insert(obj)
|
124
|
+
query.insert(obj.attributes)
|
109
125
|
end
|
110
126
|
|
111
|
-
def
|
112
|
-
|
113
|
-
|
127
|
+
def delete(obj)
|
128
|
+
if _id = id_from(obj)
|
129
|
+
query.delete(_id)
|
130
|
+
end
|
114
131
|
end
|
115
132
|
|
116
|
-
def
|
117
|
-
|
133
|
+
def query
|
134
|
+
@_query ||= Query.new(self)
|
118
135
|
end
|
119
136
|
|
120
137
|
private
|
121
|
-
def
|
122
|
-
|
138
|
+
def id_from(obj)
|
139
|
+
obj.is_a?(self) ? obj.id : obj.to_i
|
123
140
|
end
|
141
|
+
end
|
124
142
|
|
125
|
-
|
126
|
-
|
127
|
-
|
143
|
+
def initialize(params = {})
|
144
|
+
params = Oj.load(params) if params.is_a?(String)
|
145
|
+
params.symbolize_keys!
|
146
|
+
params.keys.each {|attr| self.class.define_field(attr) }
|
147
|
+
@_attributes = params
|
148
|
+
end
|
128
149
|
|
129
|
-
|
130
|
-
|
131
|
-
|
150
|
+
def id
|
151
|
+
attributes[:id].presence
|
152
|
+
end
|
153
|
+
|
154
|
+
def id=(value)
|
155
|
+
attributes[:id] = value
|
156
|
+
end
|
157
|
+
|
158
|
+
def hash
|
159
|
+
id.hash
|
160
|
+
end
|
161
|
+
|
162
|
+
def new_record?
|
163
|
+
id ? !self.class.exists?(self) : true
|
132
164
|
end
|
165
|
+
|
166
|
+
def persisted?
|
167
|
+
!new_record?
|
168
|
+
end
|
169
|
+
|
170
|
+
def save
|
171
|
+
@_attributes = self.class.insert(self)
|
172
|
+
self
|
173
|
+
end
|
174
|
+
alias_method :save!, :save
|
175
|
+
|
176
|
+
def delete
|
177
|
+
self.class.delete(self)
|
178
|
+
end
|
179
|
+
alias_method :destroy, :delete
|
180
|
+
|
181
|
+
def attributes
|
182
|
+
@_attributes ||= {}
|
183
|
+
end
|
184
|
+
|
185
|
+
private
|
186
|
+
def attribute=(attr, value)
|
187
|
+
attributes[attr.to_sym] = value
|
188
|
+
end
|
189
|
+
|
190
|
+
def attribute?(attr)
|
191
|
+
attributes[attr.to_sym].present?
|
192
|
+
end
|
133
193
|
end
|
134
194
|
end
|
135
195
|
|