hoardable 0.11.0 → 0.12.2

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