hoardable 0.11.0 → 0.12.2

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: 2d9f9ea3a4559d3a4515357eabc0c7ee05526ee1308a4e6eef111f2d6207aef6
4
- data.tar.gz: be0011cb1ea9d3fcdcd5d1a4f4951d7185371cc1d05c91dfd27ea163b59620b7
3
+ metadata.gz: eec6cda69bb2bbb1aeec6a0c460881c33abbe1de123ef9178d2c16378374b2bd
4
+ data.tar.gz: f3f57007e08f958017483dfffd0275b5b70c0ef1522984ea78da4c595a9e5506
5
5
  SHA512:
6
- metadata.gz: 20ebc8d7ec2924e43e6bbd806e7aaf08350653b695dc4aef16de28359ceb95d8de16da4c19790e8b1212bc8d8d202caeb3614e05b326977bd470defe48e4b731
7
- data.tar.gz: fc3fa92ae0ab25bee4122456b889f86eec75b0eee4efba742dced8737d7bc9ab3a99e63e63839f49089183900df8ed54a315a4561aad634c0e2ea3fa56aa8732
6
+ metadata.gz: 04a13fec8198d4c4fda05b5364a7925755e7f2708004e15a564f243b64836577f4393cb502e7dd7d88ea123df29909ccb36a1d603cae8b2e711958250dc45763
7
+ data.tar.gz: 34a59cb91ed643784e4aa6ab88abac2d3e7a59a59945bcdce6e849aa441bc1490a6870c2661f65c31d246e4cb87bb39c4502e6cb8ce05cb3c42a6ad0e56f1140
data/README.md CHANGED
@@ -84,9 +84,9 @@ of that model. As we continue our example from above:
84
84
  ```
85
85
  $ irb
86
86
  >> Post
87
- => Post(id: integer, body: text, user_id: integer, created_at: datetime)
87
+ => Post(id: integer, body: text, user_id: integer, created_at: datetime, hoardable_id: integer)
88
88
  >> PostVersion
89
- => PostVersion(id: integer, body: text, user_id: integer, created_at: datetime, _data: jsonb, _during: tsrange, hoardable_source_id: integer)
89
+ => PostVersion(id: integer, body: text, user_id: integer, created_at: datetime, hoardable_id: integer, _data: jsonb, _during: tsrange)
90
90
  ```
91
91
 
92
92
  A `Post` now `has_many :versions`. With the default configuration, whenever an update and deletion
@@ -144,7 +144,7 @@ If you want to look-up the version of a record at a specific time, you can use t
144
144
  ```ruby
145
145
  post.at(1.day.ago) # => #<PostVersion>
146
146
  # or you can use the scope on the version model class
147
- PostVersion.at(1.day.ago).find_by(hoardable_source_id: post.id) # => #<PostVersion>
147
+ PostVersion.at(1.day.ago).find_by(hoardable_id: post.id) # => #<PostVersion>
148
148
  ```
149
149
 
150
150
  The source model class also has an `.at` method:
@@ -357,7 +357,7 @@ Hoardable.at(datetime) do
357
357
  post.comments.size # => 2
358
358
  post.id # => 2
359
359
  post.version? # => true
360
- post.hoardable_source_id # => 1
360
+ post.hoardable_id # => 1
361
361
  end
362
362
  ```
363
363
 
@@ -370,7 +370,7 @@ version, even though it is masquerading as a `Post`.
370
370
 
371
371
  If you are ever unsure if a Hoardable record is a "source" or a "version", you can be sure by
372
372
  calling `version?` on it. If you want to get the true original source record ID, you can call
373
- `hoardable_source_id`.
373
+ `hoardable_id`.
374
374
 
375
375
  Sometimes you’ll trash something that `has_many :children, dependent: :destroy` and want
376
376
  to untrash everything in a similar dependent manner. Whenever a hoardable version is created in a
@@ -393,7 +393,7 @@ class Post < ActiveRecord::Base
393
393
  end
394
394
  ```
395
395
 
396
- ## ActionText
396
+ ## Action Text
397
397
 
398
398
  Hoardable provides support for ActiveRecord models with `has_rich_text`. First, you must create a
399
399
  temporal table for `ActionText::RichText`:
@@ -420,9 +420,9 @@ post = Post.create!(content: '<div>Hello World</div>')
420
420
  datetime = DateTime.current
421
421
  post.update!(content: '<div>Goodbye Cruel World</div>')
422
422
  post.content.versions.size # => 1
423
- assert_equal post.content.to_plain_text, 'Goodbye Cruel World'
423
+ post.content.to_plain_text # => 'Goodbye Cruel World'
424
424
  Hoardable.at(datetime) do
425
- assert_equal post.content.to_plain_text, 'Hello World'
425
+ post.content.to_plain_text # => 'Hello World'
426
426
  end
427
427
  ```
428
428
 
@@ -23,7 +23,7 @@ module Hoardable
23
23
  end
24
24
 
25
25
  def create_migration_file
26
- migration_template 'functions.rb.erb', 'db/migrate/install_hoardable.rb'
26
+ migration_template 'install.rb.erb', 'db/migrate/install_hoardable.rb'
27
27
  end
28
28
 
29
29
  def self.next_migration_number(dir)
@@ -12,7 +12,7 @@ module Hoardable
12
12
  class_option :foreign_key_type, type: :string
13
13
 
14
14
  def create_versions_table
15
- migration_template migration_template_name, "db/migrate/create_#{singularized_table_name}_versions.rb"
15
+ migration_template 'migration.rb.erb', "db/migrate/create_#{singularized_table_name}_versions.rb"
16
16
  end
17
17
 
18
18
  no_tasks do
@@ -23,12 +23,10 @@ module Hoardable
23
23
  'bigint'
24
24
  end
25
25
 
26
- def migration_template_name
27
- if Gem::Version.new(ActiveRecord::Migration.current_version.to_s) < Gem::Version.new('7')
28
- 'migration_6.rb.erb'
29
- else
30
- 'migration.rb.erb'
31
- end
26
+ def primary_key
27
+ options[:primary_key] || class_name.singularize.constantize.primary_key
28
+ rescue StandardError
29
+ 'id'
32
30
  end
33
31
 
34
32
  def singularized_table_name
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ class InstallHoardable < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
4
+ def up
5
+ execute(
6
+ <<~SQL
7
+ DO $$
8
+ BEGIN
9
+ IF NOT EXISTS (
10
+ SELECT 1 FROM pg_type t WHERE t.typname = 'hoardable_operation'
11
+ ) THEN
12
+ CREATE TYPE hoardable_operation AS ENUM ('update', 'delete', 'insert');
13
+ END IF;
14
+ END
15
+ $$;
16
+ CREATE OR REPLACE FUNCTION hoardable_source_set_id() RETURNS trigger
17
+ LANGUAGE plpgsql AS
18
+ $$
19
+ DECLARE
20
+ _pk information_schema.constraint_column_usage.column_name%TYPE;
21
+ _id _pk%TYPE;
22
+ BEGIN
23
+ SELECT c.column_name
24
+ FROM information_schema.table_constraints t
25
+ JOIN information_schema.constraint_column_usage c
26
+ ON c.constraint_name = t.constraint_name
27
+ WHERE c.table_name = TG_TABLE_NAME AND t.constraint_type = 'PRIMARY KEY'
28
+ LIMIT 1
29
+ INTO _pk;
30
+ EXECUTE format('SELECT $1.%I', _pk) INTO _id USING NEW;
31
+ NEW.hoardable_id = _id;
32
+ RETURN NEW;
33
+ END;$$;
34
+ CREATE OR REPLACE FUNCTION hoardable_version_prevent_update() RETURNS trigger
35
+ LANGUAGE plpgsql AS
36
+ $$BEGIN
37
+ RAISE EXCEPTION 'updating a version is not allowed';
38
+ RETURN NEW;
39
+ END;$$;
40
+ SQL
41
+ )
42
+ end
43
+ def down
44
+ execute(
45
+ <<~SQL
46
+ DROP TYPE IF EXISTS hoardable_operation;
47
+ DROP FUNCTION IF EXISTS hoardable_version_prevent_update();
48
+ DROP FUNCTION IF EXISTS hoardable_source_set_id();
49
+ SQL
50
+ )
51
+ end
52
+ end
@@ -2,21 +2,25 @@
2
2
 
3
3
  class Create<%= class_name.singularize.delete(':') %>Versions < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
4
4
  def change
5
- create_enum :hoardable_operation, %w[update delete insert]
5
+ add_column :<%= table_name %>, :hoardable_id, :<%= foreign_key_type %>
6
+ add_index :<%= table_name %>, :hoardable_id
6
7
  create_table :<%= singularized_table_name %>_versions, id: false, options: 'INHERITS (<%= table_name %>)' do |t|
7
8
  t.jsonb :_data
8
9
  t.tsrange :_during, null: false
9
10
  t.uuid :_event_uuid, null: false, index: true
10
- t.enum :_operation, enum_type: 'hoardable_operation', null: false, index: true
11
- t.<%= foreign_key_type %> :hoardable_source_id, null: false, index: true
11
+ t.column :_operation, :hoardable_operation, null: false, index: true
12
12
  end
13
13
  reversible do |dir|
14
14
  dir.up do
15
15
  execute(
16
16
  <<~SQL
17
+ UPDATE <%= table_name %> SET hoardable_id = <%= primary_key %>;
17
18
  CREATE TRIGGER <%= singularized_table_name %>_versions_prevent_update
18
19
  BEFORE UPDATE ON <%= singularized_table_name %>_versions FOR EACH ROW
19
20
  EXECUTE PROCEDURE hoardable_version_prevent_update();
21
+ CREATE TRIGGER <%= table_name %>_set_hoardable_id
22
+ BEFORE INSERT ON <%= table_name %> FOR EACH ROW
23
+ EXECUTE PROCEDURE hoardable_source_set_id();
20
24
  SQL
21
25
  )
22
26
  end
@@ -25,14 +29,17 @@ class Create<%= class_name.singularize.delete(':') %>Versions < ActiveRecord::Mi
25
29
  <<~SQL
26
30
  DROP TRIGGER <%= singularized_table_name %>_versions_prevent_update
27
31
  ON <%= singularized_table_name %>_versions;
32
+ DROP TRIGGER <%= table_name %>_set_hoardable_id
33
+ ON <%= table_name %>;
28
34
  SQL
29
35
  )
30
36
  end
31
37
  end
32
- add_index(:<%= singularized_table_name %>_versions, :id, unique: true)
38
+ change_column_null :<%= table_name %>, :hoardable_id, false
39
+ add_index(:<%= singularized_table_name %>_versions, :<%= primary_key %>, unique: true)
33
40
  add_index(
34
41
  :<%= singularized_table_name %>_versions,
35
- %i[_during hoardable_source_id],
42
+ %i[_during hoardable_id],
36
43
  name: 'idx_<%= singularized_table_name %>_versions_temporally'
37
44
  )
38
45
  end
@@ -12,7 +12,6 @@ module Hoardable
12
12
  include HasOne
13
13
  include BelongsTo
14
14
  include HasRichText
15
- include HasOneAttached
16
15
  end
17
16
  end
18
17
  end
@@ -21,7 +21,7 @@ module Hoardable
21
21
  define_method("trashed_#{name}") do
22
22
  source_reflection = reflections[name.to_s]
23
23
  source_reflection.version_class.trashed.only_most_recent.find_by(
24
- hoardable_source_id: source_reflection.foreign_key
24
+ hoardable_id: source_reflection.foreign_key
25
25
  )
26
26
  end
27
27
 
@@ -13,30 +13,25 @@ module Hoardable
13
13
  delegate :version_class, to: :source_record
14
14
 
15
15
  def insert_hoardable_version(operation, &block)
16
- version = version_class.insert(initialize_version_attributes(operation), returning: :id)
17
- version_id = version[0]['id']
16
+ version = version_class.insert(initialize_version_attributes(operation), returning: source_primary_key.to_sym)
17
+ version_id = version[0][source_primary_key]
18
18
  source_record.instance_variable_set('@hoardable_version', version_class.find(version_id))
19
19
  source_record.run_callbacks(:versioned, &block)
20
20
  end
21
21
 
22
- def find_or_initialize_hoardable_event_uuid
23
- Thread.current[:hoardable_event_uuid] ||= ActiveRecord::Base.connection.query('SELECT gen_random_uuid();')[0][0]
24
- end
25
-
26
- def hoardable_version_source_id
27
- @hoardable_version_source_id ||= query_hoardable_version_source_id
22
+ def source_primary_key
23
+ source_record.class.primary_key
28
24
  end
29
25
 
30
- def query_hoardable_version_source_id
31
- primary_key = source_record.class.primary_key
32
- version_class.where(primary_key => source_record.read_attribute(primary_key)).pluck('hoardable_source_id')[0]
26
+ def find_or_initialize_hoardable_event_uuid
27
+ Thread.current[:hoardable_event_uuid] ||= ActiveRecord::Base.connection.query('SELECT gen_random_uuid();')[0][0]
33
28
  end
34
29
 
35
30
  def initialize_version_attributes(operation)
36
- source_record.attributes_before_type_cast.without('id').merge(
31
+ source_attributes_without_primary_key.merge(
37
32
  source_record.changes.transform_values { |h| h[0] },
38
33
  {
39
- 'hoardable_source_id' => source_record.id,
34
+ 'hoardable_id' => source_record.id,
40
35
  '_event_uuid' => find_or_initialize_hoardable_event_uuid,
41
36
  '_operation' => operation,
42
37
  '_data' => initialize_hoardable_data.merge(changes: source_record.changes),
@@ -45,6 +40,10 @@ module Hoardable
45
40
  )
46
41
  end
47
42
 
43
+ def source_attributes_without_primary_key
44
+ source_record.attributes_before_type_cast.without(source_primary_key)
45
+ end
46
+
48
47
  def initialize_temporal_range
49
48
  ((previous_temporal_tsrange_end || hoardable_source_epoch)..Time.now.utc)
50
49
  end
@@ -105,11 +105,5 @@ module Hoardable
105
105
  require_relative 'encrypted_rich_text' if SUPPORTS_ENCRYPTED_ACTION_TEXT
106
106
  end
107
107
  end
108
-
109
- initializer 'hoardable.active_storage' do
110
- ActiveSupport.on_load(:active_storage_attachment) do
111
- require_relative 'attachment'
112
- end
113
- end
114
108
  end
115
109
  end
@@ -7,18 +7,18 @@ module Hoardable
7
7
  # with the class method +hoardable+.
8
8
  module FinderMethods
9
9
  def find_one(id)
10
- super(hoardable_source_ids([id])[0])
10
+ super(hoardable_ids([id])[0])
11
11
  end
12
12
 
13
13
  def find_some(ids)
14
- super(hoardable_source_ids(ids))
14
+ super(hoardable_ids(ids))
15
15
  end
16
16
 
17
17
  private
18
18
 
19
- def hoardable_source_ids(ids)
19
+ def hoardable_ids(ids)
20
20
  ids.map do |id|
21
- version_class.where(hoardable_source_id: id).select(primary_key).ids[0] || id
21
+ version_class.where(hoardable_id: id).select(primary_key).ids[0] || id
22
22
  end
23
23
  end
24
24
  end
@@ -5,7 +5,7 @@ module Hoardable
5
5
  module HasMany
6
6
  extend ActiveSupport::Concern
7
7
 
8
- # An +ActiveRecord+ extension that allows looking up {VersionModel}s by +hoardable_source_id+ as
8
+ # An +ActiveRecord+ extension that allows looking up {VersionModel}s by +hoardable_id+ as
9
9
  # if they were {SourceModel}s when using {Hoardable#at}.
10
10
  module HasManyExtension
11
11
  def scope
@@ -15,9 +15,13 @@ module Hoardable
15
15
  private
16
16
 
17
17
  def hoardable_scope
18
- if Hoardable.instance_variable_get('@at') &&
19
- (hoardable_source_id = @association.owner.hoardable_source_id)
20
- @association.scope.rewhere(@association.reflection.foreign_key => hoardable_source_id)
18
+ if Hoardable.instance_variable_get('@at') && (hoardable_id = @association.owner.hoardable_id)
19
+ if @association.reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
20
+ @association.reflection.source_reflection.instance_variable_set(
21
+ '@active_record_primary_key', 'hoardable_id'
22
+ )
23
+ end
24
+ @association.scope.rewhere(@association.reflection.foreign_key => hoardable_id)
21
25
  else
22
26
  @association.scope
23
27
  end
@@ -62,10 +62,10 @@ module Hoardable
62
62
  scope :at, lambda { |datetime|
63
63
  raise(CreatedAtColumnMissingError, @klass.table_name) unless @klass.column_names.include?('created_at')
64
64
 
65
- include_versions.where(id: version_class.at(datetime).select('id')).or(
65
+ include_versions.where(id: version_class.at(datetime).select(@klass.primary_key)).or(
66
66
  exclude_versions
67
67
  .where("#{table_name}.created_at < ?", datetime)
68
- .where.not(id: version_class.select(:hoardable_source_id).where(DURING_QUERY, datetime))
68
+ .where.not(id: version_class.select(:hoardable_id).where(DURING_QUERY, datetime))
69
69
  ).hoardable
70
70
  }
71
71
  end
@@ -32,8 +32,6 @@ module Hoardable
32
32
  include Scopes
33
33
 
34
34
  around_update(if: [HOARDABLE_CALLBACKS_ENABLED, HOARDABLE_VERSION_UPDATES]) do |_, block|
35
- next if self.is_a?(Hoardable::Attachment)
36
-
37
35
  hoardable_client.insert_hoardable_version('update', &block)
38
36
  end
39
37
 
@@ -53,7 +51,7 @@ module Hoardable
53
51
  dependent: nil,
54
52
  class_name: version_class.to_s,
55
53
  inverse_of: :hoardable_source,
56
- foreign_key: :hoardable_source_id
54
+ foreign_key: :hoardable_id
57
55
  )
58
56
  end
59
57
 
@@ -62,7 +60,7 @@ module Hoardable
62
60
  #
63
61
  # @return [Boolean]
64
62
  def trashed?
65
- versions.trashed.only_most_recent.first&.hoardable_source_id == id
63
+ !self.class.exists?(self.class.primary_key => id)
66
64
  end
67
65
 
68
66
  # Returns a boolean of whether the record is actually a +version+ cast as an instance of the
@@ -70,7 +68,7 @@ module Hoardable
70
68
  #
71
69
  # @return [Boolean]
72
70
  def version?
73
- !!hoardable_client.hoardable_version_source_id
71
+ hoardable_id != id
74
72
  end
75
73
 
76
74
  # Returns the +version+ at the supplied +datetime+ or +time+, or +self+ if there is none.
@@ -100,13 +98,8 @@ module Hoardable
100
98
  version.is_a?(version_class) ? version.revert! : self
101
99
  end
102
100
 
103
- # Returns the +hoardable_source_id+ that represents the original {SourceModel} record’s ID. Will
104
- # return nil if the current {SourceModel} record is not an instance of a {VersionModel} cast as
105
- # {SourceModel}.
106
- #
107
- # @return [Integer, nil]
108
- def hoardable_source_id
109
- hoardable_client.hoardable_version_source_id || id
101
+ def hoardable_id
102
+ read_attribute('hoardable_id')
110
103
  end
111
104
 
112
105
  delegate :version_class, to: :class
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hoardable
4
- VERSION = '0.11.0'
4
+ VERSION = '0.12.2'
5
5
  end
@@ -7,6 +7,13 @@ module Hoardable
7
7
  extend ActiveSupport::Concern
8
8
 
9
9
  class_methods do
10
+ # This is needed to allow {FinderMethods} to work with the version class.
11
+ #
12
+ # @!visibility private
13
+ def version_class
14
+ self
15
+ end
16
+
10
17
  # This is needed to omit the pseudo row of 'tableoid' when using +ActiveRecord+’s +insert+.
11
18
  #
12
19
  # @!visibility private
@@ -20,6 +27,7 @@ module Hoardable
20
27
  belongs_to(
21
28
  :hoardable_source,
22
29
  inverse_of: :versions,
30
+ foreign_key: :hoardable_id,
23
31
  class_name: superclass.model_name
24
32
  )
25
33
 
@@ -37,7 +45,7 @@ module Hoardable
37
45
  # Returns only trashed +versions+ that are currently orphans.
38
46
  scope :trashed, lambda {
39
47
  left_outer_joins(:hoardable_source)
40
- .where(superclass.table_name => { id: nil })
48
+ .where(superclass.table_name => { superclass.primary_key => nil })
41
49
  .where(_operation: 'delete')
42
50
  }
43
51
 
@@ -78,7 +86,7 @@ module Hoardable
78
86
 
79
87
  transaction do
80
88
  hoardable_source.tap do |reverted|
81
- reverted.update!(hoardable_source_attributes.without('id'))
89
+ reverted.update!(hoardable_source_attributes.without(self.class.superclass.primary_key))
82
90
  reverted.instance_variable_set(:@hoardable_version, self)
83
91
  reverted.run_callbacks(:reverted)
84
92
  end
@@ -113,24 +121,16 @@ module Hoardable
113
121
  _data&.dig('changes')
114
122
  end
115
123
 
116
- # Returns the ID of the {SourceModel} that created this {VersionModel}
117
- def hoardable_source_id
118
- read_attribute('hoardable_source_id')
119
- end
120
-
121
124
  private
122
125
 
123
126
  def insert_untrashed_source
124
127
  superscope = self.class.superclass.unscoped
125
- superscope.insert(hoardable_source_attributes.merge('id' => hoardable_source_id))
126
- superscope.find(hoardable_source_id)
128
+ superscope.insert(hoardable_source_attributes.merge(superscope.primary_key => hoardable_id))
129
+ superscope.find(hoardable_id)
127
130
  end
128
131
 
129
132
  def hoardable_source_attributes
130
- @hoardable_source_attributes ||=
131
- attributes_before_type_cast
132
- .without('hoardable_source_id')
133
- .reject { |k, _v| k.start_with?('_') }
133
+ attributes_before_type_cast.without(self.class.column_names - self.class.superclass.column_names)
134
134
  end
135
135
  end
136
136
  end
data/lib/hoardable.rb CHANGED
@@ -15,6 +15,5 @@ require_relative 'hoardable/has_many'
15
15
  require_relative 'hoardable/belongs_to'
16
16
  require_relative 'hoardable/has_one'
17
17
  require_relative 'hoardable/has_rich_text'
18
- require_relative 'hoardable/has_one_attached'
19
18
  require_relative 'generators/hoardable/migration_generator'
20
19
  require_relative 'generators/hoardable/install_generator'
data/sig/hoardable.rbs CHANGED
@@ -8,6 +8,7 @@ module Hoardable
8
8
  HOARDABLE_CALLBACKS_ENABLED: ^(untyped) -> untyped
9
9
  HOARDABLE_SAVE_TRASH: ^(untyped) -> untyped
10
10
  HOARDABLE_VERSION_UPDATES: ^(untyped) -> untyped
11
+ SUPPORTS_ENCRYPTED_ACTION_TEXT: untyped
11
12
  self.@context: Hash[untyped, untyped]
12
13
  self.@config: untyped
13
14
  self.@at: nil
@@ -17,12 +18,15 @@ module Hoardable
17
18
  def self.at: (untyped datetime) -> untyped
18
19
  def self.logger: -> untyped
19
20
 
21
+ class Engine
22
+ end
23
+
20
24
  module FinderMethods
21
25
  def find_one: (untyped id) -> untyped
22
26
  def find_some: (untyped ids) -> untyped
23
27
 
24
28
  private
25
- def hoardable_source_ids: ([untyped] ids) -> Array[untyped]
29
+ def hoardable_ids: ([untyped] ids) -> Array[untyped]
26
30
  end
27
31
 
28
32
  module Scopes
@@ -44,15 +48,13 @@ module Hoardable
44
48
  end
45
49
 
46
50
  class DatabaseClient
47
- @hoardable_version_source_id: untyped
48
-
49
51
  attr_reader source_record: SourceModel
50
52
  def initialize: (SourceModel source_record) -> void
51
53
  def insert_hoardable_version: (untyped operation) -> untyped
54
+ def source_primary_key: -> untyped
52
55
  def find_or_initialize_hoardable_event_uuid: -> untyped
53
- def hoardable_version_source_id: -> untyped
54
- def query_hoardable_version_source_id: -> untyped
55
56
  def initialize_version_attributes: (untyped operation) -> untyped
57
+ def source_attributes_without_primary_key: -> untyped
56
58
  def initialize_temporal_range: -> Range
57
59
  def initialize_hoardable_data: -> untyped
58
60
  def assign_hoardable_context: (:event_uuid | :meta | :note | :whodunit key) -> nil
@@ -71,7 +73,7 @@ module Hoardable
71
73
  def at: (untyped datetime) -> SourceModel?
72
74
  def version_at: (untyped datetime) -> untyped
73
75
  def revert_to!: (untyped datetime) -> SourceModel?
74
- def hoardable_source_id: -> untyped
76
+ def hoardable_id: -> untyped
75
77
 
76
78
  private
77
79
  def hoardable_client: -> DatabaseClient
@@ -82,18 +84,16 @@ module Hoardable
82
84
  end
83
85
 
84
86
  module VersionModel
85
- @hoardable_source_attributes: untyped
86
-
87
87
  def revert!: -> untyped
88
88
  def untrash!: -> untyped
89
89
  def changes: -> untyped
90
- def hoardable_source_id: -> untyped
91
90
 
92
91
  private
93
92
  def insert_untrashed_source: -> untyped
94
93
  def hoardable_source_attributes: -> untyped
95
94
 
96
95
  public
96
+ def version_class: -> VersionModel
97
97
  def scope_attributes: -> untyped
98
98
  end
99
99
 
@@ -108,15 +108,15 @@ module Hoardable
108
108
  end
109
109
 
110
110
  module Associations
111
- @hoardable_association_overrider: Overrider
111
+ include HasRichText
112
+ include BelongsTo
113
+ include HasOne
114
+ include HasMany
115
+ end
112
116
 
113
- def belongs_to: (*untyped args) -> nil
114
- def has_one: (*untyped args) -> nil
117
+ module HasMany
115
118
  def has_many: (*untyped args) -> untyped
116
119
 
117
- private
118
- def hoardable_association_overrider: -> Overrider
119
-
120
120
  module HasManyExtension
121
121
  @scope: untyped
122
122
  @association: bot
@@ -126,14 +126,21 @@ module Hoardable
126
126
  private
127
127
  def hoardable_scope: -> untyped
128
128
  end
129
+ end
129
130
 
130
- class Overrider
131
- attr_reader klass: Associations
132
- def initialize: (Associations klass) -> void
133
- def override_belongs_to: (untyped name) -> untyped
134
- def override_has_one: (untyped name) -> untyped
135
- def override_has_many: (untyped name) -> untyped
136
- end
131
+ module BelongsTo
132
+ def belongs_to: (*untyped args) -> nil
133
+
134
+ private
135
+ def hoardable_override_belongs_to: (untyped name) -> untyped
136
+ end
137
+
138
+ module HasOne
139
+ def has_one: (*untyped args) -> nil
140
+ end
141
+
142
+ module HasRichText
143
+ def has_rich_text: (untyped name, ?encrypted: false, ?hoardable: false) -> nil
137
144
  end
138
145
 
139
146
  class MigrationGenerator
@@ -141,7 +148,7 @@ module Hoardable
141
148
 
142
149
  def create_versions_table: -> untyped
143
150
  def foreign_key_type: -> String
144
- def migration_template_name: -> String
151
+ def primary_key: -> String
145
152
  def singularized_table_name: -> untyped
146
153
  end
147
154
 
@@ -150,4 +157,12 @@ module Hoardable
150
157
  def create_migration_file: -> untyped
151
158
  def self.next_migration_number: (untyped dir) -> untyped
152
159
  end
160
+
161
+ class RichText
162
+ include Model
163
+ end
164
+
165
+ class EncryptedRichText
166
+ include Model
167
+ end
153
168
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hoardable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.12.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - justin talbott
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-09 00:00:00.000000000 Z
11
+ date: 2022-10-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -106,12 +106,10 @@ files:
106
106
  - Rakefile
107
107
  - lib/generators/hoardable/install_generator.rb
108
108
  - lib/generators/hoardable/migration_generator.rb
109
- - lib/generators/hoardable/templates/functions.rb.erb
109
+ - lib/generators/hoardable/templates/install.rb.erb
110
110
  - lib/generators/hoardable/templates/migration.rb.erb
111
- - lib/generators/hoardable/templates/migration_6.rb.erb
112
111
  - lib/hoardable.rb
113
112
  - lib/hoardable/associations.rb
114
- - lib/hoardable/attachment.rb
115
113
  - lib/hoardable/belongs_to.rb
116
114
  - lib/hoardable/database_client.rb
117
115
  - lib/hoardable/encrypted_rich_text.rb
@@ -120,7 +118,6 @@ files:
120
118
  - lib/hoardable/finder_methods.rb
121
119
  - lib/hoardable/has_many.rb
122
120
  - lib/hoardable/has_one.rb
123
- - lib/hoardable/has_one_attached.rb
124
121
  - lib/hoardable/has_rich_text.rb
125
122
  - lib/hoardable/model.rb
126
123
  - lib/hoardable/rich_text.rb
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class InstallHoardable < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
4
- def up
5
- execute(
6
- <<~SQL
7
- CREATE OR REPLACE FUNCTION hoardable_version_prevent_update() RETURNS trigger
8
- LANGUAGE plpgsql AS
9
- $$BEGIN
10
- RAISE EXCEPTION 'updating a version is not allowed';
11
- RETURN NEW;
12
- END;$$;
13
- SQL
14
- )
15
- end
16
- end
@@ -1,54 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Create<%= class_name.singularize.delete(':') %>Versions < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
4
- def change
5
- reversible do |dir|
6
- dir.up do
7
- execute <<~SQL
8
- DO $$
9
- BEGIN
10
- IF NOT EXISTS (
11
- SELECT 1 FROM pg_type t
12
- WHERE t.typname = 'hoardable_operation'
13
- ) THEN
14
- CREATE TYPE hoardable_operation AS ENUM ('update', 'delete', 'insert');
15
- END IF;
16
- END
17
- $$;
18
- SQL
19
- end
20
- end
21
- create_table :<%= singularized_table_name %>_versions, id: false, options: 'INHERITS (<%= table_name %>)' do |t|
22
- t.jsonb :_data
23
- t.tsrange :_during, null: false
24
- t.uuid :_event_uuid, null: false, index: true
25
- t.column :_operation, :hoardable_operation, null: false, index: true
26
- t.<%= foreign_key_type %> :hoardable_source_id, null: false, index: true
27
- end
28
- reversible do |dir|
29
- dir.up do
30
- execute(
31
- <<~SQL
32
- CREATE TRIGGER <%= singularized_table_name %>_versions_prevent_update
33
- BEFORE UPDATE ON <%= singularized_table_name %>_versions FOR EACH ROW
34
- EXECUTE PROCEDURE hoardable_version_prevent_update();
35
- SQL
36
- )
37
- end
38
- dir.down do
39
- execute(
40
- <<~SQL
41
- DROP TRIGGER <%= singularized_table_name %>_versions_prevent_update
42
- ON <%= singularized_table_name %>_versions;
43
- SQL
44
- )
45
- end
46
- end
47
- add_index(:<%= singularized_table_name %>_versions, :id, unique: true)
48
- add_index(
49
- :<%= singularized_table_name %>_versions,
50
- %i[_during hoardable_source_id],
51
- name: 'idx_<%= singularized_table_name %>_versions_temporally'
52
- )
53
- end
54
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Hoardable
4
- # A {Hoardable} subclass of {ActiveStorage::Attachment}
5
- class Attachment < ActiveStorage::Attachment
6
- include Model
7
-
8
- class CreateOne < ActiveStorage::Attached::Changes::CreateOne
9
- private
10
-
11
- def build_attachment
12
- Attachment.new(record: record, name: name, blob: blob)
13
- end
14
- end
15
- end
16
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Hoardable
4
- # Provides temporal version awareness for +ActionText+.
5
- module HasOneAttached
6
- extend ActiveSupport::Concern
7
-
8
- class_methods do
9
- def has_one_attached(name, **options, &block)
10
- hoardable = options.delete(:hoardable)
11
- super(name, **options, &block)
12
- return unless hoardable
13
-
14
- 'ActiveStorage::Attachment'.constantize
15
- reflection_options = reflections["#{name}_attachment"].options
16
- reflection_options[:class_name] = reflection_options[:class_name].sub(/ActiveStorage/, 'Hoardable')
17
-
18
- generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
19
- # frozen_string_literal: true
20
- def #{name}=(attachable)
21
- attachment_changes["#{name}"] =
22
- if attachable.nil?
23
- ActiveStorage::Attached::Changes::DeleteOne.new("#{name}", self)
24
- else
25
- Hoardable::Attachment::CreateOne.new("#{name}", self, attachable)
26
- end
27
- end
28
- CODE
29
- end
30
- end
31
- end
32
- private_constant :HasOneAttached
33
- end