sack 1.1.3 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -3
- data/lib/sack/database.rb +4 -1
- data/lib/sack/database/data.rb +25 -0
- data/lib/sack/database/generator.rb +2 -2
- data/lib/sack/database/model/data.rb +48 -0
- data/lib/sack/database/model/relationships.rb +6 -0
- data/lib/sack/database/model/relationships/has_many.rb +4 -1
- data/lib/sack/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5c9e2e71505eb323e90a5113162fb887269f089d
|
4
|
+
data.tar.gz: fc1de55fd35990aba81d923f39cd05e22003c477
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3bc090d5caaa1be73bbc06d7e03b0cb735fe299738b235f110da87835544bd8ee175444ffa1806640871b9474cb1c014190a1cdc3c0b3cd9a2b4b4d912a841fe
|
7
|
+
data.tar.gz: d0f2664d7ff967d4ecb6508c3cf17438c4ae1e0411881f74c601ab924bbeaedfd230acd317894904561d8ba693eb3fef9f5f49ade232c2210984b4f1eff23cfb
|
data/README.md
CHANGED
@@ -60,6 +60,9 @@ users = db.fetch_all :user
|
|
60
60
|
# Find (fetch by ID - first result)
|
61
61
|
u = db.find :user, u[:id]
|
62
62
|
|
63
|
+
# Find by Field
|
64
|
+
u = db.find_by :user, :name, 'foobar'
|
65
|
+
|
63
66
|
# Count
|
64
67
|
user_count = db.count :user
|
65
68
|
|
@@ -259,16 +262,20 @@ The name of the association can be anything. The target model NEEDS to be specif
|
|
259
262
|
|
260
263
|
Unless explicitly specified, Sack will use the name of the current model to determine the foreign key (the field within the target model which holds the ID of the current model).
|
261
264
|
|
265
|
+
Delete actions can be defined on 'has_many' relationships:
|
266
|
+
* detach (nullifies the foreign key)
|
267
|
+
* delete (removes the related entities)
|
268
|
+
|
262
269
|
```ruby
|
263
270
|
module Foo
|
264
271
|
include Sack::Database::Model
|
265
272
|
field id: [:int, :pk, :ai]
|
266
273
|
|
267
274
|
# Foreign Key in Bar is 'foo'
|
268
|
-
has_many bars: :bar
|
275
|
+
has_many bars: :bar, on_delete: :detach
|
269
276
|
|
270
277
|
# Foreign Key in Bork is 'parent'
|
271
|
-
has_many borks: :bork, fk: :parent
|
278
|
+
has_many borks: :bork, fk: :parent, on_delete: :delete
|
272
279
|
end
|
273
280
|
|
274
281
|
module Bar
|
@@ -297,7 +304,7 @@ Upon initialization, Sack verifies the database against the schema defined by yo
|
|
297
304
|
|
298
305
|
New tables will be created, as well as new fields (columns) in already-existing tables.
|
299
306
|
|
300
|
-
*HOWEVER* it must be noted that things only get
|
307
|
+
*HOWEVER* it must be noted that things only get added. Tables and fields are NEVER removed. Existing fields are NEVER modified.
|
301
308
|
This keeps the concept of migration VERY simple.
|
302
309
|
|
303
310
|
## License
|
data/lib/sack/database.rb
CHANGED
data/lib/sack/database/data.rb
CHANGED
@@ -60,6 +60,12 @@ module Sack
|
|
60
60
|
fetch(table, id).try :first
|
61
61
|
end
|
62
62
|
|
63
|
+
# Find By Field:
|
64
|
+
# Fetches the first matching row from a given _table_ where _field_ matches _val_.
|
65
|
+
def find_by table, field, val
|
66
|
+
fetch_by(table, field, val).try :first
|
67
|
+
end
|
68
|
+
|
63
69
|
# Fetch:
|
64
70
|
# Fetches rows from a given _table_ by _id_.
|
65
71
|
# @param [Symbol] table Table name
|
@@ -104,6 +110,16 @@ module Sack
|
|
104
110
|
@db.execute Statement.prep("update #{Sanitizer.table @schema, table} set #{Generator.update_marks @schema, table, fields} where id = ?;", [Generator.values(fields.values), id].flatten)
|
105
111
|
end
|
106
112
|
|
113
|
+
# Update By Field:
|
114
|
+
# Updates _fields_ in rows where _field_ matches _val_.
|
115
|
+
# @param [Symbol] table Table name
|
116
|
+
# @param [Symbol] field Field name
|
117
|
+
# @param [Object] val Field value
|
118
|
+
# @param [Hash] fields Fields to be updated
|
119
|
+
def update_by table, field, val, fields
|
120
|
+
@db.execute Statement.prep("update #{Sanitizer.table @schema, table} set #{Generator.update_marks @schema, table, fields} where #{Sanitizer.field @schema, table, field} = ?;", [Generator.values(fields.values), val].flatten)
|
121
|
+
end
|
122
|
+
|
107
123
|
# Save:
|
108
124
|
# Creates or updates _fields_ in a given _table_, depending on the presence of an _id_.
|
109
125
|
# @param [Symbol] table Table name
|
@@ -117,6 +133,15 @@ module Sack
|
|
117
133
|
end
|
118
134
|
|
119
135
|
# Destroy:
|
136
|
+
# Removes rows from a given _table_ where _field_ matches _val_.
|
137
|
+
# @param [Symbol] table Table name
|
138
|
+
# @param [Symbol] field Field name
|
139
|
+
# @param [Object] val Field value
|
140
|
+
def delete_by table, field, val
|
141
|
+
@db.execute Statement.prep("delete from #{Sanitizer.table @schema, table} where #{Sanitizer.field @schema, table, field} = ?;", [val])
|
142
|
+
end
|
143
|
+
|
144
|
+
# Destroy By Field:
|
120
145
|
# Removes rows identified by _id_ from a given _table_.
|
121
146
|
# @param [Symbol] table Table name
|
122
147
|
# @param [Object] id ID of rows to be removed
|
@@ -39,7 +39,7 @@ module Sack
|
|
39
39
|
# @param [Hash] field_hash Hash containing field names as keys
|
40
40
|
# @return [String] A comma-separated list of field value markers ('field = ?')
|
41
41
|
def self.update_marks schema, table, field_hash
|
42
|
-
field_hash.
|
42
|
+
field_hash.collect { |k, v| "#{Sanitizer.field schema, table, k} = #{v ? '?' : 'NULL'}" }.join ', '
|
43
43
|
end
|
44
44
|
|
45
45
|
# Values:
|
@@ -47,7 +47,7 @@ module Sack
|
|
47
47
|
# @param [Array] vals Array of value objects
|
48
48
|
# @return [Array] A copy of _vals_ where every symbol has been replaced by its string representation
|
49
49
|
def self.values vals
|
50
|
-
vals.collect { |v| v.is_a?(Symbol) ? v.to_s : v }
|
50
|
+
vals.collect { |v| v.nil? ? nil : (v.is_a?(Symbol) ? v.to_s : v) }.reject { |e| e.nil? }
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
@@ -52,6 +52,54 @@ module Sack
|
|
52
52
|
raise Validation::ValidationException.new "Invalid Entity [#{data}] for Model #{@model}", errors unless is_valid? db, data, errors
|
53
53
|
end
|
54
54
|
|
55
|
+
# Check Delete
|
56
|
+
if name == :delete
|
57
|
+
|
58
|
+
# Get ID
|
59
|
+
id = args.first
|
60
|
+
|
61
|
+
# Run through Relations
|
62
|
+
relationships.each do |_rname, relationship|
|
63
|
+
|
64
|
+
# Acquire Target Model
|
65
|
+
tmodel = @model_root.const_get relationship[:target].to_s.camelcase
|
66
|
+
|
67
|
+
# Switch on delete action
|
68
|
+
case relationship[:options][:on_delete]
|
69
|
+
when :detach
|
70
|
+
tmodel.update_by db, relationship[:fk], id, relationship[:fk] => nil
|
71
|
+
when :delete
|
72
|
+
tmodel.delete_by db, relationship[:fk], id
|
73
|
+
else
|
74
|
+
# NoOp
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Check Delete By
|
80
|
+
if name == :delete_by
|
81
|
+
|
82
|
+
# Fetch Set
|
83
|
+
set = fetch_by db, *args
|
84
|
+
|
85
|
+
# Run through Relations
|
86
|
+
relationships.each do |_rname, relationship|
|
87
|
+
|
88
|
+
# Acquire Target Model
|
89
|
+
tmodel = @model_root.const_get relationship[:target].to_s.camelcase
|
90
|
+
|
91
|
+
# Switch on delete action
|
92
|
+
case relationship[:options][:on_delete]
|
93
|
+
when :detach
|
94
|
+
set.each { |e| tmodel.update_by db, relationship[:fk], e[:id], relationship[:fk] => nil }
|
95
|
+
when :delete
|
96
|
+
set.each { |e| tmodel.delete_by db, relationship[:fk], e[:id] }
|
97
|
+
else
|
98
|
+
# NoOp
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
55
103
|
# Forward to Database
|
56
104
|
result = db.send name, table_name, *args
|
57
105
|
|
@@ -28,6 +28,12 @@ module Sack
|
|
28
28
|
# Class Methods:
|
29
29
|
# Collection of methods to be injected into anything that includes this module.
|
30
30
|
module ClassMethods
|
31
|
+
|
32
|
+
# Relationships:
|
33
|
+
# Gets the relationships for the Model.
|
34
|
+
def relationships
|
35
|
+
@relationships ||= {}
|
36
|
+
end
|
31
37
|
end
|
32
38
|
end
|
33
39
|
end
|
@@ -22,7 +22,7 @@ module Sack
|
|
22
22
|
def has_many options
|
23
23
|
|
24
24
|
# Internalise Options (so we can mess it up)
|
25
|
-
options = options.
|
25
|
+
options = options.dclone
|
26
26
|
|
27
27
|
# Pull Relationship Name & Target Model
|
28
28
|
name, target_model_name = options.first
|
@@ -31,6 +31,9 @@ module Sack
|
|
31
31
|
# Determine Foreign Key (which field in the remote model holds our ID)
|
32
32
|
foreign_key = options[:fk] || table_name
|
33
33
|
|
34
|
+
# Register Relationship
|
35
|
+
relationships[name] = { target: target_model_name, fk: foreign_key, options: options.dclone }
|
36
|
+
|
34
37
|
# Construct Proxy Method Module
|
35
38
|
proxy = Module.new do
|
36
39
|
|
data/lib/sack/version.rb
CHANGED