-
Notifications
You must be signed in to change notification settings - Fork 0
/
attibute.go
316 lines (272 loc) · 7.93 KB
/
attibute.go
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
package activerecord
import (
"fmt"
"sort"
"github.com/activegraph/activegraph/activesupport"
)
// primaryKey must implement attributes that are primary keys.
type primaryKey interface {
PrimaryKey() bool
}
type Attribute interface {
AttributeName() string
AttributeType() Type
}
type AttributeMethods interface {
AttributeNames() []string
HasAttribute(attrName string) bool
HasAttributes(attrNames ...string) bool
AttributeForInspect(attrName string) Attribute
AttributesForInspect(attrNames ...string) []Attribute
}
type AttributeAccessors interface {
ID() interface{}
AttributePresent(attrName string) bool
Attribute(attrName string) interface{}
// AccessAttribute(attrName string) interface{}
AssignAttribute(attrName string, val interface{}) error
AssignAttributes(newAttributes map[string]interface{}) error
}
// PrimaryKey makes any specified attribute a primary key.
type PrimaryKey struct {
Attribute
}
// PrimaryKey always returns true.
func (p PrimaryKey) PrimaryKey() bool {
return true
}
type attr struct {
Name string
Type Type
}
func (a attr) AttributeName() string {
return a.Name
}
func (a attr) AttributeType() Type {
return a.Type
}
// ErrUnknownAttribute is returned on attempt to assign unknown attribute to the
// ActiveRecord.
type ErrUnknownAttribute struct {
RecordName string
Attr string
}
// Error returns a string representation of the error.
func (e *ErrUnknownAttribute) Error() string {
return fmt.Sprintf("unknown attribute %q for %s", e.Attr, e.RecordName)
}
const (
// default name of the primary key.
defaultPrimaryKeyName = "id"
)
type attributesMap map[string]Attribute
func (m attributesMap) copy() attributesMap {
mm := make(attributesMap, len(m))
for name, attr := range m {
mm[name] = attr
}
return mm
}
// attributes of the ActiveRecord.
type attributes struct {
recordName string
primaryKey Attribute
keys attributesMap
values activesupport.Hash
}
func (a *attributes) copy() *attributes {
return &attributes{
recordName: a.recordName,
primaryKey: a.primaryKey,
keys: a.keys.copy(),
values: a.values.Copy(),
}
}
func (a *attributes) clear() *attributes {
newa := a.copy()
newa.values = make(activesupport.Hash, len(a.keys))
return newa
}
func (a *attributes) merge(a1 *attributes) *attributes {
for attrName, attr := range a1.keys {
a.keys[attrName] = attr
}
for attrName, attrVal := range a1.values {
a.values[attrName] = attrVal
}
return a
}
// newAttributes creates a new collection of attributes for the specified record.
func newAttributes(recordName string, attrs attributesMap, values activesupport.Hash) (
*attributes, error,
) {
recordAttrs := attributes{
recordName: recordName,
keys: attrs,
values: values,
}
for _, attr := range recordAttrs.keys {
// Save the primary key attribute as a standalone property for
// easier access to it.
if pk, ok := attr.(primaryKey); ok && pk.PrimaryKey() {
if recordAttrs.primaryKey != nil {
return nil, fmt.Errorf("multiple primary keys are not supported")
}
recordAttrs.primaryKey = attr
}
}
// When the primary key attribute was not specified directly, generate
// a new "id" integer attribute, ensure that the attribute with the same
// name is not presented in the schema definition.
if _, dup := recordAttrs.keys[defaultPrimaryKeyName]; dup && recordAttrs.primaryKey == nil {
err := fmt.Errorf("%q is an attribute, but not a primary key", defaultPrimaryKeyName)
return nil, err
}
if recordAttrs.primaryKey == nil {
pk := PrimaryKey{Attribute: attr{Name: defaultPrimaryKeyName, Type: new(Int64)}}
recordAttrs.primaryKey = pk
if recordAttrs.keys == nil {
recordAttrs.keys = make(attributesMap)
}
recordAttrs.keys[defaultPrimaryKeyName] = pk
}
// Enforce values within a record, all of them must be
// presented in the specified list of attributes.
for attrName := range recordAttrs.values {
if _, ok := recordAttrs.keys[attrName]; !ok {
err := &ErrUnknownAttribute{RecordName: recordName, Attr: attrName}
return nil, err
}
}
return &recordAttrs, nil
}
func (a *attributes) each(fn func(name string, value interface{})) {
for attrName, value := range a.values {
fn(attrName, value)
}
}
func (a *attributes) PrimaryKey() string {
return a.primaryKey.AttributeName()
}
// ID returns the primary key column's value.
func (a *attributes) ID() interface{} {
return a.values[a.primaryKey.AttributeName()]
}
// AttributeNames return an array of names for the attributes available on this object.
func (a *attributes) AttributeNames() []string {
names := make([]string, 0, len(a.keys))
for name := range a.keys {
names = append(names, name)
}
sort.StringSlice(names).Sort()
return names
}
func (a *attributes) ColumnNames() []string {
names := make([]string, 0, len(a.keys))
for name := range a.keys {
names = append(names, a.recordName+"s."+name)
}
sort.StringSlice(names).Sort()
return names
}
// HasAttribute returns true if the given table attribute is in the attribute map,
// otherwise false.
func (a *attributes) HasAttribute(attrName string) bool {
_, ok := a.keys[attrName]
return ok
}
func (a *attributes) HasAttributes(attrNames ...string) bool {
for _, attrName := range attrNames {
if !a.HasAttribute(attrName) {
return false
}
}
return true
}
// AssignAttribute allows to set attribute by the name.
//
// Method return an error when value does not pass validation of the attribute.
func (a *attributes) AssignAttribute(attrName string, val interface{}) error {
if !a.HasAttribute(attrName) {
return &ErrUnknownAttribute{RecordName: a.recordName, Attr: attrName}
}
// TODO: Ensure that attribute passes validation?
// if err := attr.Validate(val); err != nil {
// return err
// }
if a.values == nil {
a.values = make(activesupport.Hash)
}
a.values[attrName] = val
return nil
}
// AssignAttributes allows to set all the attributes by passing in a map of attributes
// with keys matching attributet names.
//
// The method either assigns all provided attributes, no attributes are assigned
// in case of error.
func (a *attributes) AssignAttributes(newAttributes map[string]interface{}) error {
// Create a copy of attributes, either update all attributes or
// return the object in the previous state.
var (
keys = a.keys.copy()
values = a.values.Copy()
)
for attrName, val := range newAttributes {
err := a.AssignAttribute(attrName, val)
if err != nil {
// Return the original state of the attributes.
a.keys = keys
a.values = values
return err
}
}
return nil
}
// AccessAttribute returns the value of the attribute identified by attrName.
func (a *attributes) AccessAttribute(attrName string) (val interface{}) {
if !a.HasAttribute(attrName) {
return nil
}
return a.values[attrName]
}
// Attribute is an alias for AccessAttribute.
func (a *attributes) Attribute(attrName string) (val interface{}) {
return a.AccessAttribute(attrName)
}
// AttributePresent returns true if the specified attribute has been set by the user
// or by a database and is not nil, otherwise false.
func (a *attributes) AttributePresent(attrName string) bool {
if !a.HasAttribute(attrName) {
return false
}
return a.values[attrName] != nil
}
func (a *attributes) AttributeForInspect(attrName string) Attribute {
if !a.HasAttribute(attrName) {
return nil
}
return a.keys[attrName]
}
func (a *attributes) AttributesForInspect(attrNames ...string) []Attribute {
attrs := make([]Attribute, 0, len(attrNames))
if len(attrNames) == 0 {
attrNames = a.AttributeNames()
}
for _, attrName := range attrNames {
if a.HasAttribute(attrName) {
attrs = append(attrs, a.keys[attrName])
}
}
return attrs
}
// ExceptAttribute removes the specified attribute. Method returns error when attribute
// is unknown.
func (a *attributes) ExceptAttribute(attrName string) error {
if !a.HasAttribute(attrName) {
return &ErrUnknownAttribute{RecordName: a.recordName, Attr: attrName}
}
delete(a.keys, attrName)
delete(a.values, attrName)
return nil
}