groovy 0.6.1 → 0.6.6

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: dc2a1c85a6d482813b6ec9de3616e2c369206a419008b55600194f930bc56416
4
+ data.tar.gz: 968d357fb616dcfa9761f1ba93843678263d69ff38af2ec342ee9430bbf7ccc6
5
5
  SHA512:
6
- metadata.gz: '0228c03673a0d86e3072c3f7a952a152c3799125f3c3a2998a219d6015a0c6ccea54bd10972996f17dab52b47298fdc3424fe9bff5de3343a3803748d22bbef1'
7
- data.tar.gz: 8302d8af1e7940a878643f123760e5ae482b786c6c6857cf69306f8b2bb0e79a7364f05035659611102966a7b199aae7ce1eab869cdc9f2ab8055551cb5cdc42
6
+ metadata.gz: 7df3be7e3c43b5d48be0b563bb156da21a8992c97be9392b91b939a1943920f0587ee01ff4521c983b8524beb27b0ecd0eede630aff7c16e42e80e004ec42a46
7
+ data.tar.gz: e9a7fd6abc2dda7d902cd07d091f1fafbfe053e229a730754f0170dc04aa3326fbbef4a9646f3d6788cf896b7b72e9c0c10f0c010a4d17bd1e99aa97222fcda9
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
@@ -53,6 +62,10 @@ module Groovy
53
62
  db_context[table_name]
54
63
  end
55
64
 
65
+ def underscore_name
66
+ name.gsub(/([A-Z])/) { |x| "_#{x.downcase}" }.sub(/^_/, '')
67
+ end
68
+
56
69
  def attribute_names
57
70
  @attribute_names ||= schema.attribute_columns
58
71
  end
@@ -253,10 +266,14 @@ module Groovy
253
266
  @callbacks ||= {}
254
267
  end
255
268
 
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)
269
+ [:before_create, :after_create, :before_update, :after_update].each do |event|
270
+ define_method(event) do |*args, &block|
271
+ callbacks[event] ||= []
272
+ if block
273
+ callbacks[event].push(block)
274
+ else
275
+ callbacks[event].push(*args)
276
+ end
260
277
  end
261
278
  end
262
279
 
@@ -306,6 +323,7 @@ module Groovy
306
323
 
307
324
  def initialize(attrs = nil, record = nil, key = nil)
308
325
  @attributes, @vectors, @_key = {}, {}, key # key is used on creation only
326
+ @foreign_refs_to_update = {}
309
327
 
310
328
  if set_record(record)
311
329
  # load_attributes_from_record(record)
@@ -344,6 +362,10 @@ module Groovy
344
362
  # _key.nil?
345
363
  end
346
364
 
365
+ def persisted?
366
+ !new_record?
367
+ end
368
+
347
369
  def [](key)
348
370
  k = key.to_sym
349
371
  @attributes[k] = get_record_attribute(k) unless @attributes.key?(k)
@@ -377,6 +399,18 @@ module Groovy
377
399
  obj.each { |k,v| public_send("#{k}=", v) }
378
400
  end
379
401
 
402
+ def set_reverse_reference(key, obj, removing = false)
403
+ if self.class.schema.singular_references.include?(key.to_sym)
404
+ # puts "setting #{obj} as #{key}"
405
+ set_ref(key, removing ? nil : obj, true)
406
+ elsif self.class.schema.plural_references.include?(key.to_sym)
407
+ # puts "adding #{obj} to #{key}"
408
+ public_send(key).set_ref(obj, removing)
409
+ else
410
+ raise "Invalid reference name: #{name}"
411
+ end
412
+ end
413
+
380
414
  def update_attributes(obj)
381
415
  set_attributes(obj)
382
416
  return false if respond_to?(:invalid?) and invalid?
@@ -392,7 +426,7 @@ module Groovy
392
426
  if new_record?
393
427
  create && true
394
428
  else
395
- update
429
+ update && true
396
430
  end
397
431
  end
398
432
 
@@ -422,6 +456,7 @@ module Groovy
422
456
 
423
457
  @attributes = {}
424
458
  @changes = {}
459
+ @foreign_refs_to_update = {}
425
460
  self
426
461
  end
427
462
 
@@ -439,6 +474,7 @@ module Groovy
439
474
 
440
475
  private
441
476
 
477
+
442
478
  def get_record_attribute(key)
443
479
  return if record.nil?
444
480
  val = record[key]
@@ -475,12 +511,26 @@ module Groovy
475
511
  end
476
512
  end
477
513
 
478
- def set_ref(name, obj)
479
- if record.nil?
514
+
515
+ def set_ref(name, obj, from_foreign = false)
516
+ # puts "Setting ref #{name} -> #{obj} (#{obj.class})"
517
+
518
+ if !from_foreign && obj.is_a?(Model) && !obj.new_record?
519
+ if foreign_ref = self.class.schema.reverse_reference_of(name.to_sym)
520
+ # puts "Found circular ref (#{name} -> #{foreign_ref})"
521
+ if current = get_ref(name)
522
+ current.set_reverse_reference(foreign_ref, self, true)
523
+ end
524
+
525
+ @foreign_refs_to_update[name] = [foreign_ref, obj]
526
+ end
527
+ end
528
+
529
+ if record.nil? # not persisted yet
480
530
  set_attribute(name, obj.id) # obj should be a groovy model or groonga record
481
531
  else
482
- obj = obj.record if obj.respond_to?(:record)
483
- record[name] = obj
532
+ val = obj.respond_to?(:record) ? obj.record : obj
533
+ record[name] = val
484
534
  end
485
535
  end
486
536
 
@@ -492,17 +542,28 @@ module Groovy
492
542
  def create
493
543
  fire_callbacks(:before_create)
494
544
  set_record(self.class.insert(@attributes, @_key))
545
+ update_foreign_refs
495
546
  fire_callbacks(:after_create)
496
547
  self
497
548
  end
498
549
 
499
550
  def fire_callbacks(name)
551
+ # puts "Firing #{name} callbacks"
500
552
  if arr = self.class.callbacks[name] and arr.any?
501
- arr.each { |fn| send(fn) }
553
+ arr.each { |fn| fire_callback(fn) }
554
+ end
555
+ end
556
+
557
+ def fire_callback(fn)
558
+ if fn.is_a?(Proc)
559
+ fn.call(self)
560
+ else
561
+ send(fn)
502
562
  end
503
563
  end
504
564
 
505
565
  def update
566
+ fire_callbacks(:before_update)
506
567
  ensure_persisted!
507
568
  changes.each do |key, values|
508
569
  # puts "Updating #{key} from #{values[0]} to #{values[1]}"
@@ -510,9 +571,22 @@ module Groovy
510
571
  end
511
572
  self.class.set_timestamp(record, :updated_at)
512
573
  changes = {}
574
+ update_foreign_refs
575
+ fire_callbacks(:after_update)
513
576
  self
514
577
  end
515
578
 
579
+ def update_foreign_refs
580
+ return if @foreign_refs_to_update.empty?
581
+
582
+ @foreign_refs_to_update.each do |attr, (ref, obj)|
583
+ # puts "setting #{obj.class}##{ref} to #{self}"
584
+ obj.set_reverse_reference(ref, self)
585
+ end
586
+
587
+ @foreign_refs_to_update = {}
588
+ end
589
+
516
590
  def ensure_persisted!
517
591
  raise "Not persisted" if new_record?
518
592
  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,9 +116,25 @@ 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
 
130
+ def one(name, table_name = nil, options = {})
131
+ reference(name, table_name, options)
132
+ end
133
+
134
+ def many(name, table_name = nil, options = {})
135
+ reference(name, table_name, options.merge(type: :vector))
136
+ end
137
+
101
138
  def timestamps(opts = {})
102
139
  column(:created_at, 'time')
103
140
  column(:updated_at, 'time')
@@ -188,15 +225,15 @@ module Groovy
188
225
  end
189
226
  end
190
227
 
191
- def create_table!
192
- log "Creating table #{table_name}!"
193
- Groonga::Schema.create_table(table_name, context: context)
228
+ def create_table!(name = table_name)
229
+ log "Creating table #{name}!"
230
+ Groonga::Schema.create_table(name, context: context)
194
231
  end
195
232
 
196
- def remove_table!
197
- log "Removing table #{table_name}!"
198
- Groonga::Schema.remove_table(table_name, context: context)
199
- @table = nil
233
+ def remove_table!(name = table_name)
234
+ log "Removing table #{name}!"
235
+ Groonga::Schema.remove_table(name, context: context)
236
+ @table = nil if name == table_name
200
237
  end
201
238
 
202
239
  def get_names(columns)
@@ -204,7 +241,7 @@ module Groovy
204
241
  end
205
242
 
206
243
  def log(str)
207
- puts "[#{table_name}] #{str}" # if ENV['DEBUG']
244
+ puts "[#{table_name}] #{str}" if ENV['DEBUG']
208
245
  end
209
246
  end
210
247
  end
data/lib/groovy/vector.rb CHANGED
@@ -26,20 +26,6 @@ module Groovy
26
26
 
27
27
  alias_method :count, :size
28
28
 
29
- # we redefine first and last to avoid having to perform a full query
30
-
31
- def first
32
- if obj = records.first
33
- Model.initialize_from_record(obj)
34
- end
35
- end
36
-
37
- def last
38
- if obj = records.last
39
- Model.initialize_from_record(obj)
40
- end
41
- end
42
-
43
29
  def each(&block)
44
30
  items.each { |r| block.call(r) }
45
31
  end
@@ -50,7 +36,9 @@ module Groovy
50
36
 
51
37
  def set(new_items)
52
38
  check_parent!
53
- obj.record[key] = new_items
39
+ obj.record[key] = new_items.map do |obj|
40
+ obj.is_a?(Model) ? obj.record : obj
41
+ end
54
42
  end
55
43
 
56
44
  def has?(item)
@@ -61,25 +49,62 @@ module Groovy
61
49
  def push(item)
62
50
  raise "Invalid item type: #{item.class}" unless item.is_a?(Model)
63
51
  check_parent!
64
- raise "Already in list!" if has?(item)
52
+ if has?(item)
53
+ # puts "Item already present #{item.inspect}"
54
+ return false
55
+ end
65
56
  item.save unless item.record
66
57
  push_record(item.record)
58
+ # size
59
+ end
60
+
61
+ def push!(item)
62
+ push or raise "Already in list!"
67
63
  end
68
64
 
69
65
  def remove(item)
70
66
  check_parent!
71
- raise "Item not saved: #{record}" unless item.record
67
+ raise "Item not saved: #{item.inspect}" unless item.record
72
68
  remove_record(item.record)
73
69
  end
74
70
 
71
+ def set_ref(item, removing = false)
72
+ check_parent!
73
+ raise "Item not saved: #{item.inspect}" unless item.record
74
+
75
+ if removing
76
+ remove_record(item.record)
77
+ elsif !has?(item)
78
+ push_record(item.record)
79
+ end
80
+ end
81
+
75
82
  alias_method :<<, :push
76
83
 
77
- [:first, :last, :find_by, :search, :where, :not, :sort_by, :limit, :offset, :paginate, :in_batches].each do |scope_method|
84
+ [:new, :create, :create!].each do |relation_method|
85
+ define_method relation_method do |attrs|
86
+ vector_model.public_send(relation_method, attrs.merge(object_name => obj))
87
+ end
88
+ end
89
+
90
+ [:find_by, :search, :where, :not, :sort_by, :limit, :offset, :paginate, :in_batches].each do |scope_method|
78
91
  define_method scope_method do |*args, &block|
79
92
  query.public_send(scope_method, *args, &block)
80
93
  end
81
94
  end
82
95
 
96
+ def first
97
+ if obj = records.first
98
+ Model.initialize_from_record(obj)
99
+ end
100
+ end
101
+
102
+ def last
103
+ if obj = records.last
104
+ Model.initialize_from_record(obj)
105
+ end
106
+ end
107
+
83
108
  def ==(arr)
84
109
  to_a == arr
85
110
  end
@@ -87,21 +112,26 @@ module Groovy
87
112
  private
88
113
  attr_reader :obj, :key
89
114
 
90
- def query
91
- model = Model.model_from_table(key.capitalize)
92
- obj_name = obj.class.name.downcase
93
- model.query.where(obj_name => obj.id)
94
- end
95
-
96
115
  def method_missing(name, *args, &block)
97
- model = Model.model_from_table(key.capitalize)
98
- if model.scopes.include?(name)
116
+ if vector_model.scopes.include?(name)
99
117
  query.send(name)
100
118
  else
101
119
  super
102
120
  end
103
121
  end
104
122
 
123
+ def query
124
+ vector_model.query.where({ object_name => obj.id })
125
+ end
126
+
127
+ def object_name
128
+ obj.class.underscore_name # eg "product_brand"
129
+ end
130
+
131
+ def vector_model
132
+ @vector_model ||= Model.model_from_table(key.capitalize)
133
+ end
134
+
105
135
  def remove_record(rec)
106
136
  recs = obj.record[key].delete_if { |r| r == rec }
107
137
  obj.record[key] = recs
@@ -1,3 +1,3 @@
1
1
  module Groovy
2
- VERSION = '0.6.1'.freeze
2
+ VERSION = '0.6.6'.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.6
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-12-02 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