acts_as_eav_model 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +1 -0
- data/CHANGELOG +3 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +92 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +117 -0
- data/Rakefile +46 -0
- data/SPECDOC +23 -0
- data/TODO +0 -0
- data/VERSION +1 -0
- data/doc/classes/ActiveRecord/Acts/EavModel.html +213 -0
- data/doc/classes/ActiveRecord/Acts/EavModel/ClassMethods.html +343 -0
- data/doc/classes/ActiveRecord/Acts/EavModel/InstanceMethods.html +474 -0
- data/doc/classes/ActiveRecord/Acts/EavModel/InstanceMethods/ClassMethods.html +192 -0
- data/doc/classes/ActiveRecord/Base.html +170 -0
- data/doc/created.rid +1 -0
- data/doc/files/CHANGELOG.html +113 -0
- data/doc/files/MIT-LICENSE.html +129 -0
- data/doc/files/README_rdoc.html +248 -0
- data/doc/files/SPECDOC.html +170 -0
- data/doc/files/lib/acts_as_eav_model_rb.html +101 -0
- data/doc/fr_class_index.html +31 -0
- data/doc/fr_file_index.html +31 -0
- data/doc/fr_method_index.html +40 -0
- data/doc/index.html +24 -0
- data/doc/rdoc-style.css +208 -0
- data/init.rb +1 -0
- data/install.rb +1 -0
- data/lib/acts_as_eav_model.rb +542 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/document.rb +7 -0
- data/spec/dummy/app/models/person.rb +13 -0
- data/spec/dummy/app/models/person_contact_info.rb +2 -0
- data/spec/dummy/app/models/post.rb +4 -0
- data/spec/dummy/app/models/post_attribute.rb +2 -0
- data/spec/dummy/app/models/preference.rb +5 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +45 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +22 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +26 -0
- data/spec/dummy/config/environments/production.rb +49 -0
- data/spec/dummy/config/environments/test.rb +35 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/schema.rb +51 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/javascripts/application.js +2 -0
- data/spec/dummy/public/javascripts/controls.js +965 -0
- data/spec/dummy/public/javascripts/dragdrop.js +974 -0
- data/spec/dummy/public/javascripts/effects.js +1123 -0
- data/spec/dummy/public/javascripts/prototype.js +6001 -0
- data/spec/dummy/public/javascripts/rails.js +175 -0
- data/spec/dummy/public/stylesheets/.gitkeep +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/fixtures/people.yml +4 -0
- data/spec/fixtures/person_contact_infos.yml +10 -0
- data/spec/fixtures/post_attributes.yml +15 -0
- data/spec/fixtures/posts.yml +9 -0
- data/spec/fixtures/preferences.yml +10 -0
- data/spec/models/eav_model_with_no_arguments_spec.rb +82 -0
- data/spec/models/eav_model_with_options_spec.rb +37 -0
- data/spec/models/eav_validation_spec.rb +11 -0
- data/spec/schema.rb +50 -0
- data/spec/spec_helper.rb +38 -0
- data/uninstall.rb +1 -0
- metadata +213 -0
@@ -0,0 +1,101 @@
|
|
1
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
2
|
+
<!DOCTYPE html
|
3
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
4
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
5
|
+
|
6
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
7
|
+
<head>
|
8
|
+
<title>File: acts_as_eav_model.rb</title>
|
9
|
+
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
10
|
+
<meta http-equiv="Content-Script-Type" content="text/javascript" />
|
11
|
+
<link rel="stylesheet" href="../.././rdoc-style.css" type="text/css" media="screen" />
|
12
|
+
<script type="text/javascript">
|
13
|
+
// <![CDATA[
|
14
|
+
|
15
|
+
function popupCode( url ) {
|
16
|
+
window.open(url, "Code", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
|
17
|
+
}
|
18
|
+
|
19
|
+
function toggleCode( id ) {
|
20
|
+
if ( document.getElementById )
|
21
|
+
elem = document.getElementById( id );
|
22
|
+
else if ( document.all )
|
23
|
+
elem = eval( "document.all." + id );
|
24
|
+
else
|
25
|
+
return false;
|
26
|
+
|
27
|
+
elemStyle = elem.style;
|
28
|
+
|
29
|
+
if ( elemStyle.display != "block" ) {
|
30
|
+
elemStyle.display = "block"
|
31
|
+
} else {
|
32
|
+
elemStyle.display = "none"
|
33
|
+
}
|
34
|
+
|
35
|
+
return true;
|
36
|
+
}
|
37
|
+
|
38
|
+
// Make codeblocks hidden by default
|
39
|
+
document.writeln( "<style type=\"text/css\">div.method-source-code { display: none }</style>" )
|
40
|
+
|
41
|
+
// ]]>
|
42
|
+
</script>
|
43
|
+
|
44
|
+
</head>
|
45
|
+
<body>
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
<div id="fileHeader">
|
50
|
+
<h1>acts_as_eav_model.rb</h1>
|
51
|
+
<table class="header-table">
|
52
|
+
<tr class="top-aligned-row">
|
53
|
+
<td><strong>Path:</strong></td>
|
54
|
+
<td>lib/acts_as_eav_model.rb
|
55
|
+
</td>
|
56
|
+
</tr>
|
57
|
+
<tr class="top-aligned-row">
|
58
|
+
<td><strong>Last Update:</strong></td>
|
59
|
+
<td>Thu Dec 18 13:30:11 +1300 2008</td>
|
60
|
+
</tr>
|
61
|
+
</table>
|
62
|
+
</div>
|
63
|
+
<!-- banner header -->
|
64
|
+
|
65
|
+
<div id="bodyContent">
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
<div id="contextContent">
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
</div>
|
74
|
+
|
75
|
+
|
76
|
+
</div>
|
77
|
+
|
78
|
+
|
79
|
+
<!-- if includes -->
|
80
|
+
|
81
|
+
<div id="section">
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
|
89
|
+
|
90
|
+
<!-- if method_list -->
|
91
|
+
|
92
|
+
|
93
|
+
</div>
|
94
|
+
|
95
|
+
|
96
|
+
<div id="validator-badges">
|
97
|
+
<p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
|
98
|
+
</div>
|
99
|
+
|
100
|
+
</body>
|
101
|
+
</html>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
3
|
+
<!DOCTYPE html
|
4
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
5
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
6
|
+
|
7
|
+
<!--
|
8
|
+
|
9
|
+
Classes
|
10
|
+
|
11
|
+
-->
|
12
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
13
|
+
<head>
|
14
|
+
<title>Classes</title>
|
15
|
+
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
16
|
+
<link rel="stylesheet" href="rdoc-style.css" type="text/css" />
|
17
|
+
<base target="docwin" />
|
18
|
+
</head>
|
19
|
+
<body>
|
20
|
+
<div id="index">
|
21
|
+
<h1 class="section-bar">Classes</h1>
|
22
|
+
<div id="index-entries">
|
23
|
+
<a href="classes/ActiveRecord/Acts/EavModel.html">ActiveRecord::Acts::EavModel</a><br />
|
24
|
+
<a href="classes/ActiveRecord/Acts/EavModel/ClassMethods.html">ActiveRecord::Acts::EavModel::ClassMethods</a><br />
|
25
|
+
<a href="classes/ActiveRecord/Acts/EavModel/InstanceMethods.html">ActiveRecord::Acts::EavModel::InstanceMethods</a><br />
|
26
|
+
<a href="classes/ActiveRecord/Acts/EavModel/InstanceMethods/ClassMethods.html">ActiveRecord::Acts::EavModel::InstanceMethods::ClassMethods</a><br />
|
27
|
+
<a href="classes/ActiveRecord/Base.html">ActiveRecord::Base</a><br />
|
28
|
+
</div>
|
29
|
+
</div>
|
30
|
+
</body>
|
31
|
+
</html>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
3
|
+
<!DOCTYPE html
|
4
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
5
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
6
|
+
|
7
|
+
<!--
|
8
|
+
|
9
|
+
Files
|
10
|
+
|
11
|
+
-->
|
12
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
13
|
+
<head>
|
14
|
+
<title>Files</title>
|
15
|
+
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
16
|
+
<link rel="stylesheet" href="rdoc-style.css" type="text/css" />
|
17
|
+
<base target="docwin" />
|
18
|
+
</head>
|
19
|
+
<body>
|
20
|
+
<div id="index">
|
21
|
+
<h1 class="section-bar">Files</h1>
|
22
|
+
<div id="index-entries">
|
23
|
+
<a href="files/CHANGELOG.html">CHANGELOG</a><br />
|
24
|
+
<a href="files/MIT-LICENSE.html">MIT-LICENSE</a><br />
|
25
|
+
<a href="files/README_rdoc.html">README.rdoc</a><br />
|
26
|
+
<a href="files/SPECDOC.html">SPECDOC</a><br />
|
27
|
+
<a href="files/lib/acts_as_eav_model_rb.html">lib/acts_as_eav_model.rb</a><br />
|
28
|
+
</div>
|
29
|
+
</div>
|
30
|
+
</body>
|
31
|
+
</html>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
3
|
+
<!DOCTYPE html
|
4
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
5
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
6
|
+
|
7
|
+
<!--
|
8
|
+
|
9
|
+
Methods
|
10
|
+
|
11
|
+
-->
|
12
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
13
|
+
<head>
|
14
|
+
<title>Methods</title>
|
15
|
+
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
16
|
+
<link rel="stylesheet" href="rdoc-style.css" type="text/css" />
|
17
|
+
<base target="docwin" />
|
18
|
+
</head>
|
19
|
+
<body>
|
20
|
+
<div id="index">
|
21
|
+
<h1 class="section-bar">Methods</h1>
|
22
|
+
<div id="index-entries">
|
23
|
+
<a href="classes/ActiveRecord/Base.html#M000014">attributes= (ActiveRecord::Base)</a><br />
|
24
|
+
<a href="classes/ActiveRecord/Acts/EavModel/InstanceMethods/ClassMethods.html#M000012">create_attribute_table (ActiveRecord::Acts::EavModel::InstanceMethods::ClassMethods)</a><br />
|
25
|
+
<a href="classes/ActiveRecord/Acts/EavModel/InstanceMethods/ClassMethods.html#M000013">drop_attribute_table (ActiveRecord::Acts::EavModel::InstanceMethods::ClassMethods)</a><br />
|
26
|
+
<a href="classes/ActiveRecord/Acts/EavModel/InstanceMethods.html#M000011">each_eav_relation (ActiveRecord::Acts::EavModel::InstanceMethods)</a><br />
|
27
|
+
<a href="classes/ActiveRecord/Acts/EavModel/InstanceMethods.html#M000003">eav_attributes (ActiveRecord::Acts::EavModel::InstanceMethods)</a><br />
|
28
|
+
<a href="classes/ActiveRecord/Acts/EavModel/InstanceMethods.html#M000009">eav_related (ActiveRecord::Acts::EavModel::InstanceMethods)</a><br />
|
29
|
+
<a href="classes/ActiveRecord/Acts/EavModel/InstanceMethods.html#M000010">exec_if_related (ActiveRecord::Acts::EavModel::InstanceMethods)</a><br />
|
30
|
+
<a href="classes/ActiveRecord/Acts/EavModel/InstanceMethods.html#M000008">find_related_eav_attribute (ActiveRecord::Acts::EavModel::InstanceMethods)</a><br />
|
31
|
+
<a href="classes/ActiveRecord/Acts/EavModel/ClassMethods.html#M000001">has_eav_behavior (ActiveRecord::Acts::EavModel::ClassMethods)</a><br />
|
32
|
+
<a href="classes/ActiveRecord/Acts/EavModel/InstanceMethods.html#M000002">is_eav_attribute? (ActiveRecord::Acts::EavModel::InstanceMethods)</a><br />
|
33
|
+
<a href="classes/ActiveRecord/Acts/EavModel/InstanceMethods.html#M000007">method_missing_with_eav_behavior (ActiveRecord::Acts::EavModel::InstanceMethods)</a><br />
|
34
|
+
<a href="classes/ActiveRecord/Acts/EavModel/InstanceMethods.html#M000005">read_attribute_with_eav_behavior (ActiveRecord::Acts::EavModel::InstanceMethods)</a><br />
|
35
|
+
<a href="classes/ActiveRecord/Acts/EavModel/InstanceMethods.html#M000004">save_modified_eav_attributes (ActiveRecord::Acts::EavModel::InstanceMethods)</a><br />
|
36
|
+
<a href="classes/ActiveRecord/Acts/EavModel/InstanceMethods.html#M000006">write_attribute_with_eav_behavior (ActiveRecord::Acts::EavModel::InstanceMethods)</a><br />
|
37
|
+
</div>
|
38
|
+
</div>
|
39
|
+
</body>
|
40
|
+
</html>
|
data/doc/index.html
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
2
|
+
<!DOCTYPE html
|
3
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
|
4
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
|
5
|
+
|
6
|
+
<!--
|
7
|
+
|
8
|
+
acts_as_eav_model
|
9
|
+
|
10
|
+
-->
|
11
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
12
|
+
<head>
|
13
|
+
<title>acts_as_eav_model</title>
|
14
|
+
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
15
|
+
</head>
|
16
|
+
<frameset rows="20%, 80%">
|
17
|
+
<frameset cols="25%,35%,45%">
|
18
|
+
<frame src="fr_file_index.html" title="Files" name="Files" />
|
19
|
+
<frame src="fr_class_index.html" name="Classes" />
|
20
|
+
<frame src="fr_method_index.html" name="Methods" />
|
21
|
+
</frameset>
|
22
|
+
<frame src="files/README_rdoc.html" name="docwin" />
|
23
|
+
</frameset>
|
24
|
+
</html>
|
data/doc/rdoc-style.css
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
|
2
|
+
body {
|
3
|
+
font-family: Verdana,Arial,Helvetica,sans-serif;
|
4
|
+
font-size: 90%;
|
5
|
+
margin: 0;
|
6
|
+
margin-left: 40px;
|
7
|
+
padding: 0;
|
8
|
+
background: white;
|
9
|
+
}
|
10
|
+
|
11
|
+
h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
|
12
|
+
h1 { font-size: 150%; }
|
13
|
+
h2,h3,h4 { margin-top: 1em; }
|
14
|
+
|
15
|
+
a { background: #eef; color: #039; text-decoration: none; }
|
16
|
+
a:hover { background: #039; color: #eef; }
|
17
|
+
|
18
|
+
/* Override the base stylesheet's Anchor inside a table cell */
|
19
|
+
td > a {
|
20
|
+
background: transparent;
|
21
|
+
color: #039;
|
22
|
+
text-decoration: none;
|
23
|
+
}
|
24
|
+
|
25
|
+
/* and inside a section title */
|
26
|
+
.section-title > a {
|
27
|
+
background: transparent;
|
28
|
+
color: #eee;
|
29
|
+
text-decoration: none;
|
30
|
+
}
|
31
|
+
|
32
|
+
/* === Structural elements =================================== */
|
33
|
+
|
34
|
+
div#index {
|
35
|
+
margin: 0;
|
36
|
+
margin-left: -40px;
|
37
|
+
padding: 0;
|
38
|
+
font-size: 90%;
|
39
|
+
}
|
40
|
+
|
41
|
+
|
42
|
+
div#index a {
|
43
|
+
margin-left: 0.7em;
|
44
|
+
}
|
45
|
+
|
46
|
+
div#index .section-bar {
|
47
|
+
margin-left: 0px;
|
48
|
+
padding-left: 0.7em;
|
49
|
+
background: #ccc;
|
50
|
+
font-size: small;
|
51
|
+
}
|
52
|
+
|
53
|
+
|
54
|
+
div#classHeader, div#fileHeader {
|
55
|
+
width: auto;
|
56
|
+
color: white;
|
57
|
+
padding: 0.5em 1.5em 0.5em 1.5em;
|
58
|
+
margin: 0;
|
59
|
+
margin-left: -40px;
|
60
|
+
border-bottom: 3px solid #006;
|
61
|
+
}
|
62
|
+
|
63
|
+
div#classHeader a, div#fileHeader a {
|
64
|
+
background: inherit;
|
65
|
+
color: white;
|
66
|
+
}
|
67
|
+
|
68
|
+
div#classHeader td, div#fileHeader td {
|
69
|
+
background: inherit;
|
70
|
+
color: white;
|
71
|
+
}
|
72
|
+
|
73
|
+
|
74
|
+
div#fileHeader {
|
75
|
+
background: #057;
|
76
|
+
}
|
77
|
+
|
78
|
+
div#classHeader {
|
79
|
+
background: #048;
|
80
|
+
}
|
81
|
+
|
82
|
+
|
83
|
+
.class-name-in-header {
|
84
|
+
font-size: 180%;
|
85
|
+
font-weight: bold;
|
86
|
+
}
|
87
|
+
|
88
|
+
|
89
|
+
div#bodyContent {
|
90
|
+
padding: 0 1.5em 0 1.5em;
|
91
|
+
}
|
92
|
+
|
93
|
+
div#description {
|
94
|
+
padding: 0.5em 1.5em;
|
95
|
+
background: #efefef;
|
96
|
+
border: 1px dotted #999;
|
97
|
+
}
|
98
|
+
|
99
|
+
div#description h1,h2,h3,h4,h5,h6 {
|
100
|
+
color: #125;;
|
101
|
+
background: transparent;
|
102
|
+
}
|
103
|
+
|
104
|
+
div#validator-badges {
|
105
|
+
text-align: center;
|
106
|
+
}
|
107
|
+
div#validator-badges img { border: 0; }
|
108
|
+
|
109
|
+
div#copyright {
|
110
|
+
color: #333;
|
111
|
+
background: #efefef;
|
112
|
+
font: 0.75em sans-serif;
|
113
|
+
margin-top: 5em;
|
114
|
+
margin-bottom: 0;
|
115
|
+
padding: 0.5em 2em;
|
116
|
+
}
|
117
|
+
|
118
|
+
|
119
|
+
/* === Classes =================================== */
|
120
|
+
|
121
|
+
table.header-table {
|
122
|
+
color: white;
|
123
|
+
font-size: small;
|
124
|
+
}
|
125
|
+
|
126
|
+
.type-note {
|
127
|
+
font-size: small;
|
128
|
+
color: #DEDEDE;
|
129
|
+
}
|
130
|
+
|
131
|
+
.xxsection-bar {
|
132
|
+
background: #eee;
|
133
|
+
color: #333;
|
134
|
+
padding: 3px;
|
135
|
+
}
|
136
|
+
|
137
|
+
.section-bar {
|
138
|
+
color: #333;
|
139
|
+
border-bottom: 1px solid #999;
|
140
|
+
margin-left: -20px;
|
141
|
+
}
|
142
|
+
|
143
|
+
|
144
|
+
.section-title {
|
145
|
+
background: #79a;
|
146
|
+
color: #eee;
|
147
|
+
padding: 3px;
|
148
|
+
margin-top: 2em;
|
149
|
+
margin-left: -30px;
|
150
|
+
border: 1px solid #999;
|
151
|
+
}
|
152
|
+
|
153
|
+
.top-aligned-row { vertical-align: top }
|
154
|
+
.bottom-aligned-row { vertical-align: bottom }
|
155
|
+
|
156
|
+
/* --- Context section classes ----------------------- */
|
157
|
+
|
158
|
+
.context-row { }
|
159
|
+
.context-item-name { font-family: monospace; font-weight: bold; color: black; }
|
160
|
+
.context-item-value { font-size: small; color: #448; }
|
161
|
+
.context-item-desc { color: #333; padding-left: 2em; }
|
162
|
+
|
163
|
+
/* --- Method classes -------------------------- */
|
164
|
+
.method-detail {
|
165
|
+
background: #efefef;
|
166
|
+
padding: 0;
|
167
|
+
margin-top: 0.5em;
|
168
|
+
margin-bottom: 1em;
|
169
|
+
border: 1px dotted #ccc;
|
170
|
+
}
|
171
|
+
.method-heading {
|
172
|
+
color: black;
|
173
|
+
background: #ccc;
|
174
|
+
border-bottom: 1px solid #666;
|
175
|
+
padding: 0.2em 0.5em 0 0.5em;
|
176
|
+
}
|
177
|
+
.method-signature { color: black; background: inherit; }
|
178
|
+
.method-name { font-weight: bold; }
|
179
|
+
.method-args { font-style: italic; }
|
180
|
+
.method-description { padding: 0 0.5em 0 0.5em; }
|
181
|
+
|
182
|
+
/* --- Source code sections -------------------- */
|
183
|
+
|
184
|
+
a.source-toggle { font-size: 90%; }
|
185
|
+
div.method-source-code {
|
186
|
+
background: #262626;
|
187
|
+
color: #ffdead;
|
188
|
+
margin: 1em;
|
189
|
+
padding: 0.5em;
|
190
|
+
border: 1px dashed #999;
|
191
|
+
overflow: hidden;
|
192
|
+
}
|
193
|
+
|
194
|
+
div.method-source-code pre { color: #ffdead; overflow: hidden; }
|
195
|
+
|
196
|
+
/* --- Ruby keyword styles --------------------- */
|
197
|
+
|
198
|
+
.standalone-code { background: #221111; color: #ffdead; overflow: hidden; }
|
199
|
+
|
200
|
+
.ruby-constant { color: #7fffd4; background: transparent; }
|
201
|
+
.ruby-keyword { color: #00ffff; background: transparent; }
|
202
|
+
.ruby-ivar { color: #eedd82; background: transparent; }
|
203
|
+
.ruby-operator { color: #00ffee; background: transparent; }
|
204
|
+
.ruby-identifier { color: #ffdead; background: transparent; }
|
205
|
+
.ruby-node { color: #ffa07a; background: transparent; }
|
206
|
+
.ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
|
207
|
+
.ruby-regexp { color: #ffa07a; background: transparent; }
|
208
|
+
.ruby-value { color: #7fffd4; background: transparent; }
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'acts_as_eav_model'
|
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Install hook code here
|
@@ -0,0 +1,542 @@
|
|
1
|
+
module ActiveRecord # :nodoc:
|
2
|
+
module Acts # :nodoc:
|
3
|
+
##
|
4
|
+
# ActsAsEavModel allow for the Entity-attribute-value model (EAV), also
|
5
|
+
# known as object-attribute-value model and open schema on any of your ActiveRecord
|
6
|
+
# models.
|
7
|
+
#
|
8
|
+
# = What is Entity-attribute-value model?
|
9
|
+
# Entity-attribute-value model (EAV) is a data model that is used in circumstances
|
10
|
+
# where the number of attributes (properties, parameters) that can be used to describe
|
11
|
+
# a thing (an "entity" or "object") is potentially very vast, but the number that will
|
12
|
+
# actually apply to a given entity is relatively modest.
|
13
|
+
#
|
14
|
+
# = Typical Problem
|
15
|
+
# A good example of this is where you need to store
|
16
|
+
# lots (possible hundreds) of optional attributes on an object. My typical
|
17
|
+
# reference example is when you have a User object. You want to store the
|
18
|
+
# user's preferences between sessions. Every search, sort, etc in your
|
19
|
+
# application you want to keep track of so when the user visits that section
|
20
|
+
# of the application again you can simply restore the display to how it was.
|
21
|
+
#
|
22
|
+
# So your controller might have:
|
23
|
+
#
|
24
|
+
# Project.find :all, :conditions => current_user.project_search,
|
25
|
+
# :order => current_user.project_order
|
26
|
+
#
|
27
|
+
# But there could be hundreds of these little attributes that you really don't
|
28
|
+
# want to store directly on the user object. It would make your table have too
|
29
|
+
# many columns so it would be too much of a pain to deal with. Also there might
|
30
|
+
# be performance problems. So instead you might do something like
|
31
|
+
# this:
|
32
|
+
#
|
33
|
+
# class User < ActiveRecord::Base
|
34
|
+
# has_many :preferences
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# class Preferences < ActiveRecord::Base
|
38
|
+
# belongs_to :user
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# Now simply give the Preference model a "name" and "value" column and you are
|
42
|
+
# set..... except this is now too complicated. To retrieve a attribute you will
|
43
|
+
# need to do something like:
|
44
|
+
#
|
45
|
+
# Project.find :all,
|
46
|
+
# :conditions => current_user.preferences.find_by_name('project_search').value,
|
47
|
+
# :order => current_user.preferences.find_by_name('project_order').value
|
48
|
+
#
|
49
|
+
# Sure you could fix this through a few methods on your model. But what about
|
50
|
+
# saving?
|
51
|
+
#
|
52
|
+
# current_user.preferences.create :name => 'project_search',
|
53
|
+
# :value => "lastname LIKE 'jones%'"
|
54
|
+
# current_user.preferences.create :name => 'project_order',
|
55
|
+
# :value => "name"
|
56
|
+
#
|
57
|
+
# Again this seems to much. Again we could add some methods to our model to
|
58
|
+
# make this simpler but do we want to do this on every model. NO! So instead
|
59
|
+
# we use this plugin which does everything for us.
|
60
|
+
#
|
61
|
+
# = Capabilities
|
62
|
+
#
|
63
|
+
# The ActsAsEavModel plugin is capable of modeling this problem in a intuitive
|
64
|
+
# way. Instead of having to deal with a related model you treat all attributes
|
65
|
+
# (both on the model and related) as if they are all on the model. The plugin
|
66
|
+
# will try to save all attributes to the model (normal ActiveRecord behaviour)
|
67
|
+
# but if there is no column for an attribute it will try to save it to a
|
68
|
+
# related model whose purpose is to store these many sparsly populated
|
69
|
+
# attributes.
|
70
|
+
#
|
71
|
+
# The main design goals are:
|
72
|
+
#
|
73
|
+
# * Have the eav attributes feel like normal attributes. Simple gets and sets
|
74
|
+
# will add and remove records from the related model.
|
75
|
+
# * Allow for more than one related model. So for example on my User model I might
|
76
|
+
# have some eav behavior going into a contact_info table while others are
|
77
|
+
# going in a user_preferences table.
|
78
|
+
# * Allow a model to determine what a valid eav attribute is for a given
|
79
|
+
# related model so our model still can generate a NoMethodError.
|
80
|
+
#
|
81
|
+
module EavModel
|
82
|
+
|
83
|
+
MAGIC_FIELD_NAMES = [:created_at, :created_on, :updated_at, :updated_on, :created_by, :updated_by,
|
84
|
+
:lock_version, :type, :id, :position, :parent_id, :lft, :rgt, :quote_value, :template, :to_ary,
|
85
|
+
:marshal_dump, :marshal_load, :_dump, :_load, :to_yaml_type, :to_yaml, :yaml_initialize, :to_xml, :to_json, :as_json,
|
86
|
+
:from_json, :from_xml,:validate,:validate_on_create,:validate_on_update].freeze
|
87
|
+
|
88
|
+
def self.included(base) # :nodoc:
|
89
|
+
base.extend ClassMethods
|
90
|
+
end
|
91
|
+
|
92
|
+
module ClassMethods
|
93
|
+
|
94
|
+
##
|
95
|
+
# Will make the current class have eav behaviour.
|
96
|
+
#
|
97
|
+
# class Post < ActiveRecord::Base
|
98
|
+
# has_eav_behavior
|
99
|
+
# end
|
100
|
+
# post = Post.find_by_title 'hello world'
|
101
|
+
# puts "My post intro is: #{post.intro}"
|
102
|
+
# post.teaser = 'An awesome introduction to the blog'
|
103
|
+
# post.save
|
104
|
+
#
|
105
|
+
# The above example should work even though "intro" and "teaser" are not
|
106
|
+
# attributes on the Post model.
|
107
|
+
#
|
108
|
+
# The following options are available on for has_eav_behavior to modify
|
109
|
+
# the behavior. Reasonable defaults are provided:
|
110
|
+
#
|
111
|
+
# * <tt>class_name</tt>:
|
112
|
+
# The class for the related model. This defaults to the
|
113
|
+
# model name prepended to "Attribute". So for a "User" model the class
|
114
|
+
# name would be "UserAttribute". The class can actually exist (in that
|
115
|
+
# case the model file will be loaded through Rails dependency system) or
|
116
|
+
# if it does not exist a basic model will be dynamically defined for you.
|
117
|
+
# This allows you to implement custom methods on the related class by
|
118
|
+
# simply defining the class manually.
|
119
|
+
# * <tt>table_name</tt>:
|
120
|
+
# The table for the related model. This defaults to the
|
121
|
+
# attribute model's table name.
|
122
|
+
# * <tt>relationship_name</tt>:
|
123
|
+
# This is the name of the actual has_many
|
124
|
+
# relationship. Most of the type this relationship will only be used
|
125
|
+
# indirectly but it is there if the user wants more raw access. This
|
126
|
+
# defaults to the class name underscored then pluralized finally turned
|
127
|
+
# into a symbol.
|
128
|
+
# * <tt>foreign_key</tt>:
|
129
|
+
# The key in the attribute table to relate back to the
|
130
|
+
# model. This defaults to the model name underscored prepended to "_id"
|
131
|
+
# * <tt>name_field</tt>:
|
132
|
+
# The field which stores the name of the attribute in the related object
|
133
|
+
# * <tt>value_field</tt>:
|
134
|
+
# The field that stores the value in the related object
|
135
|
+
# * <tt>fields</tt>:
|
136
|
+
# A list of fields that are valid eav attributes. By default
|
137
|
+
# this is "nil" which means that all field are valid. Use this option if
|
138
|
+
# you want some fields to go to one flex attribute model while other
|
139
|
+
# fields will go to another. As an alternative you can override the
|
140
|
+
# #eav_attributes method which will return a list of all valid flex
|
141
|
+
# attributes. This is useful if you want to read the list of attributes
|
142
|
+
# from another source to keep your code DRY. This method is given a
|
143
|
+
# single argument which is the class for the related model. The following
|
144
|
+
# provide an example:
|
145
|
+
#
|
146
|
+
# class User < ActiveRecord::Base
|
147
|
+
# has_eav_behavior :class_name => 'UserContactInfo'
|
148
|
+
# has_eav_behavior :class_name => 'Preferences'
|
149
|
+
#
|
150
|
+
# def eav_attributes(model)
|
151
|
+
# case model
|
152
|
+
# when UserContactInfo
|
153
|
+
# %w(email phone aim yahoo msn)
|
154
|
+
# when Preference
|
155
|
+
# %w(project_search project_order user_search user_order)
|
156
|
+
# else Array.new
|
157
|
+
# end
|
158
|
+
# end
|
159
|
+
# end
|
160
|
+
#
|
161
|
+
# marcus = User.find_by_login 'marcus'
|
162
|
+
# marcus.email = 'marcus@example.com' # Will save to UserContactInfo model
|
163
|
+
# marcus.project_order = 'name' # Will save to Preference
|
164
|
+
# marcus.save # Carries out save so now values are in database
|
165
|
+
#
|
166
|
+
# Note the else clause in our case statement. Since an empty array is
|
167
|
+
# returned for all other models (perhaps added later) then we can be
|
168
|
+
# certain that only the above eav attributes are allowed.
|
169
|
+
#
|
170
|
+
# If both a :fields option and #eav_attributes method is defined the
|
171
|
+
# <tt>fields</tt> option take precidence. This allows you to easily define the
|
172
|
+
# field list inline for one model while implementing #eav_attributes
|
173
|
+
# for another model and not having #eav_attributes need to determine
|
174
|
+
# what model it is answering for. In both cases the list of flex
|
175
|
+
# attributes can be a list of string or symbols
|
176
|
+
#
|
177
|
+
# A final alternative to :fields and #eav_attributes is the
|
178
|
+
# #is_eav_attribute? method. This method is given two arguments. The
|
179
|
+
# first is the attribute being retrieved/saved the second is the Model we
|
180
|
+
# are testing for. If you override this method then the #eav_attributes
|
181
|
+
# method or the :fields option will have no affect. Use of this method
|
182
|
+
# is ideal when you want to retrict the attributes but do so in a
|
183
|
+
# algorithmic way. The following is an example:
|
184
|
+
# class User < ActiveRecord::Base
|
185
|
+
# has_eav_behavior :class_name => 'UserContactInfo'
|
186
|
+
# has_eav_behavior :class_name => 'Preferences'
|
187
|
+
#
|
188
|
+
# def is_eav_attribute?(attr, model)
|
189
|
+
# case attr.to_s
|
190
|
+
# when /^contact_/ then true
|
191
|
+
# when /^preference_/ then true
|
192
|
+
# else
|
193
|
+
# false
|
194
|
+
# end
|
195
|
+
# end
|
196
|
+
# end
|
197
|
+
#
|
198
|
+
# marcus = User.find_by_login 'marcus'
|
199
|
+
# marcus.contact_phone = '021 654 9876'
|
200
|
+
# marcus.contact_email = 'marcus@example.com'
|
201
|
+
# marcus.preference_project_order = 'name'
|
202
|
+
# marcus.some_attribute = 'blah' # If some_attribute is not defined on
|
203
|
+
# # the model then method not found is thrown
|
204
|
+
#
|
205
|
+
def has_eav_behavior(options = {})
|
206
|
+
Rails.logger.debug("HERE OPTIONS=#{options.inspect}")
|
207
|
+
# Provide default options
|
208
|
+
options[:class_name] ||= self.name + 'Attribute'
|
209
|
+
options[:table_name] ||= options[:class_name].tableize
|
210
|
+
options[:relationship_name] ||= options[:class_name].tableize.to_sym
|
211
|
+
options[:foreign_key] ||= "#{self.table_name.singularize.downcase}_id" #self.name.foreign_key
|
212
|
+
options[:base_foreign_key] ||= self.name.underscore.foreign_key
|
213
|
+
options[:name_field] ||= 'name'
|
214
|
+
options[:value_field] ||= 'value'
|
215
|
+
options[:fields].collect! {|f| f.to_s} unless options[:fields].nil?
|
216
|
+
options[:parent] = self.name
|
217
|
+
|
218
|
+
# Init option storage if necessary
|
219
|
+
cattr_accessor :eav_options
|
220
|
+
self.eav_options ||= Hash.new
|
221
|
+
|
222
|
+
# Return if already processed.
|
223
|
+
return if self.eav_options.keys.include? options[:class_name]
|
224
|
+
|
225
|
+
# Attempt to load related class. If not create it
|
226
|
+
begin
|
227
|
+
options[:class_name].constantize
|
228
|
+
rescue
|
229
|
+
Object.const_set(options[:class_name],
|
230
|
+
Class.new(ActiveRecord::Base)).class_eval do
|
231
|
+
def self.reloadable? #:nodoc:
|
232
|
+
false
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Store options
|
238
|
+
self.eav_options[options[:class_name]] = options
|
239
|
+
|
240
|
+
# Only mix instance methods once
|
241
|
+
unless self.included_modules.include?(ActiveRecord::Acts::EavModel::InstanceMethods)
|
242
|
+
send :include, ActiveRecord::Acts::EavModel::InstanceMethods
|
243
|
+
end
|
244
|
+
|
245
|
+
# Modify attribute class
|
246
|
+
attribute_class = options[:class_name].constantize
|
247
|
+
base_class = self.name.underscore.to_sym
|
248
|
+
|
249
|
+
attribute_class.class_eval do
|
250
|
+
belongs_to base_class, :foreign_key => options[:base_foreign_key]
|
251
|
+
alias_method :base, base_class # For generic access
|
252
|
+
end
|
253
|
+
|
254
|
+
# Modify main class
|
255
|
+
class_eval do
|
256
|
+
has_many options[:relationship_name],
|
257
|
+
:class_name => options[:class_name],
|
258
|
+
:table_name => options[:table_name],
|
259
|
+
:foreign_key => options[:foreign_key],
|
260
|
+
:dependent => :destroy
|
261
|
+
|
262
|
+
# The following is only setup once
|
263
|
+
unless method_defined? :method_missing_without_eav_behavior
|
264
|
+
|
265
|
+
# Carry out delayed actions before save
|
266
|
+
after_validation :save_modified_eav_attributes,:on=>:update
|
267
|
+
|
268
|
+
# Make attributes seem real
|
269
|
+
alias_method_chain :respond_to?, :eav_behavior
|
270
|
+
alias_method_chain :method_missing, :eav_behavior
|
271
|
+
|
272
|
+
private
|
273
|
+
|
274
|
+
alias_method_chain :read_attribute, :eav_behavior
|
275
|
+
alias_method_chain :write_attribute, :eav_behavior
|
276
|
+
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
create_attribute_table
|
281
|
+
|
282
|
+
end
|
283
|
+
|
284
|
+
end
|
285
|
+
|
286
|
+
module InstanceMethods
|
287
|
+
|
288
|
+
def self.included(base) # :nodoc:
|
289
|
+
base.extend ClassMethods
|
290
|
+
end
|
291
|
+
|
292
|
+
module ClassMethods
|
293
|
+
|
294
|
+
##
|
295
|
+
# Rake migration task to create the versioned table using options passed to has_eav_behavior
|
296
|
+
#
|
297
|
+
def create_attribute_table(options = {})
|
298
|
+
eav_options.keys.each do |key|
|
299
|
+
continue if eav_options[key][:parent] != self.name
|
300
|
+
model = eav_options[key][:class_name]
|
301
|
+
|
302
|
+
return if connection.tables.include?(eav_options[model][:table_name])
|
303
|
+
|
304
|
+
self.connection.create_table(eav_options[model][:table_name], options) do |t|
|
305
|
+
t.integer eav_options[model][:foreign_key], :null => false
|
306
|
+
t.string eav_options[model][:name_field], :null => false
|
307
|
+
t.string eav_options[model][:value_field], :null => false
|
308
|
+
|
309
|
+
t.timestamps
|
310
|
+
end
|
311
|
+
|
312
|
+
self.connection.add_index eav_options[model][:table_name], eav_options[model][:foreign_key]
|
313
|
+
end
|
314
|
+
|
315
|
+
end
|
316
|
+
|
317
|
+
##
|
318
|
+
# Rake migration task to drop the attribute table
|
319
|
+
#
|
320
|
+
def drop_attribute_table(options = {})
|
321
|
+
eav_options.keys.each do |key|
|
322
|
+
continue if eav_options[key][:parent] != self.name
|
323
|
+
model = eav_options[key][:class_name]
|
324
|
+
self.connection.drop_table eav_options[model][:table_name]
|
325
|
+
end
|
326
|
+
|
327
|
+
end
|
328
|
+
|
329
|
+
end
|
330
|
+
|
331
|
+
##
|
332
|
+
# Will determine if the given attribute is a eav attribute on the
|
333
|
+
# given model. Override this in your class to provide custom logic if
|
334
|
+
# the #eav_attributes method or the :fields option are not flexible
|
335
|
+
# enough. If you override this method :fields and #eav_attributes will
|
336
|
+
# not apply at all unless you implement them yourself.
|
337
|
+
#
|
338
|
+
def is_eav_attribute?(attribute_name, model)
|
339
|
+
attribute_name = attribute_name.to_s
|
340
|
+
model_options = eav_options[model.name]
|
341
|
+
return model_options[:fields].include?(attribute_name) unless model_options[:fields].nil?
|
342
|
+
return eav_attributes(model).collect {|field| field.to_s}.include?(attribute_name) unless
|
343
|
+
eav_attributes(model).nil?
|
344
|
+
true
|
345
|
+
end
|
346
|
+
|
347
|
+
##
|
348
|
+
# Return a list of valid eav attributes for the given model. Return
|
349
|
+
# nil if any field is allowed. If you want to say no field is allowed
|
350
|
+
# then return an empty array. If you just have a static list the :fields
|
351
|
+
# option is most likely easier.
|
352
|
+
#
|
353
|
+
def eav_attributes(model); nil end
|
354
|
+
|
355
|
+
##
|
356
|
+
# CLK added a respond_to? implementation so that ActiveRecord AssociationProxy (polymorphic relationships)
|
357
|
+
# does not mask the method_missing implementation here. See:
|
358
|
+
# https://rails.lighthouseapp.com/projects/8994/tickets/2378-associationproxymethod_missing-masks-method_missing-in-models
|
359
|
+
# Updated when certain magic fields (like timestamp columns) were interfering with saves, etc.
|
360
|
+
# http://oldwiki.rubyonrails.org/rails/pages/MagicFieldNames
|
361
|
+
#
|
362
|
+
def respond_to_with_eav_behavior?(method_id, include_private = false)
|
363
|
+
if MAGIC_FIELD_NAMES.include?(method_id.to_sym)
|
364
|
+
respond_to_without_eav_behavior?(method_id, include_private)
|
365
|
+
else
|
366
|
+
true
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
private
|
371
|
+
|
372
|
+
##
|
373
|
+
# Called after validation on update so that eav attributes behave
|
374
|
+
# like normal attributes in the fact that the database is not touched
|
375
|
+
# until save is called.
|
376
|
+
#
|
377
|
+
def save_modified_eav_attributes
|
378
|
+
return if @save_eav_attr.nil?
|
379
|
+
@save_eav_attr.each do |s|
|
380
|
+
model, attribute_name = s
|
381
|
+
related_attribute = find_related_eav_attribute(model, attribute_name)
|
382
|
+
unless related_attribute.nil?
|
383
|
+
if related_attribute.value.nil?
|
384
|
+
eav_related(model).delete(related_attribute)
|
385
|
+
else
|
386
|
+
related_attribute.save
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
@save_eav_attr = []
|
391
|
+
end
|
392
|
+
|
393
|
+
##
|
394
|
+
# Overrides ActiveRecord::Base#read_attribute
|
395
|
+
#
|
396
|
+
def read_attribute_with_eav_behavior(attribute_name)
|
397
|
+
attribute_name = attribute_name.to_s
|
398
|
+
exec_if_related(attribute_name) do |model|
|
399
|
+
return nil if !@remove_eav_attr.nil? && @remove_eav_attr.any? do |r|
|
400
|
+
r[0] == model && r[1] == attribute_name
|
401
|
+
end
|
402
|
+
|
403
|
+
value_field = eav_options[model.name][:value_field]
|
404
|
+
related_attribute = find_related_eav_attribute(model, attribute_name)
|
405
|
+
|
406
|
+
return nil if related_attribute.nil?
|
407
|
+
return related_attribute.send(value_field)
|
408
|
+
end
|
409
|
+
read_attribute_without_eav_behavior(attribute_name)
|
410
|
+
end
|
411
|
+
|
412
|
+
##
|
413
|
+
# Overrides ActiveRecord::Base#write_attribute
|
414
|
+
#
|
415
|
+
def write_attribute_with_eav_behavior(attribute_name, value)
|
416
|
+
attribute_name = attribute_name.to_s
|
417
|
+
exec_if_related(attribute_name) do |model|
|
418
|
+
value_field = eav_options[model.name][:value_field]
|
419
|
+
@save_eav_attr ||= []
|
420
|
+
@save_eav_attr << [model, attribute_name]
|
421
|
+
related_attribute = find_related_eav_attribute(model, attribute_name)
|
422
|
+
if related_attribute.nil?
|
423
|
+
# Used to check for nil? but this caused validation
|
424
|
+
# problems that are harder to solve. blank? is probably
|
425
|
+
# not correct but it works well for now.
|
426
|
+
unless value.blank?
|
427
|
+
name_field = eav_options[model.name][:name_field]
|
428
|
+
foreign_key = eav_options[model.name][:foreign_key]
|
429
|
+
eav_related(model).build name_field => attribute_name,
|
430
|
+
value_field => value, foreign_key => self.id
|
431
|
+
end
|
432
|
+
return value
|
433
|
+
else
|
434
|
+
value_field = (value_field.to_s + '=').to_sym
|
435
|
+
return related_attribute.send(value_field, value)
|
436
|
+
end
|
437
|
+
end
|
438
|
+
write_attribute_without_eav_behavior(attribute_name, value)
|
439
|
+
end
|
440
|
+
|
441
|
+
##
|
442
|
+
# Custom method added by ckraybill!
|
443
|
+
#
|
444
|
+
def attribute_empty_with_eav_behavior(attribute_name)
|
445
|
+
!read_attribute_with_eav_behavior(attribute_name).blank?
|
446
|
+
end
|
447
|
+
|
448
|
+
##
|
449
|
+
# Implements eav-attributes as if real getter/setter methods
|
450
|
+
# were defined.
|
451
|
+
# Method modified to support '?' behavior
|
452
|
+
def method_missing_with_eav_behavior(method_id, *args, &block)
|
453
|
+
begin
|
454
|
+
method_missing_without_eav_behavior(method_id, *args, &block)
|
455
|
+
rescue NoMethodError => e
|
456
|
+
m = method_id.to_s
|
457
|
+
attribute_name = m.sub(/\=|\?$/, '')
|
458
|
+
exec_if_related(attribute_name) do |model|
|
459
|
+
if m =~ /\=$/
|
460
|
+
return write_attribute_with_eav_behavior(attribute_name, args[0])
|
461
|
+
elsif m =~ /\?$/
|
462
|
+
return attribute_empty_with_eav_behavior(attribute_name)
|
463
|
+
else
|
464
|
+
return read_attribute_with_eav_behavior(attribute_name)
|
465
|
+
end
|
466
|
+
end
|
467
|
+
raise e
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
##
|
472
|
+
# Retrieve the related eav attribute object
|
473
|
+
#
|
474
|
+
def find_related_eav_attribute(model, attribute_name)
|
475
|
+
name_field = eav_options[model.name][:name_field]
|
476
|
+
eav_related(model).to_a.find do |relation|
|
477
|
+
relation.send(name_field) == attribute_name
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
##
|
482
|
+
# Retrieve the collection of related eav attributes
|
483
|
+
#
|
484
|
+
def eav_related(model)
|
485
|
+
relationship = eav_options[model.name][:relationship_name]
|
486
|
+
send relationship
|
487
|
+
end
|
488
|
+
|
489
|
+
##
|
490
|
+
# yield only if attribute_name is a eav_attribute
|
491
|
+
#
|
492
|
+
def exec_if_related(attribute_name)
|
493
|
+
return false if self.class.column_names.include?(attribute_name) || MAGIC_FIELD_NAMES.include?(attribute_name.to_sym)
|
494
|
+
each_eav_relation do |model|
|
495
|
+
if is_eav_attribute?(attribute_name, model)
|
496
|
+
yield model
|
497
|
+
end
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
##
|
502
|
+
# yields for each eav relation.
|
503
|
+
#
|
504
|
+
def each_eav_relation
|
505
|
+
eav_options.keys.each {|kls| yield kls.constantize}
|
506
|
+
end
|
507
|
+
|
508
|
+
end
|
509
|
+
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
class Base
|
514
|
+
|
515
|
+
##
|
516
|
+
# Overrides ActiveRecord::Base#attributes=
|
517
|
+
#
|
518
|
+
# Because in version >=2.2.2 of ActiveRecord the behaviour in the attributes
|
519
|
+
# have been changed to throw a NoMethodError if the AR object don't respond
|
520
|
+
# to an attribute setter, we could not use method missing to allow for our
|
521
|
+
# entity-attribute-value behaviour.
|
522
|
+
#
|
523
|
+
def attributes=(new_attributes, guard_protected_attributes = true)
|
524
|
+
return if new_attributes.nil?
|
525
|
+
attributes = new_attributes.dup
|
526
|
+
attributes.stringify_keys!
|
527
|
+
multi_parameter_attributes = []
|
528
|
+
attributes = sanitize_for_mass_assignment(attributes) if guard_protected_attributes
|
529
|
+
attributes.each do |k, v|
|
530
|
+
if k.include?("(")
|
531
|
+
multi_parameter_attributes << [ k, v ]
|
532
|
+
else
|
533
|
+
send(:"#{k}=", v)
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
assign_multiparameter_attributes(multi_parameter_attributes)
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
ActiveRecord::Base.send :include, ActiveRecord::Acts::EavModel
|