groovy 0.6.1 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bf0e3aa6e2c0d1e3d9e0245062e1b778e4da15e9130cc672d07eb1e474e25d0f
4
- data.tar.gz: add6fe670e018efb4f5706b02d05154bdf525d5f2bb3f8ce378e60bb92471f59
3
+ metadata.gz: a622cdd3453e0fee1feeebf798cbcec9f0ef9f68759aec64f65c3d4a500f52af
4
+ data.tar.gz: be1220642bf299a61ad5900bea1fffa3359725575def843ce233290e26a5b8e2
5
5
  SHA512:
6
- metadata.gz: '0228c03673a0d86e3072c3f7a952a152c3799125f3c3a2998a219d6015a0c6ccea54bd10972996f17dab52b47298fdc3424fe9bff5de3343a3803748d22bbef1'
7
- data.tar.gz: 8302d8af1e7940a878643f123760e5ae482b786c6c6857cf69306f8b2bb0e79a7364f05035659611102966a7b199aae7ce1eab869cdc9f2ab8055551cb5cdc42
6
+ metadata.gz: 9943e2715a0099f8b1b2795ab66259cbb5b71d5a782e4669b503e61f30a8df56fa57332f88c3592e3b69c0c63638da5d1ea471b102e62511028178886cd5b968
7
+ data.tar.gz: fa02339637f33c69bb37301f63d8b14b77d61b185f0b3c23d78712fb26159d2d5594dc2bb9bba7bb225211c4ab6a5adef167ddade4ea5b5999dae8ce2d5e5f07
data/lib/groovy/model.rb CHANGED
@@ -13,6 +13,10 @@ module Groovy
13
13
  class Error < StandardError; end
14
14
  class RecordNotPersisted < Error; end
15
15
 
16
+ def self.models
17
+ @models ||= {}
18
+ end
19
+
16
20
  module Model
17
21
 
18
22
  def self.initialize_from_record(obj)
@@ -33,9 +37,14 @@ module Groovy
33
37
  base.extend(ClassMethods)
34
38
  base.include(Forwardable)
35
39
  base.table_name = base.name.sub(/y$/, 'ie') + 's'
40
+
41
+ # add to global model list
42
+ # Groovy.models[base.name] = base
43
+ Groovy.models[base.table_name] = base
36
44
  end
37
45
 
38
46
  def self.get_class(table_name)
47
+ # classify method
39
48
  classified = table_name.gsub(/([A-Z])/, '_\1').split('_').collect! { |w| w.capitalize }.join
40
49
  Kernel.const_get(classified)
41
50
  end
@@ -253,10 +262,14 @@ module Groovy
253
262
  @callbacks ||= {}
254
263
  end
255
264
 
256
- [:before_create, :after_create].each do |event|
257
- define_method(event) do |*method_names|
258
- callbacks[:before_create] ||= []
259
- callbacks[:before_create].push(*method_names)
265
+ [:before_create, :after_create, :before_update, :after_update].each do |event|
266
+ define_method(event) do |*args, &block|
267
+ callbacks[event] ||= []
268
+ if block
269
+ callbacks[event].push(block)
270
+ else
271
+ callbacks[event].push(*args)
272
+ end
260
273
  end
261
274
  end
262
275
 
@@ -306,6 +319,7 @@ module Groovy
306
319
 
307
320
  def initialize(attrs = nil, record = nil, key = nil)
308
321
  @attributes, @vectors, @_key = {}, {}, key # key is used on creation only
322
+ @foreign_refs_to_update = {}
309
323
 
310
324
  if set_record(record)
311
325
  # load_attributes_from_record(record)
@@ -344,6 +358,10 @@ module Groovy
344
358
  # _key.nil?
345
359
  end
346
360
 
361
+ def persisted?
362
+ !new_record?
363
+ end
364
+
347
365
  def [](key)
348
366
  k = key.to_sym
349
367
  @attributes[k] = get_record_attribute(k) unless @attributes.key?(k)
@@ -377,6 +395,18 @@ module Groovy
377
395
  obj.each { |k,v| public_send("#{k}=", v) }
378
396
  end
379
397
 
398
+ def set_reverse_reference(key, obj, removing = false)
399
+ if self.class.schema.singular_references.include?(key.to_sym)
400
+ # puts "setting #{obj} as #{key}"
401
+ set_ref(key, removing ? nil : obj, true)
402
+ elsif self.class.schema.plural_references.include?(key.to_sym)
403
+ # puts "adding #{obj} to #{key}"
404
+ public_send(key).set_ref(obj, removing)
405
+ else
406
+ raise "Invalid reference name: #{name}"
407
+ end
408
+ end
409
+
380
410
  def update_attributes(obj)
381
411
  set_attributes(obj)
382
412
  return false if respond_to?(:invalid?) and invalid?
@@ -392,7 +422,7 @@ module Groovy
392
422
  if new_record?
393
423
  create && true
394
424
  else
395
- update
425
+ update && true
396
426
  end
397
427
  end
398
428
 
@@ -422,6 +452,7 @@ module Groovy
422
452
 
423
453
  @attributes = {}
424
454
  @changes = {}
455
+ @foreign_refs_to_update = {}
425
456
  self
426
457
  end
427
458
 
@@ -439,6 +470,7 @@ module Groovy
439
470
 
440
471
  private
441
472
 
473
+
442
474
  def get_record_attribute(key)
443
475
  return if record.nil?
444
476
  val = record[key]
@@ -475,12 +507,26 @@ module Groovy
475
507
  end
476
508
  end
477
509
 
478
- def set_ref(name, obj)
479
- if record.nil?
510
+
511
+ def set_ref(name, obj, from_foreign = false)
512
+ # puts "Setting ref #{name} -> #{obj} (#{obj.class})"
513
+
514
+ if !from_foreign && obj.is_a?(Model) && !obj.new_record?
515
+ if foreign_ref = self.class.schema.reverse_reference_of(name.to_sym)
516
+ # puts "Found circular ref (#{name} -> #{foreign_ref})"
517
+ if current = get_ref(name)
518
+ current.set_reverse_reference(foreign_ref, self, true)
519
+ end
520
+
521
+ @foreign_refs_to_update[name] = [foreign_ref, obj]
522
+ end
523
+ end
524
+
525
+ if record.nil? # not persisted yet
480
526
  set_attribute(name, obj.id) # obj should be a groovy model or groonga record
481
527
  else
482
- obj = obj.record if obj.respond_to?(:record)
483
- record[name] = obj
528
+ val = obj.respond_to?(:record) ? obj.record : obj
529
+ record[name] = val
484
530
  end
485
531
  end
486
532
 
@@ -492,17 +538,28 @@ module Groovy
492
538
  def create
493
539
  fire_callbacks(:before_create)
494
540
  set_record(self.class.insert(@attributes, @_key))
541
+ update_foreign_refs
495
542
  fire_callbacks(:after_create)
496
543
  self
497
544
  end
498
545
 
499
546
  def fire_callbacks(name)
547
+ # puts "Firing #{name} callbacks"
500
548
  if arr = self.class.callbacks[name] and arr.any?
501
- arr.each { |fn| send(fn) }
549
+ arr.each { |fn| fire_callback(fn) }
550
+ end
551
+ end
552
+
553
+ def fire_callback(fn)
554
+ if fn.is_a?(Proc)
555
+ fn.call(self)
556
+ else
557
+ send(fn)
502
558
  end
503
559
  end
504
560
 
505
561
  def update
562
+ fire_callbacks(:before_update)
506
563
  ensure_persisted!
507
564
  changes.each do |key, values|
508
565
  # puts "Updating #{key} from #{values[0]} to #{values[1]}"
@@ -510,9 +567,22 @@ module Groovy
510
567
  end
511
568
  self.class.set_timestamp(record, :updated_at)
512
569
  changes = {}
570
+ update_foreign_refs
571
+ fire_callbacks(:after_update)
513
572
  self
514
573
  end
515
574
 
575
+ def update_foreign_refs
576
+ return if @foreign_refs_to_update.empty?
577
+
578
+ @foreign_refs_to_update.each do |attr, (ref, obj)|
579
+ # puts "setting #{obj.class}##{ref} to #{self}"
580
+ obj.set_reverse_reference(ref, self)
581
+ end
582
+
583
+ @foreign_refs_to_update = {}
584
+ end
585
+
516
586
  def ensure_persisted!
517
587
  raise "Not persisted" if new_record?
518
588
  end
data/lib/groovy/query.rb CHANGED
@@ -238,7 +238,6 @@ module Groovy
238
238
  end
239
239
 
240
240
  def in_batches(of: 1000, from: nil, &block)
241
- puts "of: #{of}"
242
241
  sorting[:limit] = of
243
242
  sorting[:offset] = from || 0
244
243
 
data/lib/groovy/schema.rb CHANGED
@@ -33,6 +33,11 @@ module Groovy
33
33
  @table ||= context[table_name]
34
34
  end
35
35
 
36
+ # def [](key)
37
+ # # @spec[key.to_sym]
38
+ # table.columns.select { |c| c.column? && c.name.to_s == "#{table_name}.#{key.to_s}" }
39
+ # end
40
+
36
41
  def search_table
37
42
  @cache[:search_table] ||= context[SEARCH_TABLE_NAME]
38
43
  end
@@ -69,6 +74,22 @@ module Groovy
69
74
  get_names(table.columns.select { |c| c.column? && c.range.name == type })
70
75
  end
71
76
 
77
+ def refs_by_table
78
+ @refs_by_table ||= {}
79
+ end
80
+
81
+ def refs_by_name
82
+ @refs_by_name ||= {}
83
+ end
84
+
85
+ def reverse_reference_of(ref_name)
86
+ if ref_table = refs_by_name[ref_name.to_sym]
87
+ if foreign_model = Groovy.models[ref_table]
88
+ foreign_model.schema.refs_by_table[@table_name]
89
+ end
90
+ end
91
+ end
92
+
72
93
  def reload
73
94
  @cache = {}
74
95
  end
@@ -95,6 +116,14 @@ module Groovy
95
116
 
96
117
  def reference(name, table_name = nil, options = {})
97
118
  table_name = "#{name}s" if table_name.nil?
119
+
120
+ unless context[table_name]
121
+ log "Table #{table_name} doesn't exist yet! Creating now..."
122
+ create_table!(table_name)
123
+ end
124
+
125
+ refs_by_table[table_name] = name.to_sym
126
+ refs_by_name[name.to_sym] = table_name
98
127
  @spec[name.to_sym] = { type: :reference, args: [table_name, options] }
99
128
  end
100
129
 
@@ -188,15 +217,15 @@ module Groovy
188
217
  end
189
218
  end
190
219
 
191
- def create_table!
192
- log "Creating table #{table_name}!"
193
- Groonga::Schema.create_table(table_name, context: context)
220
+ def create_table!(name = table_name)
221
+ log "Creating table #{name}!"
222
+ Groonga::Schema.create_table(name, context: context)
194
223
  end
195
224
 
196
- def remove_table!
197
- log "Removing table #{table_name}!"
198
- Groonga::Schema.remove_table(table_name, context: context)
199
- @table = nil
225
+ def remove_table!(name = table_name)
226
+ log "Removing table #{name}!"
227
+ Groonga::Schema.remove_table(name, context: context)
228
+ @table = nil if name == table_name
200
229
  end
201
230
 
202
231
  def get_names(columns)
@@ -204,7 +233,7 @@ module Groovy
204
233
  end
205
234
 
206
235
  def log(str)
207
- puts "[#{table_name}] #{str}" # if ENV['DEBUG']
236
+ puts "[#{table_name}] #{str}" if ENV['DEBUG']
208
237
  end
209
238
  end
210
239
  end
data/lib/groovy/vector.rb CHANGED
@@ -50,7 +50,9 @@ module Groovy
50
50
 
51
51
  def set(new_items)
52
52
  check_parent!
53
- obj.record[key] = new_items
53
+ obj.record[key] = new_items.map do |obj|
54
+ obj.is_a?(Model) ? obj.record : obj
55
+ end
54
56
  end
55
57
 
56
58
  def has?(item)
@@ -61,17 +63,36 @@ module Groovy
61
63
  def push(item)
62
64
  raise "Invalid item type: #{item.class}" unless item.is_a?(Model)
63
65
  check_parent!
64
- raise "Already in list!" if has?(item)
66
+ if has?(item)
67
+ # puts "Item already present #{item.inspect}"
68
+ return false
69
+ end
65
70
  item.save unless item.record
66
71
  push_record(item.record)
72
+ # size
73
+ end
74
+
75
+ def push!(item)
76
+ push or raise "Already in list!"
67
77
  end
68
78
 
69
79
  def remove(item)
70
80
  check_parent!
71
- raise "Item not saved: #{record}" unless item.record
81
+ raise "Item not saved: #{item.inspect}" unless item.record
72
82
  remove_record(item.record)
73
83
  end
74
84
 
85
+ def set_ref(item, removing = false)
86
+ check_parent!
87
+ raise "Item not saved: #{item.inspect}" unless item.record
88
+
89
+ if removing
90
+ remove_record(item.record)
91
+ elsif !has?(item)
92
+ push_record(item.record)
93
+ end
94
+ end
95
+
75
96
  alias_method :<<, :push
76
97
 
77
98
  [:first, :last, :find_by, :search, :where, :not, :sort_by, :limit, :offset, :paginate, :in_batches].each do |scope_method|
@@ -1,3 +1,3 @@
1
1
  module Groovy
2
- VERSION = '0.6.1'.freeze
2
+ VERSION = '0.6.4'.freeze
3
3
  end
@@ -0,0 +1,58 @@
1
+ require_relative './spec_helper'
2
+
3
+ describe Groovy::Model do
4
+
5
+ before :all do
6
+ Groovy.open('tmp/callbacks', 'callbacks_spec')
7
+ load_callbacks_schema!
8
+ end
9
+
10
+ after :all do
11
+ Groovy.close('callbacks_spec')
12
+ end
13
+
14
+ def load_callbacks_schema!
15
+ klass = Class.new
16
+ Object.const_set("User", klass)
17
+ User.class_eval do
18
+ include Groovy::Model
19
+
20
+ after_create :created!
21
+ after_update do |record|
22
+ record.updated!
23
+ end
24
+
25
+ schema(context: 'callbacks_spec') do |t|
26
+ t.string :name
27
+ t.timestamps
28
+ end
29
+
30
+ def created!
31
+ # puts "created"
32
+ end
33
+
34
+ def updated!
35
+ # puts "updated"
36
+ end
37
+ end
38
+
39
+ User.add_reference :comments, "Comments", type: :vector
40
+ end
41
+
42
+ it 'fires created callbacks' do
43
+ user = User.new(name: 'John')
44
+ expect(user).to receive(:created!)
45
+ expect(user).not_to receive(:updated!)
46
+ expect(user.save).to eq(true)
47
+ end
48
+
49
+ it 'fires updated callbacks' do
50
+ user = User.new(name: 'John')
51
+ user.save
52
+ user.name = 'Josh'
53
+ expect(user).not_to receive(:created!)
54
+ expect(user).to receive(:updated!)
55
+ expect(user.save).to eq(true)
56
+ end
57
+
58
+ end
data/spec/vector_spec.rb CHANGED
@@ -22,7 +22,7 @@ describe Groovy::Model do
22
22
  end
23
23
  end
24
24
 
25
- klass = Class.new;
25
+ klass = Class.new
26
26
  Object.const_set("User", klass)
27
27
  User.class_eval do
28
28
  include Groovy::Model
@@ -42,13 +42,25 @@ describe Groovy::Model do
42
42
  load_vector_schema!
43
43
  end
44
44
 
45
- it 'loads records and keeps relations' do
45
+ it 'loads records and adds reverse relations automatically' do
46
46
  user = User.new(name: 'John')
47
47
  expect(user.save).to eq(true)
48
48
  comment = Comment.new(author: user, content: 'Hello there!')
49
49
  expect(comment.save).to eq(true)
50
- user.comments << comment
50
+ # user.comments << comment
51
51
  expect(user.reload.comments).to eq([comment])
52
+
53
+ user2 = User.new(name: 'Paul')
54
+ expect(user2.save).to eq(true)
55
+
56
+ comment.author = user2
57
+ comment.save
58
+
59
+ expect(user.reload.comments).to eq([])
60
+ expect(user2.reload.comments).to eq([comment])
61
+
62
+ comment.delete
63
+ expect(user2.reload.comments).to eq([])
52
64
  end
53
65
 
54
66
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: groovy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.6.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tomás Pollak
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-29 00:00:00.000000000 Z
11
+ date: 2023-11-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rroonga
@@ -97,6 +97,7 @@ files:
97
97
  - lib/groovy/types.rb
98
98
  - lib/groovy/vector.rb
99
99
  - lib/groovy/version.rb
100
+ - spec/callbacks_spec.rb
100
101
  - spec/groovy_spec.rb
101
102
  - spec/model_spec.rb
102
103
  - spec/query_spec.rb