groovy 0.6.0 → 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: e3e87c857a45ab045967ed1af60d619ec7340fc751c950032645657049bd1c68
4
- data.tar.gz: 4886e8db94b7fa3627312f82842d141b9c24ea7954e59955f4840dab7f70f776
3
+ metadata.gz: a622cdd3453e0fee1feeebf798cbcec9f0ef9f68759aec64f65c3d4a500f52af
4
+ data.tar.gz: be1220642bf299a61ad5900bea1fffa3359725575def843ce233290e26a5b8e2
5
5
  SHA512:
6
- metadata.gz: df4c68551089cf9ade2f20ac70436962fff82c0a83db5caeb0208b107a1215443386ab429d644c939200fef844a70db315c502cfb4c901aff7c809f578a07b67
7
- data.tar.gz: 8eb292ae1c4365040a62c4f9feb8e04b27119ae5b4d84b1adf0640cb7f1d46cbea29bafd57ad076ca0d0f593fae504f4bb6eb4afac392b530f725f82948b8ad1
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)
@@ -26,13 +30,23 @@ module Groovy
26
30
  # end
27
31
 
28
32
  def self.model_from_table(table_name)
29
- Kernel.const_get(table_name.to_s.sub(/ies$/, 'y').sub(/s$/, '').classify)
33
+ get_class(table_name.to_s.sub(/ies$/, 'y').sub(/s$/, ''))
30
34
  end
31
35
 
32
36
  def self.included(base)
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
44
+ end
45
+
46
+ def self.get_class(table_name)
47
+ # classify method
48
+ classified = table_name.gsub(/([A-Z])/, '_\1').split('_').collect! { |w| w.capitalize }.join
49
+ Kernel.const_get(classified)
36
50
  end
37
51
 
38
52
  module ClassMethods
@@ -86,6 +100,12 @@ module Groovy
86
100
  add_accessors_for(name)
87
101
  end
88
102
 
103
+ def add_reference(name, table_name, options = {})
104
+ schema.reference(name, table_name, options)
105
+ schema.sync
106
+ add_accessors_for(name)
107
+ end
108
+
89
109
  def add_accessors_for(col, s = schema)
90
110
  if s.attribute_columns.include?(col)
91
111
  add_attr_accessors(col)
@@ -242,10 +262,14 @@ module Groovy
242
262
  @callbacks ||= {}
243
263
  end
244
264
 
245
- [:before_create, :after_create].each do |event|
246
- define_method(event) do |*method_names|
247
- callbacks[:before_create] ||= []
248
- 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
249
273
  end
250
274
  end
251
275
 
@@ -295,6 +319,7 @@ module Groovy
295
319
 
296
320
  def initialize(attrs = nil, record = nil, key = nil)
297
321
  @attributes, @vectors, @_key = {}, {}, key # key is used on creation only
322
+ @foreign_refs_to_update = {}
298
323
 
299
324
  if set_record(record)
300
325
  # load_attributes_from_record(record)
@@ -333,6 +358,10 @@ module Groovy
333
358
  # _key.nil?
334
359
  end
335
360
 
361
+ def persisted?
362
+ !new_record?
363
+ end
364
+
336
365
  def [](key)
337
366
  k = key.to_sym
338
367
  @attributes[k] = get_record_attribute(k) unless @attributes.key?(k)
@@ -366,6 +395,18 @@ module Groovy
366
395
  obj.each { |k,v| public_send("#{k}=", v) }
367
396
  end
368
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
+
369
410
  def update_attributes(obj)
370
411
  set_attributes(obj)
371
412
  return false if respond_to?(:invalid?) and invalid?
@@ -378,7 +419,11 @@ module Groovy
378
419
 
379
420
  def save(options = {})
380
421
  return false if respond_to?(:invalid?) and invalid?
381
- new_record? ? create : update
422
+ if new_record?
423
+ create && true
424
+ else
425
+ update && true
426
+ end
382
427
  end
383
428
 
384
429
  def save!(options = {})
@@ -407,6 +452,7 @@ module Groovy
407
452
 
408
453
  @attributes = {}
409
454
  @changes = {}
455
+ @foreign_refs_to_update = {}
410
456
  self
411
457
  end
412
458
 
@@ -424,6 +470,7 @@ module Groovy
424
470
 
425
471
  private
426
472
 
473
+
427
474
  def get_record_attribute(key)
428
475
  return if record.nil?
429
476
  val = record[key]
@@ -460,12 +507,26 @@ module Groovy
460
507
  end
461
508
  end
462
509
 
463
- def set_ref(name, obj)
464
- 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
465
526
  set_attribute(name, obj.id) # obj should be a groovy model or groonga record
466
527
  else
467
- obj = obj.record if obj.respond_to?(:record)
468
- record[name] = obj
528
+ val = obj.respond_to?(:record) ? obj.record : obj
529
+ record[name] = val
469
530
  end
470
531
  end
471
532
 
@@ -477,17 +538,28 @@ module Groovy
477
538
  def create
478
539
  fire_callbacks(:before_create)
479
540
  set_record(self.class.insert(@attributes, @_key))
541
+ update_foreign_refs
480
542
  fire_callbacks(:after_create)
481
543
  self
482
544
  end
483
545
 
484
546
  def fire_callbacks(name)
547
+ # puts "Firing #{name} callbacks"
485
548
  if arr = self.class.callbacks[name] and arr.any?
486
- 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)
487
558
  end
488
559
  end
489
560
 
490
561
  def update
562
+ fire_callbacks(:before_update)
491
563
  ensure_persisted!
492
564
  changes.each do |key, values|
493
565
  # puts "Updating #{key} from #{values[0]} to #{values[1]}"
@@ -495,9 +567,22 @@ module Groovy
495
567
  end
496
568
  self.class.set_timestamp(record, :updated_at)
497
569
  changes = {}
570
+ update_foreign_refs
571
+ fire_callbacks(:after_update)
498
572
  self
499
573
  end
500
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
+
501
586
  def ensure_persisted!
502
587
  raise "Not persisted" if new_record?
503
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
 
@@ -108,9 +137,11 @@ module Groovy
108
137
 
109
138
  ensure_created!
110
139
  remove_columns_not_in(@spec.keys)
140
+
111
141
  @spec.each do |col, spec|
112
142
  check_and_add_column(col, spec[:type], spec[:args])
113
143
  end
144
+
114
145
  @index_columns.each do |col|
115
146
  add_index_on(col)
116
147
  end
@@ -186,15 +217,15 @@ module Groovy
186
217
  end
187
218
  end
188
219
 
189
- def create_table!
190
- log "Creating table!"
191
- 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)
192
223
  end
193
224
 
194
- def remove_table!
195
- log "Removing table!"
196
- Groonga::Schema.remove_table(table_name, context: context)
197
- @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
198
229
  end
199
230
 
200
231
  def get_names(columns)
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|
@@ -80,6 +101,10 @@ module Groovy
80
101
  end
81
102
  end
82
103
 
104
+ def ==(arr)
105
+ to_a == arr
106
+ end
107
+
83
108
  private
84
109
  attr_reader :obj, :key
85
110
 
@@ -1,3 +1,3 @@
1
1
  module Groovy
2
- VERSION = '0.6.0'.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
@@ -0,0 +1,68 @@
1
+ require_relative './spec_helper'
2
+
3
+ describe Groovy::Model do
4
+
5
+ before :all do
6
+ Groovy.open('tmp/vector', 'vector_spec')
7
+ load_schema! 'vector_spec'
8
+ end
9
+
10
+ after :all do
11
+ Groovy.close('vector_spec')
12
+ end
13
+
14
+ def load_vector_schema!
15
+ klass = Class.new
16
+ Object.const_set("Comment", klass)
17
+ Comment.class_eval do
18
+ include Groovy::Model
19
+ schema(context: 'vector_spec') do |t|
20
+ t.string :content
21
+ t.timestamps
22
+ end
23
+ end
24
+
25
+ klass = Class.new
26
+ Object.const_set("User", klass)
27
+ User.class_eval do
28
+ include Groovy::Model
29
+ schema(context: 'vector_spec') do |t|
30
+ t.string :name
31
+ t.timestamps
32
+ end
33
+ end
34
+
35
+ Comment.add_reference :author, "Users"
36
+ User.add_reference :comments, "Comments", type: :vector
37
+ end
38
+
39
+ describe 'relations' do
40
+
41
+ before do
42
+ load_vector_schema!
43
+ end
44
+
45
+ it 'loads records and adds reverse relations automatically' do
46
+ user = User.new(name: 'John')
47
+ expect(user.save).to eq(true)
48
+ comment = Comment.new(author: user, content: 'Hello there!')
49
+ expect(comment.save).to eq(true)
50
+ # user.comments << comment
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([])
64
+ end
65
+
66
+ end
67
+
68
+ 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.0
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-27 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,11 +97,13 @@ 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
103
104
  - spec/search_spec.rb
104
105
  - spec/spec_helper.rb
106
+ - spec/vector_spec.rb
105
107
  homepage: https://github.com/tomas/groovy
106
108
  licenses: []
107
109
  metadata: {}