activerecord-model_inheritance 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (26) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +451 -0
  3. data/CHANGELOG.md +5 -0
  4. data/Gemfile +8 -0
  5. data/LICENSE +21 -0
  6. data/README.md +255 -0
  7. data/Rakefile +7 -0
  8. data/lib/active_record/model_inheritance/error.rb +5 -0
  9. data/lib/active_record/model_inheritance/model.rb +169 -0
  10. data/lib/active_record/model_inheritance/version.rb +5 -0
  11. data/lib/active_record/model_inheritance/view_definition.rb +112 -0
  12. data/lib/active_record/model_inheritance.rb +57 -0
  13. data/lib/activerecord/model_inheritance.rb +1 -0
  14. data/lib/generators/active_record/model_inheritance/generators.rb +6 -0
  15. data/lib/generators/active_record/model_inheritance/model/model_generator.rb +75 -0
  16. data/lib/generators/active_record/model_inheritance/model/templates/definition.erb +1 -0
  17. data/lib/generators/active_record/model_inheritance/model/templates/model.erb +2 -0
  18. data/lib/generators/active_record/model_inheritance/model/templates/model_config.erb +3 -0
  19. data/lib/generators/active_record/model_inheritance/view/templates/create_migration.erb +13 -0
  20. data/lib/generators/active_record/model_inheritance/view/templates/update_migration.erb +5 -0
  21. data/lib/generators/active_record/model_inheritance/view/view_generator.rb +121 -0
  22. data/sig/active_record/model_inheritance/generators/model_generator.rbs +23 -0
  23. data/sig/active_record/model_inheritance/generators/view_generator.rbs +45 -0
  24. data/sig/active_record/model_inheritance/version.rbs +5 -0
  25. data/sig/active_record/model_inheritance/view_definition.rbs +20 -0
  26. metadata +127 -0
@@ -0,0 +1,75 @@
1
+ require 'rails/generators'
2
+ require 'active_record/model_inheritance'
3
+
4
+ module ActiveRecord
5
+ module ModelInheritance
6
+ module Generators
7
+ class ModelGenerator < Rails::Generators::NamedBase
8
+ source_root File.expand_path('templates', __dir__)
9
+
10
+ desc 'Create derived model and definition files'
11
+
12
+ argument :derives_from, type: :string, optional: false
13
+
14
+ class_option :only_definition,
15
+ type: :boolean,
16
+ required: false,
17
+ default: false,
18
+ desc: 'Whether or not to generate only the definition file'
19
+
20
+ def generate_model
21
+ return if skip_generate_model?
22
+
23
+ if rails_application?
24
+ invoke 'model',
25
+ [file_path.singularize],
26
+ options.merge(
27
+ fixture_replacement: false,
28
+ migration: false
29
+ )
30
+ else
31
+ template 'model.erb', ModelInheritance.models_path.join("#{file_path.singularize}.rb")
32
+ end
33
+ end
34
+
35
+ def inject_configuration_to_model
36
+ return if skip_generate_model?
37
+
38
+ inject_into_class ModelInheritance.models_path.join("#{file_path.singularize}.rb"), class_name do
39
+ evaluate_template 'model_config.erb'
40
+ end
41
+ end
42
+
43
+ def create_definition_file
44
+ template 'definition.erb', ModelInheritance.definitions_path.join("#{table_name}.rb")
45
+ end
46
+
47
+ no_tasks do
48
+ def evaluate_template source
49
+ source = File.expand_path find_in_source_paths(source.to_s)
50
+
51
+ erb = ERB.new(
52
+ File.read(source),
53
+ trim_mode: '-',
54
+ eoutvar: '@output_buffer'
55
+ )
56
+
57
+ erb.result binding
58
+ end
59
+
60
+ def rails_application?
61
+ defined?(Rails.application) && Rails.application.present?
62
+ end
63
+
64
+ def skip_generate_model?
65
+ options[:only_definition]
66
+ end
67
+
68
+ def formatted_base_name
69
+ "::#{derives_from.camelize}"
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1 @@
1
+ ActiveRecord::ModelInheritance::ViewDefinition.define_derived_view <%= name %>
@@ -0,0 +1,2 @@
1
+ class <%= class_name %> < ActiveRecord::Base
2
+ end
@@ -0,0 +1,3 @@
1
+ include ActiveRecord::ModelInheritance::Model
2
+
3
+ derives_from <%= formatted_base_name %>
@@ -0,0 +1,13 @@
1
+ class <%= migration_class_name %> < <%= activerecord_migration_class %>
2
+ def change
3
+ create_table <%= formatted_inner_table_name %> do |t|
4
+ # add your derived model extra fields here
5
+
6
+ t.references <%= formatted_base_name %>, foreign_key: true
7
+
8
+ t.timestamps
9
+ end
10
+
11
+ create_view <%= formatted_table_name %>
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ class <%= migration_class_name %> < <%= activerecord_migration_class %>
2
+ def change
3
+ update_view <%= formatted_table_name %>, version: <%= next_version %>, revert_to_version: <%= current_version %>
4
+ end
5
+ end
@@ -0,0 +1,121 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/active_record'
3
+ require 'active_record/model_inheritance'
4
+ require 'active_record/model_inheritance/view_definition'
5
+
6
+ module ActiveRecord
7
+ module ModelInheritance
8
+ module Generators
9
+ class ViewGenerator < Rails::Generators::NamedBase
10
+ include Rails::Generators::Migration
11
+
12
+ desc 'Generate SQL view definition and create initial/update migration'
13
+
14
+ source_root File.expand_path('templates', __dir__)
15
+
16
+ class_option :skip_migration,
17
+ type: :boolean,
18
+ required: false,
19
+ default: false,
20
+ desc: 'Whether or not to generate a migration file'
21
+
22
+ def create_view_file
23
+ create_file ModelInheritance
24
+ .views_path
25
+ .join("#{table_name}_v#{formatted_next_version}.sql"),
26
+ view_definition.to_sql
27
+ end
28
+
29
+ def create_migration_file
30
+ return if skip_generate_migration?
31
+
32
+ if creating?
33
+ migration_template(
34
+ 'create_migration.erb',
35
+ ModelInheritance.migrations_path.join("create_#{table_name}.rb")
36
+ )
37
+ else
38
+ migration_template(
39
+ 'update_migration.erb',
40
+ ModelInheritance.migrations_path.join("update_#{table_name}_to_version_#{formatted_next_version}.rb")
41
+ )
42
+ end
43
+ end
44
+
45
+ def self.next_migration_number dir
46
+ ::ActiveRecord::Generators::Base.next_migration_number dir
47
+ end
48
+
49
+ no_tasks do
50
+ def current_version
51
+ @current_version ||= ModelInheritance
52
+ .views_path
53
+ .glob("#{table_name}_v*.sql")
54
+ .last
55
+ .then { |last_version_file| last_version_file&.basename&.to_s }
56
+ .then do |last_version_name|
57
+ version_regex
58
+ .match(last_version_name)
59
+ .try(:[], :version)
60
+ .to_i
61
+ end
62
+ end
63
+
64
+ def next_version
65
+ @next_version ||= current_version.next
66
+ end
67
+
68
+ def formatted_next_version
69
+ next_version
70
+ .to_s
71
+ .rjust(2, '0')
72
+ end
73
+
74
+ def creating?
75
+ current_version.zero?
76
+ end
77
+
78
+ def skip_generate_migration?
79
+ options[:skip_migration]
80
+ end
81
+
82
+ def version_regex
83
+ %r{\A#{table_name}_v(?<version>\d+)\.sql\z}
84
+ end
85
+
86
+ def formatted_class_name
87
+ class_name.tr('.:', '').pluralize
88
+ end
89
+
90
+ def migration_class_name
91
+ if creating?
92
+ "Create#{formatted_class_name}"
93
+ else
94
+ "Update#{formatted_class_name}ToVersion#{next_version}"
95
+ end
96
+ end
97
+
98
+ def activerecord_migration_class
99
+ "ActiveRecord::Migration[#{ActiveRecord::Migration.current_version}]"
100
+ end
101
+
102
+ def formatted_inner_table_name
103
+ ":#{view_definition.model_class.model_inheritance_inner_model.table_name}"
104
+ end
105
+
106
+ def formatted_base_name
107
+ ":#{view_definition.model_class.model_inheritance_base_model.model_name.singular}"
108
+ end
109
+
110
+ def formatted_table_name
111
+ ":#{table_name}"
112
+ end
113
+
114
+ def view_definition
115
+ @view_definition ||= ViewDefinition.from_name table_name
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,23 @@
1
+ module ActiveRecord
2
+ module ModelInheritance
3
+ module Generators
4
+ class ModelGenerator < Rails::Generators::NamedBase
5
+ def generate_model: () -> void
6
+
7
+ def inject_configuration_to_model: () -> void
8
+
9
+ def create_definition_file: () -> void
10
+
11
+ def evaluate_template: (String source) -> String
12
+
13
+ def rails_application?: () -> bool
14
+
15
+ def skip_generate_model?: () -> bool
16
+
17
+ def formatted_base_name: () -> String
18
+
19
+ def formatted_base_reference: () -> String
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,45 @@
1
+ module ActiveRecord
2
+ module ModelInheritance
3
+ module Generators
4
+ class ViewGenerator < Rails::Generators::NamedBase
5
+ include Rails::Generators::Migration
6
+
7
+ @current_version: Integer
8
+ @next_version: Integer
9
+ @view_definition: ViewDefinition
10
+
11
+ def create_view_file: () -> void
12
+
13
+ def create_migration_file: () -> void
14
+
15
+ def self.next_migration_number: (untyped dir) -> untyped
16
+
17
+ def current_version: () -> Integer
18
+
19
+ def next_version: () -> Integer
20
+
21
+ def formatted_next_version: () -> String
22
+
23
+ def creating?: () -> bool
24
+
25
+ def skip_generate_migration?: () -> bool
26
+
27
+ def version_regex: () -> Regexp
28
+
29
+ def formatted_class_name: () -> String
30
+
31
+ def migration_class_name: () -> String
32
+
33
+ def activerecord_migration_class: () -> String
34
+
35
+ def formatted_inner_table_name: () -> String
36
+
37
+ def formatted_base_name: () -> String
38
+
39
+ def formatted_table_name: () -> String
40
+
41
+ def view_definition: () -> ViewDefinition
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveRecord
2
+ module ModelInheritance
3
+ VERSION: String
4
+ end
5
+ end
@@ -0,0 +1,20 @@
1
+ module ActiveRecord
2
+ module ModelInheritance
3
+ class ViewDefinition
4
+ def initialize: (Class model_class, untyped definition) -> void
5
+
6
+ def self.define_derived_view: (Class model_class) -> ViewDefinition
7
+
8
+ def self.from_model: (Class model_class) -> ViewDefinition
9
+
10
+ def self.from_name: (String name) -> ViewDefinition
11
+
12
+ attr_reader definition: untyped
13
+ attr_reader model_class: Class
14
+
15
+ def attributes_mapping: -> {base: Array[Symbol], inner: Array[Symbol]}
16
+
17
+ def to_sql: -> String
18
+ end
19
+ end
20
+ end
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord-model_inheritance
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Moku S.r.l.
8
+ - Marco Volpato
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2023-07-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 7.0.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 7.0.0
28
+ - !ruby/object:Gem::Dependency
29
+ name: activesupport
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: 7.0.0
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: 7.0.0
42
+ - !ruby/object:Gem::Dependency
43
+ name: railties
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: 7.0.0
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: 7.0.0
56
+ - !ruby/object:Gem::Dependency
57
+ name: scenic
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: 1.7.0
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 1.7.0
70
+ description: An attempt at real inheritance for ActiveRecord models.
71
+ email:
72
+ - info@moku.io
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".rubocop.yml"
78
+ - CHANGELOG.md
79
+ - Gemfile
80
+ - LICENSE
81
+ - README.md
82
+ - Rakefile
83
+ - lib/active_record/model_inheritance.rb
84
+ - lib/active_record/model_inheritance/error.rb
85
+ - lib/active_record/model_inheritance/model.rb
86
+ - lib/active_record/model_inheritance/version.rb
87
+ - lib/active_record/model_inheritance/view_definition.rb
88
+ - lib/activerecord/model_inheritance.rb
89
+ - lib/generators/active_record/model_inheritance/generators.rb
90
+ - lib/generators/active_record/model_inheritance/model/model_generator.rb
91
+ - lib/generators/active_record/model_inheritance/model/templates/definition.erb
92
+ - lib/generators/active_record/model_inheritance/model/templates/model.erb
93
+ - lib/generators/active_record/model_inheritance/model/templates/model_config.erb
94
+ - lib/generators/active_record/model_inheritance/view/templates/create_migration.erb
95
+ - lib/generators/active_record/model_inheritance/view/templates/update_migration.erb
96
+ - lib/generators/active_record/model_inheritance/view/view_generator.rb
97
+ - sig/active_record/model_inheritance/generators/model_generator.rbs
98
+ - sig/active_record/model_inheritance/generators/view_generator.rbs
99
+ - sig/active_record/model_inheritance/version.rbs
100
+ - sig/active_record/model_inheritance/view_definition.rbs
101
+ homepage: https://github.com/moku-io/activerecord-model_inheritance
102
+ licenses:
103
+ - MIT
104
+ metadata:
105
+ homepage_uri: https://github.com/moku-io/activerecord-model_inheritance
106
+ source_code_uri: https://github.com/moku-io/activerecord-model_inheritance
107
+ changelog_uri: https://github.com/moku-io/activerecord-model_inheritance/blob/master/CHANGELOG.md
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: 3.0.0
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubygems_version: 3.3.26
124
+ signing_key:
125
+ specification_version: 4
126
+ summary: An attempt at real inheritance for ActiveRecord models.
127
+ test_files: []