activekit 0.1.3 → 0.2.1

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: cf28e084bf1e87d21b36b3abcce5f3ebc902595a9635396f985c3935467e6b48
4
- data.tar.gz: ee2abf2cd217e523b35aa980dfa84072dee883dde5a2a782f864453740448321
3
+ metadata.gz: 67eb8f62ef1b3e4f6532c5431730922130158646b4067ed14511314777d18448
4
+ data.tar.gz: 11d7429b7011c2aca230935789489c4ac6828ff60e4cb250fe18910fd14d2daf
5
5
  SHA512:
6
- metadata.gz: f9848ed64694d379141ab41865234ac80f6042452be9a3a34ee2f94db61fa7a9c4e3bd564fb85c6029bd7e86379e773fc5b3945a7676ea4955489cd325a72bd4
7
- data.tar.gz: 8724f2eb4848df56dd589ad04e241c8a95a47d27b80e2d3c7bc1838f4b02a76e726115c27c03f449699c40ca34c8f50667254b1056408dea42bf02e5aafa5bc0
6
+ metadata.gz: 63885175b26d2bace31dd3ded154ab4631819645f4c2cc41653e57179968f03149f2f352c399c9693bb69bbc476e322f3c7ef67e9e4f159e956111bc478fde7c
7
+ data.tar.gz: fdd159ca5f30a970452036b5d8fd8eb384466dc908c166b7873144a2cee0f3c6cd8b671b23813db22073b389dbb916469576c8eef3cd0a44d1ca80e844507372
@@ -5,5 +5,13 @@ module ActiveKit
5
5
  store :value, accessors: [ :sequence ], coder: JSON
6
6
 
7
7
  validates :value, presence: true, length: { maximum: 1073741823, allow_blank: true }
8
+
9
+ before_validation :set_defaults
10
+
11
+ private
12
+
13
+ def set_defaults
14
+ self.sequence = { attributes: {} } if self.sequence.blank?
15
+ end
8
16
  end
9
17
  end
@@ -4,6 +4,7 @@ require "active_kit/sequence/sequenceable"
4
4
  module ActiveKit
5
5
  module Activekitable
6
6
  extend ActiveSupport::Concern
7
+ include ActiveKit::Loader
7
8
  include ActiveKit::Sequence::Sequenceable
8
9
 
9
10
  included do
@@ -0,0 +1,11 @@
1
+ module ActiveKit
2
+ class Activekiter
3
+ def initialize(current_class:)
4
+ @current_class = current_class
5
+ end
6
+
7
+ def sequence
8
+ @sequence ||= ActiveKit::Sequence::Sequence.new(current_class: @current_class)
9
+ end
10
+ end
11
+ end
@@ -8,8 +8,6 @@ module ActiveKit
8
8
 
9
9
  ActiveSupport.on_load(:active_record) do
10
10
  include ActiveKit::Activekitable
11
-
12
- has_one :activekit, as: :record, dependent: :destroy, class_name: "ActiveKit::Attribute"
13
11
  end
14
12
  end
15
13
  end
@@ -0,0 +1,23 @@
1
+ module ActiveKit
2
+ module Loader
3
+ def self.ensure_setup_for!(current_class:)
4
+ current_class.class_eval do
5
+ unless self.reflect_on_association :activekit_association
6
+ has_one :activekit_association, as: :record, dependent: :destroy, class_name: "ActiveKit::Attribute"
7
+
8
+ def activekit
9
+ @activekit ||= Relation.new(current_object: self)
10
+ end
11
+
12
+ def self.activekiter
13
+ @activekiter ||= Activekiter.new(current_class: self)
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ def self.ensure_has_one_association_for!(record:)
20
+ record.create_activekit_association unless record.activekit_association
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,7 @@
1
+ module ActiveKit
2
+ class Relation
3
+ def initialize(current_object:)
4
+ @current_object = current_object
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,38 @@
1
+ module ActiveKit
2
+ module Sequence
3
+ class Sequence
4
+ attr_reader :defined_attributes
5
+
6
+ def initialize(current_class:)
7
+ @current_class = current_class
8
+
9
+ @defined_attributes = {}
10
+ @wordbook = Wordbook.new
11
+ end
12
+
13
+ def update(record:, attribute_name:, position:)
14
+ ActiveKit::Loader.ensure_has_one_association_for!(record: record)
15
+
16
+ if position
17
+ raise "position '#{position}' is not a valid unsigned integer value greater than 0." unless position.is_a?(Integer) && position > 0
18
+
19
+ wordbook = Wordbook.new
20
+ word_for_position = wordbook.next_word(count: position)
21
+ # TODO: committer record for the attribute with given word_for_position should be found and resaved to recalculate its position.
22
+ # json_where = 'value->"$.sequence.attributes.' + attribute_name.to_s + '" = "' + word_for_position + '"'
23
+ # record_at_position = ActiveKit::Attribute.where(record_type: record.class.name).where(json_where).first&.record
24
+ record.activekit_association.sequence[:attributes][attribute_name.to_sym] = word_for_position
25
+ record.activekit_association.save!
26
+ # record_at_position.save! if record_at_position
27
+ else
28
+ record.activekit_association.sequence[:attributes][attribute_name.to_sym] = nil
29
+ record.activekit_association.save!
30
+ end
31
+ end
32
+
33
+ def add_attribute(name:, options:)
34
+ @defined_attributes.store(name, options)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -17,31 +17,25 @@ module ActiveKit
17
17
  # sequence_attribute :name, :positioning_method, updater: { via: :assoc, on: {} }
18
18
  # sequence_attribute :name, :positioning_method, updater: { via: {}, on: {} }
19
19
  # Note: :on and :via in :updater can accept nested associations.
20
- def sequence_attribute(name, positioning_method, **options)
20
+ def sequence_attribute(name, positioning_method = nil, **options)
21
+ ActiveKit::Loader.ensure_setup_for!(current_class: self)
22
+
21
23
  name = name.to_sym
22
- positioning_method = positioning_method.to_sym
24
+ options.store(:positioning_method, positioning_method&.to_sym)
23
25
  options.deep_symbolize_keys!
24
26
 
25
- unless self.respond_to?(:sequencer)
26
- define_singleton_method :sequencer do
27
- @sequencer ||= ActiveKit::Sequence::Sequencer.new(current_class: self)
28
- end
29
-
30
- # scope :order_sequence, -> (options_hash) { includes(:sequence_attributes).where(sequence_attributes: { name: name.to_s }).order("sequence_attributes.value": :asc) }
31
- end
32
-
33
- set_active_sequence_callbacks(attribute_name: name, positioning_method: positioning_method, updater: options.delete(:updater))
34
-
35
- sequencer.add_attribute(name: name, options: options)
27
+ set_active_sequence_callbacks(attribute_name: name, options: options)
28
+ activekiter.sequence.add_attribute(name: name, options: options)
36
29
  end
37
30
 
38
- def set_active_sequence_callbacks(attribute_name:, positioning_method:, updater:)
39
- updater = updater || {}
31
+ def set_active_sequence_callbacks(attribute_name:, options:)
32
+ positioning_method = options.dig(:positioning_method)
33
+ updater = options.dig(:updater) || {}
40
34
 
41
35
  if updater.empty?
42
- after_commit do
36
+ after_save do
43
37
  position = positioning_method ? self.public_send(positioning_method) : nil
44
- self.class.sequencer.update(record: self, attribute_name: attribute_name, position: position)
38
+ self.class.activekiter.sequence.update(record: self, attribute_name: attribute_name, position: position)
45
39
  logger.info "ActiveSequence - Sequencing from #{self.class.name}: Done."
46
40
  end
47
41
  else
@@ -52,20 +46,23 @@ module ActiveKit
52
46
  updater_via = updater.delete(:via)
53
47
  updater_on = updater.delete(:on) || updater
54
48
 
55
- base_klass = search_base_klass(self.class.name, updater_via)
56
- klass = reflected_klass(base_klass, updater_on.key)
49
+ base_klass = search_base_klass(self.name, updater_via)
50
+ klass = reflected_klass(base_klass, updater_on.keys.first)
57
51
  klass.constantize.class_eval do
58
- after_commit do
59
- inverse_assoc = search_inverse_assoc(self, updater_on)
52
+ after_save :activekit_sequence_sequenceable_callback
53
+ after_destroy :activekit_sequence_sequenceable_callback
54
+
55
+ private def activekit_sequence_sequenceable_callback
56
+ inverse_assoc = self.class.search_inverse_assoc(self, updater_on)
60
57
  position = positioning_method ? self.public_send(positioning_method) : nil
61
58
  if inverse_assoc.respond_to?(:each)
62
- inverse_assoc.each { |instance| instance.class.sequencer.update(record: instance, attribute_name: attribute_name, position: position) }
59
+ inverse_assoc.each { |instance| instance.class.activekiter.sequence.update(record: instance, attribute_name: attribute_name, position: position) }
63
60
  else
64
- inverse_assoc.class.sequencer.update(record: inverse_assoc, attribute_name: attribute_name, position: position)
61
+ inverse_assoc.class.activekiter.sequence.update(record: inverse_assoc, attribute_name: attribute_name, position: position)
65
62
  end
66
63
  logger.info "ActiveSequence - Sequencing from #{self.class.name}: Done."
67
64
  end
68
- end
65
+ end
69
66
  end
70
67
  end
71
68
 
@@ -75,8 +72,8 @@ module ActiveKit
75
72
  elsif updater_via.is_a? Symbol
76
73
  reflected_klass(classname, updater_via)
77
74
  elsif updater_via.is_a? Hash
78
- klass = reflected_klass(classname, updater_via.key)
79
- updater_via.value.is_a?(Hash) ? search_base_klass(klass, updater_via.value) : reflected_klass(klass, updater_via.value)
75
+ klass = reflected_klass(classname, updater_via.keys.first)
76
+ updater_via.values.first.is_a?(Hash) ? search_base_klass(klass, updater_via.values.first) : reflected_klass(klass, updater_via.values.first)
80
77
  end
81
78
  end
82
79
 
@@ -87,11 +84,11 @@ module ActiveKit
87
84
  end
88
85
 
89
86
  def search_inverse_assoc(klass_object, updater_on)
90
- if updater_on.value.is_a?(Hash)
91
- klass_object = klass_object.public_send(updater_on.value.key)
92
- search_inverse_assoc(klass_object, updater_on.value)
87
+ if updater_on.values.first.is_a?(Hash)
88
+ klass_object = klass_object.public_send(updater_on.values.first.keys.first)
89
+ search_inverse_assoc(klass_object, updater_on.values.first)
93
90
  else
94
- klass_object.public_send(updater_on.value)
91
+ klass_object.public_send(updater_on.values.first)
95
92
  end
96
93
  end
97
94
  end
@@ -2,7 +2,7 @@ module ActiveKit
2
2
  module Sequence
3
3
  extend ActiveSupport::Autoload
4
4
 
5
- autoload :Sequencer
5
+ autoload :Sequence
6
6
  autoload :Wordbook
7
7
  end
8
8
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveKit
2
- VERSION = '0.1.3'
2
+ VERSION = '0.2.1'
3
3
  end
data/lib/active_kit.rb CHANGED
@@ -4,5 +4,8 @@ require "active_kit/engine"
4
4
  module ActiveKit
5
5
  extend ActiveSupport::Autoload
6
6
 
7
+ autoload :Activekiter
8
+ autoload :Loader
9
+ autoload :Relation
7
10
  autoload :Sequence
8
11
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activekit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - plainsource
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-16 00:00:00.000000000 Z
11
+ date: 2023-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -53,10 +53,13 @@ files:
53
53
  - db/migrate/20231016050208_create_active_kit_attributes.rb
54
54
  - lib/active_kit.rb
55
55
  - lib/active_kit/activekitable.rb
56
+ - lib/active_kit/activekiter.rb
56
57
  - lib/active_kit/engine.rb
58
+ - lib/active_kit/loader.rb
59
+ - lib/active_kit/relation.rb
57
60
  - lib/active_kit/sequence.rb
61
+ - lib/active_kit/sequence/sequence.rb
58
62
  - lib/active_kit/sequence/sequenceable.rb
59
- - lib/active_kit/sequence/sequencer.rb
60
63
  - lib/active_kit/sequence/wordbook.rb
61
64
  - lib/active_kit/version.rb
62
65
  - lib/activekit.rb
@@ -1,69 +0,0 @@
1
- module ActiveKit
2
- module Sequence
3
- class Sequencer
4
- attr_reader :defined_attributes
5
-
6
- def initialize(current_class:)
7
- @current_class = current_class
8
-
9
- @defined_attributes = {}
10
- @wordbook = Wordbook.new
11
- end
12
-
13
- def update(record:, attribute_name:, position:)
14
- attribute = Attribute.find_by(record: record, name: attribute_name)
15
- Attribute.create!(record: record, name: attribute_name) unless attribute
16
-
17
- if position
18
- raise "position '#{position}' is not a valid unsigned integer value greater than 0." unless position.is_a?(Integer) && position > 0
19
-
20
- attribute_at_position = Attribute.where(record_type: record.class.name, name: attribute_name).order(value: :asc).offset(position - 1).limit(1)
21
- attribute
22
-
23
- wordbook = Wordbook.new
24
- total_position_count = Attribute.where(record_type: record.class.name, name: attribute_name).order(value: :asc).count
25
- if attribute == attribute_at_position(position)
26
- return
27
- elsif position == 1
28
-
29
- elsif position == total_position_count
30
- wordbook.bookmark = attribute_at_position(total_position_count).value
31
-
32
- attribute.value = wordbook.next_word if wordbook.next_word?
33
- attribute.save!
34
- elsif position > total_position_count
35
- maximum_word = Attribute.where(record_type: record.class.name, name: attribute_name).maximum(:value)
36
-
37
- wordbook.bookmark = maximum_word
38
- attribute.value = wordbook.next_word if wordbook.next_word?
39
- attribute.save!
40
- elsif position < total_position_count
41
-
42
- end
43
- end
44
- end
45
-
46
- def rebalance_from(position:)
47
- ActiveRecord::Base.transaction do
48
- wordbook = Wordbook.new
49
- Attribute.where(record_type: record.class.name, name: attribute_name).order(value: :asc).offset(position - 1).limit(1)
50
- Attribute.where(record_type: record.class.name, name: attribute_name).order(value: :asc).offset(position - 1).each do |attribute|
51
- wordbook.bookmark = attribute.value
52
- raise "Could not find next word in wordbook while rebalancing" unless wordbook.next_word?
53
-
54
- attribute.value = wordbook.next_word
55
- attribute.save!
56
- end
57
- end
58
- end
59
-
60
- def attribute_at_position(position)
61
- Attribute.where(record_type: record.class.name, name: attribute_name).order(value: :asc).offset(position - 1).limit(1)
62
- end
63
-
64
- def add_attribute(name:, options:)
65
- @defined_attributes.store(name, options)
66
- end
67
- end
68
- end
69
- end