activekit 0.1.3 → 0.2.1

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