groovy 0.6.0 → 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: 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: {}