passive_record 0.1.1 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -0
- data/README.md +9 -3
- data/lib/passive_record.rb +42 -4
- data/lib/passive_record/associations.rb +7 -6
- data/lib/passive_record/associations/has_many_through.rb +15 -3
- data/lib/passive_record/associations/has_one.rb +6 -4
- data/lib/passive_record/class_inheritable_attrs.rb +28 -0
- data/lib/passive_record/core/identifier.rb +8 -0
- data/lib/passive_record/core/query.rb +17 -1
- data/lib/passive_record/hooks.rb +10 -12
- data/lib/passive_record/version.rb +1 -1
- data/spec/passive_record_spec.rb +40 -6
- data/spec/spec_helper.rb +24 -5
- metadata +3 -3
- data/lib/passive_record/hstruct.rb +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 028f8b542632306cf8fffbcb2cf7140e98220ffe
|
4
|
+
data.tar.gz: b3634261a2df5095b0663d79386a07fb2947c5ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d69bf45706fab2747b712c27b24d7189e6440cfafc1c3dc8b17fed3204213cbfb033f1fedd1efbb7e6754e1371cf4025c9c7b10335b037be666eec9554e9fc6
|
7
|
+
data.tar.gz: fe63859955c044abba1e8ecd06d8affe86162a6f6e4fd83e1e7e258743b9da51542f25b359b8ae8c6fdf04716039ea44612b1e8489150432b4e76a52b2d62ff5
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -4,7 +4,10 @@
|
|
4
4
|
* [Documentation](http://rubydoc.info/gems/passive_record/frames)
|
5
5
|
* [Email](mailto:jweissman1986 at gmail.com)
|
6
6
|
|
7
|
-
[![Code Climate GPA](https://codeclimate.com/github
|
7
|
+
[![Code Climate GPA](https://codeclimate.com/github/deepcerulean/passive_record/badges/gpa.svg)](https://codeclimate.com/github/deepcerulean/passive_record)
|
8
|
+
[![Codeship Status for deepcerulean/passive_record](https://www.codeship.io/projects/66bb2d90-ba61-0133-af95-025ac38368ea/status)](https://codeship.com/projects/128700)
|
9
|
+
[![Test Coverage](https://codeclimate.com/github/deepcerulean/passive_record/badges/coverage.svg)](https://codeclimate.com/github/deepcerulean/passive_record/coverage)
|
10
|
+
|
8
11
|
|
9
12
|
## Description
|
10
13
|
|
@@ -19,7 +22,7 @@ or look them up based on attributes,
|
|
19
22
|
or even utilize some relational semantics,
|
20
23
|
but have no real need for persistence?
|
21
24
|
|
22
|
-
PassiveRecord may be right for you
|
25
|
+
PassiveRecord may be right for you!
|
23
26
|
|
24
27
|
|
25
28
|
## Features
|
@@ -62,11 +65,14 @@ PassiveRecord may be right for you.
|
|
62
65
|
# inverse relationships
|
63
66
|
dog.child # ===> dog
|
64
67
|
|
65
|
-
|
68
|
+
Dog.find_by(child: child) # ===> dog
|
66
69
|
|
67
70
|
# has many thru
|
68
71
|
parent.dogs # ==> [dog]
|
69
72
|
|
73
|
+
# nested queries
|
74
|
+
Dog.find_all_by(child: { parent: parent }) # => [dog]
|
75
|
+
|
70
76
|
## Requirements
|
71
77
|
|
72
78
|
## Install
|
data/lib/passive_record.rb
CHANGED
@@ -5,24 +5,35 @@ require 'passive_record/version'
|
|
5
5
|
require 'passive_record/core/identifier'
|
6
6
|
require 'passive_record/core/query'
|
7
7
|
|
8
|
+
require 'passive_record/class_inheritable_attrs'
|
9
|
+
|
8
10
|
require 'passive_record/associations'
|
9
11
|
require 'passive_record/hooks'
|
10
12
|
|
11
13
|
module PassiveRecord
|
12
14
|
def self.included(base)
|
13
15
|
base.send :include, InstanceMethods
|
16
|
+
base.send :include, ClassLevelInheritableAttributes
|
17
|
+
|
18
|
+
base.class_eval do
|
19
|
+
inheritable_attrs :hooks, :associations
|
20
|
+
end
|
21
|
+
|
14
22
|
base.extend(ClassMethods)
|
15
23
|
end
|
16
24
|
|
17
25
|
module InstanceMethods
|
18
|
-
def
|
26
|
+
def inspect
|
27
|
+
self.class.name + "(#{id.inspect})"
|
28
|
+
end
|
29
|
+
def relata
|
19
30
|
@relata ||= self.class.associations.map do |assn|
|
20
31
|
assn.to_relation(self)
|
21
32
|
end
|
22
33
|
end
|
23
34
|
|
24
|
-
def
|
25
|
-
|
35
|
+
def find_relation_by_target_name_symbol(meth)
|
36
|
+
relata.detect do |relation| # matching relation...
|
26
37
|
meth == relation.association.target_name_symbol ||
|
27
38
|
meth.to_s == relation.association.target_name_symbol.to_s + "=" ||
|
28
39
|
meth.to_s == relation.association.target_name_symbol.to_s + "_id" ||
|
@@ -30,9 +41,21 @@ module PassiveRecord
|
|
30
41
|
meth.to_s == "create_" + relation.association.target_name_symbol.to_s ||
|
31
42
|
meth.to_s == "create_" + (relation.association.target_name_symbol.to_s).singularize
|
32
43
|
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def respond_to?(meth,*args,&blk)
|
47
|
+
if find_relation_by_target_name_symbol(meth)
|
48
|
+
true
|
49
|
+
else
|
50
|
+
super(meth,*args,&blk)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def method_missing(meth, *args, &blk)
|
55
|
+
matching_relation = find_relation_by_target_name_symbol(meth)
|
33
56
|
|
34
57
|
if matching_relation
|
35
|
-
if meth.to_s.
|
58
|
+
if meth.to_s == matching_relation.association.target_name_symbol.to_s + "_id"
|
36
59
|
matching_relation.parent_model_id
|
37
60
|
elsif meth.to_s.end_with?("_id=")
|
38
61
|
matching_relation.parent_model_id = args.first
|
@@ -55,6 +78,7 @@ module PassiveRecord
|
|
55
78
|
include PassiveRecord::Associations
|
56
79
|
include PassiveRecord::Hooks
|
57
80
|
|
81
|
+
|
58
82
|
include Enumerable
|
59
83
|
extend Forwardable
|
60
84
|
|
@@ -63,6 +87,10 @@ module PassiveRecord
|
|
63
87
|
end
|
64
88
|
def_delegators :all, :each
|
65
89
|
|
90
|
+
def last
|
91
|
+
all.last
|
92
|
+
end
|
93
|
+
|
66
94
|
def find_by(conditions)
|
67
95
|
if conditions.is_a?(Identifier)
|
68
96
|
find_by_id(conditions)
|
@@ -73,6 +101,14 @@ module PassiveRecord
|
|
73
101
|
end
|
74
102
|
end
|
75
103
|
|
104
|
+
def find_all_by(conditions)
|
105
|
+
if conditions.is_a?(Array) && conditions.all? { |c| c.is_a?(Identifier) }
|
106
|
+
find_by_ids(conditions)
|
107
|
+
else
|
108
|
+
where(conditions).all
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
76
112
|
def where(conditions)
|
77
113
|
Query.new(self, conditions)
|
78
114
|
end
|
@@ -82,6 +118,7 @@ module PassiveRecord
|
|
82
118
|
|
83
119
|
instance.singleton_class.class_eval { attr_accessor :id }
|
84
120
|
instance.send(:"id=", Identifier.generate)
|
121
|
+
|
85
122
|
register(instance)
|
86
123
|
|
87
124
|
attrs.each do |(k,v)|
|
@@ -104,6 +141,7 @@ module PassiveRecord
|
|
104
141
|
instances_by_id.select { |id,_| ids.include?(id) }.values
|
105
142
|
end
|
106
143
|
|
144
|
+
|
107
145
|
private
|
108
146
|
def instances_by_id
|
109
147
|
@instances ||= {}
|
@@ -5,20 +5,21 @@ require 'passive_record/associations/has_many_through'
|
|
5
5
|
|
6
6
|
module PassiveRecord
|
7
7
|
module Associations
|
8
|
-
def
|
8
|
+
def associate!(assn)
|
9
9
|
@associations ||= []
|
10
|
+
@associations += [assn]
|
10
11
|
end
|
11
12
|
|
12
13
|
def belongs_to(parent_name_sym, opts={})
|
13
14
|
target_class_name = opts.delete(:class_name) { (parent_name_sym.to_s).split('_').map(&:capitalize).join }
|
14
15
|
association = BelongsToAssociation.new(self, target_class_name, parent_name_sym)
|
15
|
-
|
16
|
+
associate!(association)
|
16
17
|
end
|
17
18
|
|
18
19
|
def has_one(child_name_sym)
|
19
20
|
child_class_name = (child_name_sym.to_s).split('_').map(&:capitalize).join
|
20
21
|
association = HasOneAssociation.new(self, child_class_name, child_name_sym)
|
21
|
-
|
22
|
+
associate!(association)
|
22
23
|
end
|
23
24
|
|
24
25
|
def has_many(collection_name_sym, opts={})
|
@@ -32,10 +33,10 @@ module PassiveRecord
|
|
32
33
|
|
33
34
|
association = HasManyThroughAssociation.new(self, target_class_name, collection_name_sym, through_class_collection_name, base_association)
|
34
35
|
|
35
|
-
|
36
|
-
else
|
36
|
+
associate!(association)
|
37
|
+
else
|
37
38
|
association = HasManyAssociation.new(self, target_class_name, collection_name_sym)
|
38
|
-
|
39
|
+
associate!(association)
|
39
40
|
end
|
40
41
|
end
|
41
42
|
end
|
@@ -8,10 +8,22 @@ module PassiveRecord
|
|
8
8
|
|
9
9
|
class HasManyThroughRelation < HasManyRelation
|
10
10
|
def lookup
|
11
|
-
association.base_association.
|
11
|
+
intermediate_results = association.base_association.
|
12
12
|
to_relation(parent_model).
|
13
|
-
lookup
|
14
|
-
|
13
|
+
lookup
|
14
|
+
|
15
|
+
singular_target_sym = association.target_name_symbol.to_s.singularize.to_sym
|
16
|
+
plural_target_sym = association.target_name_symbol.to_s.pluralize.to_sym
|
17
|
+
|
18
|
+
if !intermediate_results.empty?
|
19
|
+
if intermediate_results.first.respond_to?(singular_target_sym)
|
20
|
+
intermediate_results.flat_map(&singular_target_sym)
|
21
|
+
elsif intermediate_results.first.respond_to?(plural_target_sym)
|
22
|
+
intermediate_results.flat_map(&plural_target_sym)
|
23
|
+
end
|
24
|
+
else
|
25
|
+
[]
|
26
|
+
end
|
15
27
|
end
|
16
28
|
|
17
29
|
def create(attrs={})
|
@@ -16,10 +16,12 @@ module PassiveRecord
|
|
16
16
|
child_class.find_by(parent_model_id_field => parent_model.id)
|
17
17
|
end
|
18
18
|
|
19
|
-
def create(
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
def create(attrs={})
|
20
|
+
child_class.create(
|
21
|
+
attrs.merge(
|
22
|
+
parent_model_id_field => parent_model.id
|
23
|
+
)
|
24
|
+
)
|
23
25
|
end
|
24
26
|
|
25
27
|
def parent_model_id_field
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# taken directly from http://stackoverflow.com/a/10729812/90042
|
2
|
+
module ClassLevelInheritableAttributes
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def inheritable_attrs(*args)
|
9
|
+
@inheritable_attributes ||= [:inheritable_attributes]
|
10
|
+
@inheritable_attributes += args
|
11
|
+
args.each do |arg|
|
12
|
+
class_eval %(
|
13
|
+
class << self; attr_accessor :#{arg} end
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
# binding.pry
|
18
|
+
@inheritable_attributes
|
19
|
+
end
|
20
|
+
|
21
|
+
def inherited(subclass)
|
22
|
+
@inheritable_attributes.each do |inheritable_attribute|
|
23
|
+
instance_var = "@#{inheritable_attribute}"
|
24
|
+
subclass.instance_variable_set(instance_var, instance_variable_get(instance_var))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -2,9 +2,25 @@ module PassiveRecord
|
|
2
2
|
module Core
|
3
3
|
class Query < Struct.new(:klass, :conditions)
|
4
4
|
def all
|
5
|
+
return [] unless conditions
|
5
6
|
klass.all.select do |instance|
|
6
7
|
conditions.all? do |(field,value)|
|
7
|
-
|
8
|
+
if value.is_a?(Hash)
|
9
|
+
assn = instance.send(field)
|
10
|
+
matched = assn && value.all? do |(assn_field,val)|
|
11
|
+
if assn.is_a?(Array)
|
12
|
+
assn.any? do |assc_model|
|
13
|
+
assc_model.send(assn_field) == val
|
14
|
+
end
|
15
|
+
else
|
16
|
+
assn.send(assn_field) == val
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
matched
|
21
|
+
else
|
22
|
+
instance.send(field) == value
|
23
|
+
end
|
8
24
|
end
|
9
25
|
end
|
10
26
|
end
|
data/lib/passive_record/hooks.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
module PassiveRecord
|
2
2
|
module Hooks
|
3
3
|
class Hook
|
4
|
-
|
4
|
+
attr_reader :kind
|
5
|
+
|
6
|
+
def initialize(kind,*meth_syms,&blk)
|
7
|
+
@kind = kind
|
5
8
|
@methods_to_call = meth_syms
|
6
9
|
@block_to_invoke = blk
|
7
10
|
end
|
@@ -17,21 +20,16 @@ module PassiveRecord
|
|
17
20
|
end
|
18
21
|
end
|
19
22
|
|
20
|
-
def hooks
|
21
|
-
@hooks ||= {}
|
22
|
-
end
|
23
|
-
|
24
|
-
def after_hooks
|
25
|
-
hooks[:after] ||= {}
|
26
|
-
end
|
27
|
-
|
28
23
|
def after_create_hooks
|
29
|
-
|
24
|
+
@hooks ||= []
|
25
|
+
@hooks.select { |hook| hook.kind == :after_create }
|
30
26
|
end
|
31
27
|
|
32
28
|
def after_create(*meth_syms, &blk)
|
33
|
-
hook = Hook.new(
|
34
|
-
|
29
|
+
hook = Hook.new(:after_create,*meth_syms,&blk)
|
30
|
+
@hooks ||= []
|
31
|
+
@hooks += [ hook ]
|
32
|
+
self
|
35
33
|
end
|
36
34
|
end
|
37
35
|
end
|
data/spec/passive_record_spec.rb
CHANGED
@@ -24,10 +24,34 @@ describe Model do
|
|
24
24
|
it 'should be retrievable by query' do
|
25
25
|
expect(SimpleModel.find_by(foo: 'foo_value')).to eq(model)
|
26
26
|
end
|
27
|
+
|
28
|
+
context 'nested queries' do
|
29
|
+
let(:post) { Post.create }
|
30
|
+
let(:user) { User.create }
|
31
|
+
|
32
|
+
subject(:posts_with_comments_by_user) do
|
33
|
+
Post.find_by comments: { user: user }
|
34
|
+
end
|
35
|
+
|
36
|
+
before do
|
37
|
+
post.create_comment(user: user)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should find a single record through a nested query' do
|
41
|
+
post = Post.find_by comments: { user: user }
|
42
|
+
expect(post).to eq(post)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should find multiple records through a nested query' do
|
46
|
+
another_post = Post.create
|
47
|
+
another_post.create_comment(user: user)
|
48
|
+
|
49
|
+
posts = Post.find_all_by comments: { user: user }
|
50
|
+
expect(posts).to eq([post,another_post])
|
51
|
+
end
|
52
|
+
end
|
27
53
|
end
|
28
54
|
end
|
29
|
-
|
30
|
-
xcontext 'querying by associations'
|
31
55
|
end
|
32
56
|
|
33
57
|
context 'hooks' do
|
@@ -37,6 +61,10 @@ describe Model do
|
|
37
61
|
end
|
38
62
|
|
39
63
|
it 'should use a block' do
|
64
|
+
expect(Dog.create.sound).to eq("bark")
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should use an inherited block' do
|
40
68
|
expect(Parent.create.created_at).to be_a(Time)
|
41
69
|
end
|
42
70
|
end
|
@@ -49,7 +77,7 @@ describe Model do
|
|
49
77
|
|
50
78
|
it 'should create children' do
|
51
79
|
expect { child.create_dog }.to change { Dog.count }.by(1)
|
52
|
-
expect(child.
|
80
|
+
expect(child.dogs.first).to eq(Dog.last)
|
53
81
|
end
|
54
82
|
|
55
83
|
it 'should have inverse relationships' do
|
@@ -83,12 +111,18 @@ describe Model do
|
|
83
111
|
context 'one-to-many through relationships' do
|
84
112
|
let(:parent) { Parent.create }
|
85
113
|
let(:child) { parent.create_child }
|
86
|
-
subject(:dogs) { parent.dogs }
|
87
114
|
|
88
115
|
it 'should collect children of children' do
|
89
116
|
child.create_dog
|
90
|
-
expect(dogs).to all(be_a(Dog))
|
91
|
-
expect(dogs.
|
117
|
+
expect(parent.dogs).to all(be_a(Dog))
|
118
|
+
expect(parent.dogs.count).to eq(1)
|
119
|
+
expect(parent.dogs.first).to eq(child.dogs.first)
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'should do the nested query example from the readme' do
|
123
|
+
child.create_dog
|
124
|
+
expect(Dog.find_all_by(child: {parent: parent})).
|
125
|
+
to eq(parent.dogs)
|
92
126
|
end
|
93
127
|
end
|
94
128
|
|
data/spec/spec_helper.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
|
+
require "codeclimate-test-reporter"
|
2
|
+
CodeClimate::TestReporter.start
|
3
|
+
|
1
4
|
require 'rspec'
|
2
5
|
require 'pry'
|
3
6
|
require 'passive_record'
|
4
7
|
|
5
8
|
class Model
|
6
9
|
include PassiveRecord
|
10
|
+
attr_reader :created_at
|
11
|
+
after_create { @created_at = Time.now }
|
7
12
|
end
|
8
13
|
|
9
14
|
class SimpleModel < Struct.new(:foo)
|
@@ -11,25 +16,24 @@ class SimpleModel < Struct.new(:foo)
|
|
11
16
|
end
|
12
17
|
|
13
18
|
class Dog < Model
|
19
|
+
attr_reader :sound
|
14
20
|
belongs_to :child
|
21
|
+
after_create {@sound = 'bark'}
|
15
22
|
end
|
16
23
|
|
17
24
|
class Child < Model
|
18
|
-
|
25
|
+
has_many :dogs
|
19
26
|
belongs_to :parent
|
20
27
|
|
28
|
+
attr_reader :name
|
21
29
|
after_create :give_name
|
22
30
|
|
23
|
-
attr_reader :name
|
24
31
|
def give_name; @name = "Alice" end
|
25
32
|
end
|
26
33
|
|
27
34
|
class Parent < Model
|
28
35
|
has_many :children
|
29
36
|
has_many :dogs, :through => :children
|
30
|
-
|
31
|
-
attr_reader :created_at
|
32
|
-
after_create { @created_at = Time.now }
|
33
37
|
end
|
34
38
|
|
35
39
|
###
|
@@ -62,3 +66,18 @@ class User < Model
|
|
62
66
|
has_many :friendships
|
63
67
|
has_many :friends, :through => :friendships
|
64
68
|
end
|
69
|
+
|
70
|
+
###
|
71
|
+
|
72
|
+
class Post < Model
|
73
|
+
has_many :comments
|
74
|
+
end
|
75
|
+
|
76
|
+
class User < Model
|
77
|
+
has_many :comments
|
78
|
+
end
|
79
|
+
|
80
|
+
class Comment < Model
|
81
|
+
belongs_to :post
|
82
|
+
belongs_to :user
|
83
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: passive_record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joseph Weissman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-02-
|
11
|
+
date: 2016-02-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -139,10 +139,10 @@ files:
|
|
139
139
|
- lib/passive_record/associations/has_many.rb
|
140
140
|
- lib/passive_record/associations/has_many_through.rb
|
141
141
|
- lib/passive_record/associations/has_one.rb
|
142
|
+
- lib/passive_record/class_inheritable_attrs.rb
|
142
143
|
- lib/passive_record/core/identifier.rb
|
143
144
|
- lib/passive_record/core/query.rb
|
144
145
|
- lib/passive_record/hooks.rb
|
145
|
-
- lib/passive_record/hstruct.rb
|
146
146
|
- lib/passive_record/version.rb
|
147
147
|
- passive_record.gemspec
|
148
148
|
- spec/passive_record_spec.rb
|
@@ -1,38 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# build a hash-initialized 'record' class
|
3
|
-
#
|
4
|
-
# singleton_class pattern taken from http://stackoverflow.com/questions/15256940/in-ruby-how-do-i-implement-a-class-whose-new-method-creates-subclasses-of-itsel
|
5
|
-
#
|
6
|
-
class HStruct
|
7
|
-
singleton_class.class_eval { alias :old_new :new }
|
8
|
-
|
9
|
-
def initialize(attributes={})
|
10
|
-
attributes.each do |k,v|
|
11
|
-
send("#{k}=",v)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def fetch_values(*args)
|
16
|
-
to_h.fetch_values(*args)
|
17
|
-
end
|
18
|
-
|
19
|
-
def to_h
|
20
|
-
attribute_names.inject({}) do |hsh,k|
|
21
|
-
hsh[k] = send("#{k}"); hsh
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.new(*attribute_names)
|
26
|
-
Class.new(self){
|
27
|
-
singleton_class.class_eval {
|
28
|
-
alias :new :old_new
|
29
|
-
}
|
30
|
-
|
31
|
-
attribute_names.each do |k|
|
32
|
-
attr_accessor k.to_sym
|
33
|
-
end
|
34
|
-
|
35
|
-
define_method(:attribute_names) { attribute_names }
|
36
|
-
}
|
37
|
-
end
|
38
|
-
end
|