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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 36631ab9960ab0352fc67299eb542d4c45ff7bea
4
- data.tar.gz: 68295a9026aec98c51b35a6574608568510f22de
3
+ metadata.gz: 5c9e2e71505eb323e90a5113162fb887269f089d
4
+ data.tar.gz: fc1de55fd35990aba81d923f39cd05e22003c477
5
5
  SHA512:
6
- metadata.gz: eb2c612f125c841885c785f7ade20715d5afea212c3346a6b98c40499e758d2e2d145051ca57afc21d64f07eb5a1ab1ed2a3bec2a584cca5e941ad0609098d9a
7
- data.tar.gz: 3580ba64a327c27972d694da5a7e7962967303a0507638b9286d689dc3fe3d768c3e2778d70df01bf0567d659fb20f86cab046b62c952f9c3532aaf9b443f6e1
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 +added+. Tables and fields are NEVER removed. Existing fields are NEVER modified.
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
@@ -21,13 +21,16 @@ module Sack
21
21
  :create_table,
22
22
  :count,
23
23
  :find,
24
+ :find_by,
24
25
  :fetch,
25
26
  :fetch_by,
26
27
  :fetch_all,
27
28
  :create,
28
29
  :update,
30
+ :update_by,
29
31
  :save,
30
- :delete
32
+ :delete,
33
+ :delete_by
31
34
  ]
32
35
 
33
36
  # Construct:
@@ -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.keys.collect { |k| "#{Sanitizer.field schema, table, k} = ?" }.join ', '
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.clone
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
 
@@ -5,5 +5,5 @@
5
5
  module Sack
6
6
 
7
7
  # Version
8
- VERSION = '1.1.3'
8
+ VERSION = '1.2.1'
9
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sack
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.3
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eresse