/
api-record.html
139 lines (110 loc) · 7.95 KB
/
api-record.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
{% extends "api.html" %}
{% block api_content %}
<p style="font-size: 14px;"><em>Jump to: </em>
<a href="#mappings">Specifying table and column names</a>
<a href="#instance">Generated instance methods</a>
<a href="#associations">Associations (<code>belongs_to</code>, <code>has</code>)</a>
<a href="#hooks">Save hooks</a>
</p>
<p>
BossRecords are <em>specially compiled parameterized modules</em> that follow the "active record" pattern. BossRecords go into your project's src/model/ folder and will have functions generated for saving them into the database and for accessing related BossRecords. Important aspects of BossRecords:
</p>
<ul>
<li><p>The first parameter of a BossRecord should always be called <code>Id</code>, and the
other parameters should be CamelCased attributes of your data model.</p></li>
<li><p>All parameters will be available as lower-case, underscored functions,
e.g. <code>-module(foo, [Id, TheText])</code> will generate the getter functions
<code>id()</code> and <code>the_text()</code>.</p></li>
<li><p>Parameters can contain typespecs, e.g. <code>-module(foo, [Id, SomeString::string(), ...]).</code>. Valid types are:
<ul>
<li><code>string()</code></li>
<li><code>binary()</code></li>
<li><code>integer()</code></li>
<li><code>float()</code></li>
<li><code>date()</code></li>
<li><code>datetime()</code></li>
<li><code>timestamp()</code></li>
<li><code>boolean()</code></li>
</ul></p>
<p>Types, while optional, are validated before saving to the database.</p></li>
<li><p>BossRecords can be instantiated with either <code>my_module:new/N</code> (like any other parameterized module), or with <code>boss_record:new(my_module, Attributes)</code>, where <code>Attributes</code> is a proplist.</p></li>
<li><p>To auto-generate an ID, pass the atom <code>'id'</code> as the first parameter to <code>my_module:new/N</code>. Alternatively, just use <code>boss_record:new/2</code>.</p></li>
<li><p>Call <code>my_module:new/N</code> with strings, binaries, integers, floats, <code>erlang:now()</code> tuples, DateTime tuples, or atoms for all other parameters</p></li>
<li><p>You can add your own functions to a BossRecord module, which will have access to the generated functions described below.</p></li>
<li><p>To see the full API for a BossRecord called <code>foo</code> at any time during development, point your browser to /doc/foo on your dev server.</p></li>
</ul>
<a name="mappings"></a>
<h2>Specifying table and column names</h2>
<p>If you're connecting to an existing SQL database, you may want to override the default table and column names. (By default, the table name is the plural form of the model name, and the column names are the lowercase, underscored versions of the attribute variables.)</p>
<p>To specify your own database layout, you can use the <code>-table</code> and <code>-columns</code> module attributes:</p>
<div class="code spec">
<span class="attr">-table</span>("my_table_name").<br />
<span class="attr">-columns</span>([{attribute1, "my_column_name"}]).
</div>
<p>These names can be accessed at run-time with the <code>database_table/0</code> and <code>database_columns/0</code> functions; see below.</p>
<a name="instance"></a>
<h2>Generated functions</h2>
<p>Generated instance functions of a BossRecord include:</p>
{% for function in functions %}
{% ifnotequal function.function "trivial_counter" %}
<a name="{{ function.function }}"></a>
<div class="example {% cycle 'row1' 'row2' %}">
<div class="code spec">
{{ function.function }}{% if function.typespec %}{{ function.typespec }}{% endif %}
</div>
<p>{% if function.description_long %}{{ function.description_long }}{% else %} {% endif %}</p>
</div>
{% endifnotequal %}
{% endfor %}
<p>Other getters and setters are generated based on the parameters of your BossRecord.</p>
<a name="associations"></a>
<h2>Associations</h2>
<p>Special associations are generated from the following module attributes:</p>
<div class="code spec">
<span class="attr">-belongs_to</span>(foo).
</div>
<p>Requires a matching <code>FooId</code> in the parameter list. Adds a function <code>foo()</code> which returns the BossRecord (of any type, usually <code>foo</code>) with ID equal to the current BossRecord's <code>FooId</code>. If the model name differs from the field name, use <code>-belongs_to_<model name>(<field name>)</code>.</p>
<div class="code spec">
<span class="attr">-has</span>({bar, 1}).<br />
<span class="attr">-has</span>({bars, Count}). % Count > 1<br />
<span class="attr">-has</span>({bars, many}).<br />
</div>
<p>Generates a function <code>bar()</code> or <code>bars()</code> which returns up to <code>Count</code> "bar" BossRecords with <code>FooId</code> equal to this BossRecord's ID. If Count is greater than 1, also creates <code>first_bar()</code> and <code>last_bar()</code> which return the first and last items in the association.</p>
<p>When <code>Count</code> is not equal to 1, <code>has</code> can also take a proplist of options as the third element in the tuple:</p>
<div class="code spec">
<span class="attr">-has</span>({bars, many, [{order_by, first_name}]}).</span>
</div>
<p>Valid options are:</p>
<ul>
<li><code>order_by</code> - attribute to sort on. Defaults to 'id'</li>
<li><code>descending</code> - whether to values from high to low (a boolean).</li>
<li><code>module</code> - If the assocation name is different than the underlying module, use this option to specify the underlying module.</li>
<li><code>foreign_key</code> - If the associated module uses something unexpected for the foreign key, use this option to specify the foreign key (e.g. <code>person_id</code>).</li>
<li><code>include</code> - a list of belongs_to associations to pre-fetch (atoms). Requires the database cache to be enabled.</li>
</ul>
<p>Note that Time and float attributes are stored internally as integers, so sort them with <code>num_ascending</code> or <code>num_descending</code>.</p>
<p>The two above attributes work similar to <code>belongs_to</code> and <code>has_many/has_one</code> in Rails.</p>
<div class="code spec">
<span class="attr">-counter</span>(foo_counter).
</div>
<p>Generates a function <code>foo_counter()</code> which returns the value of the counter, initialized to zero. Each BossRecord may have an unlimited number of counters. Manipulate the counters with <code>reset</code> and <code>incr</code> above.</p>
<p>SPECIAL NOTE: Everything in the Model directory will be compiled as a BossRecord rather than as a regular Erlang module; you don't need to do or declare anything special.</p>
<a name="hooks"></a>
<h2>Save/delete hooks</h2>
<p>You can specify logic to be run anytime a BossRecord is saved, or before it is deleted. Just define one or more of these functions in your module:</p>
<ul>
<li><code>before_create()</code> - executed just before a new BossRecord is saved to the database.</li>
<li><code>before_update()</code> - executed just before an existing BossRecord is saved to the database.</li>
<li><code>after_create()</code> - executed just after a new BossRecord is saved to the database.</li>
<li><code>after_update()</code> - executed just after an existing BossRecord is saved to the database.</li>
<li><code>before_delete()</code> - executed just before a BossRecord is removed from the database.</li>
</ul>
<p><code>before_create/0</code> and <code>before_update/0</code> should return one of:</p>
<ul>
<li><code>ok</code> - Continue with the save</li>
<li><code>{ok, ModifiedRecord}</code> - Save <code>ModifiedRecord</code> instead</li>
<li><code>{error, Reason}</code> - Abort the save</li>
</ul>
<p><code>before_delete/0</code> should return <code>ok</code> to continue with the deletion, or <code>{error, Reason}</code> to abort the deletion.</p>
<p>Return values for the other hook functions are ignored.</p>
{% endblock %}