groovy 0.6.1 → 0.6.4

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
  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