schemerd 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3882b4702bf8be5603646dd0b145838a000ba86f14f133aa9e6c93f5c967a555
4
+ data.tar.gz: 81efcf0b59b4aa78220591e6722d48222e59680d711967ece0bfbae3af8b811f
5
+ SHA512:
6
+ metadata.gz: 60efa1723758b22ba9ec775e865b1a08c99a777a6f24b4346d248c39d18fc35c0d262ac307b1d9b10095a4c8fb8e5a26dcd39b11cec73e559b754f743645cfb5
7
+ data.tar.gz: fa4046b7dca821984dbd0d957a2e221b6fa3c65c44ec955d0fb59547ec25bfba91fcbfd6768166aaefd8b6707164cd342191ea8dc0277f7636cf407d7c1e91b3
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Pablo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # schemerd
2
+
3
+ Auto-generate [Mermaid](https://mermaid.js.org/) ER diagrams from your ActiveRecord models. Keeps your schema documentation in sync by regenerating after every migration.
4
+
5
+ ## Installation
6
+
7
+ Add to your Gemfile:
8
+
9
+ ```ruby
10
+ group :development do
11
+ gem "schemerd"
12
+ end
13
+ ```
14
+
15
+ Run `bundle install`.
16
+
17
+ ## Usage
18
+
19
+ ### Generate the diagram
20
+
21
+ ```bash
22
+ rake schemerd:generate
23
+ ```
24
+
25
+ This creates `doc/erd.md` with a Mermaid `erDiagram` block containing all your models, columns, and associations.
26
+
27
+ ### Auto-generate on migrations
28
+
29
+ By default, Schemerd hooks into `db:migrate`, `db:migrate:up`, `db:migrate:down`, and `db:migrate:redo` in development. The diagram regenerates automatically after each migration.
30
+
31
+ To disable this:
32
+
33
+ ```ruby
34
+ Schemerd.configure do |config|
35
+ config.auto_generate = false
36
+ end
37
+ ```
38
+
39
+ ## Configuration
40
+
41
+ Generate an initializer with all available options:
42
+
43
+ ```bash
44
+ rails generate schemerd:install
45
+ ```
46
+
47
+ This creates `config/initializers/schemerd.rb`. Available options:
48
+
49
+ ```ruby
50
+ Schemerd.configure do |config|
51
+ # Output directory relative to Rails.root (default: "doc")
52
+ config.output_directory = "doc"
53
+
54
+ # Output filename (default: "erd.md")
55
+ config.output_filename = "erd.md"
56
+
57
+ # Header text at the top of the generated file
58
+ config.header = "# Entity Relationship Diagram\n\nAuto-generated. Do not edit manually."
59
+
60
+ # Model name prefixes to exclude from the diagram (default: [])
61
+ config.excluded_prefixes = ["Flipper::", "Ahoy::"]
62
+
63
+ # Automatically regenerate after migrations (default: true)
64
+ config.auto_generate = true
65
+
66
+ # Base class for model discovery (default: "ApplicationRecord")
67
+ config.base_class = "ApplicationRecord"
68
+ end
69
+ ```
70
+
71
+ ## Output Example
72
+
73
+ The generated file contains a fenced Mermaid code block:
74
+
75
+ ~~~markdown
76
+ # Entity Relationship Diagram
77
+
78
+ Auto-generated from ActiveRecord models. Do not edit manually.
79
+
80
+ ```mermaid
81
+ erDiagram
82
+ Post }o--|| User : "author"
83
+ Comment ||--o{ Post : "comments"
84
+
85
+ User {
86
+ integer id PK
87
+ string name
88
+ string email
89
+ datetime created_at
90
+ datetime updated_at
91
+ }
92
+
93
+ Post {
94
+ integer id PK
95
+ string title
96
+ text body
97
+ integer user_id
98
+ datetime created_at
99
+ datetime updated_at
100
+ }
101
+ ```
102
+ ~~~
103
+
104
+ Render it on GitHub, GitLab, or any Mermaid-compatible viewer.
105
+
106
+ ## License
107
+
108
+ MIT License. See [LICENSE.txt](LICENSE.txt).
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schemerd
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ desc "Create a Schemerd initializer with default configuration"
7
+ source_root File.expand_path("templates", __dir__)
8
+
9
+ def copy_initializer
10
+ template "initializer.rb", "config/initializers/schemerd.rb"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ Schemerd.configure do |config|
2
+ # Output directory relative to Rails.root
3
+ # config.output_directory = "doc"
4
+
5
+ # Output filename
6
+ # config.output_filename = "erd.md"
7
+
8
+ # Header text at the top of the generated file
9
+ # config.header = "# Entity Relationship Diagram\n\nAuto-generated from ActiveRecord models. Do not edit manually."
10
+
11
+ # Model name prefixes to exclude from the diagram
12
+ # config.excluded_prefixes = ["Flipper::", "Ahoy::"]
13
+
14
+ # Automatically regenerate after running migrations
15
+ # config.auto_generate = true
16
+
17
+ # Base class for model discovery
18
+ # config.base_class = "ApplicationRecord"
19
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schemerd
4
+ class Configuration
5
+ attr_accessor :output_directory,
6
+ :output_filename,
7
+ :header,
8
+ :excluded_prefixes,
9
+ :auto_generate,
10
+ :base_class
11
+
12
+ def initialize
13
+ @output_directory = "doc"
14
+ @output_filename = "erd.md"
15
+ @header = "# Entity Relationship Diagram\n\n" \
16
+ "Auto-generated from ActiveRecord models. Do not edit manually."
17
+ @excluded_prefixes = []
18
+ @auto_generate = true
19
+ @base_class = "ApplicationRecord"
20
+ end
21
+
22
+ def output_path
23
+ Rails.root.join(output_directory, output_filename)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ require "fileutils"
5
+
6
+ module Schemerd
7
+ class Generator
8
+ def initialize(config)
9
+ @config = config
10
+ end
11
+
12
+ def call
13
+ models = load_models
14
+ content = build_diagram(models)
15
+
16
+ output_path = @config.output_path
17
+ FileUtils.mkdir_p(File.dirname(output_path))
18
+ File.write(output_path, content)
19
+
20
+ puts "Schemerd: ERD written to #{output_path}"
21
+ content
22
+ end
23
+
24
+ private
25
+
26
+ def load_models
27
+ Rails.application.eager_load!
28
+
29
+ base = @config.base_class.constantize
30
+ base.descendants
31
+ .reject(&:abstract_class?)
32
+ .reject { |m| excluded?(m.name) }
33
+ .select { |m| m.table_exists? rescue false }
34
+ .sort_by(&:name)
35
+ end
36
+
37
+ def excluded?(model_name)
38
+ @config.excluded_prefixes.any? { |prefix| model_name.start_with?(prefix) }
39
+ end
40
+
41
+ def build_diagram(models)
42
+ lines = []
43
+
44
+ lines << @config.header
45
+ lines << ""
46
+ lines << "```mermaid"
47
+ lines << "erDiagram"
48
+
49
+ lines.concat(associations_section(models))
50
+ lines << ""
51
+ lines.concat(entities_section(models))
52
+
53
+ lines << "```"
54
+ lines.join("\n") + "\n"
55
+ end
56
+
57
+ def associations_section(models)
58
+ lines = []
59
+ seen = Set.new
60
+ model_names = models.map(&:name).to_set
61
+
62
+ models.each do |model|
63
+ model.reflect_on_all_associations.each do |assoc|
64
+ target_name = assoc.klass.name rescue next
65
+ next unless model_names.include?(target_name)
66
+
67
+ pair = [model.name, target_name].sort
68
+ next if seen.include?(pair)
69
+ seen.add(pair)
70
+
71
+ line = relationship_line(model.name, target_name, assoc)
72
+ lines << " #{line}" if line
73
+ end
74
+ end
75
+
76
+ lines
77
+ end
78
+
79
+ def entities_section(models)
80
+ lines = []
81
+
82
+ models.each do |model|
83
+ lines << " #{model.name} {"
84
+ model.columns.each do |col|
85
+ pk = col.name == "id" ? "PK" : ""
86
+ type = col.type || "string"
87
+ lines << " #{type} #{col.name} #{pk}".rstrip
88
+ end
89
+ lines << " }"
90
+ lines << ""
91
+ end
92
+
93
+ lines
94
+ end
95
+
96
+ def relationship_line(source, target, assoc)
97
+ case assoc.macro
98
+ when :belongs_to
99
+ if assoc.options[:optional]
100
+ "#{source} }o--o| #{target} : \"#{assoc.name}\""
101
+ else
102
+ "#{source} }o--|| #{target} : \"#{assoc.name}\""
103
+ end
104
+ when :has_many
105
+ "#{source} ||--o{ #{target} : \"#{assoc.name}\""
106
+ when :has_one
107
+ "#{source} ||--o| #{target} : \"#{assoc.name}\""
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schemerd
4
+ class Railtie < Rails::Railtie
5
+ rake_tasks do
6
+ load File.expand_path("../../tasks/schemerd.rake", __dir__)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schemerd
4
+ VERSION = "0.1.0"
5
+ end
data/lib/schemerd.rb ADDED
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "schemerd/version"
4
+ require "schemerd/configuration"
5
+ require "schemerd/generator"
6
+ require "schemerd/railtie" if defined?(Rails::Railtie)
7
+
8
+ module Schemerd
9
+ class << self
10
+ def configuration
11
+ @configuration ||= Configuration.new
12
+ end
13
+
14
+ def configure
15
+ yield(configuration)
16
+ end
17
+
18
+ def generate
19
+ Generator.new(configuration).call
20
+ end
21
+
22
+ def reset_configuration!
23
+ @configuration = Configuration.new
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :schemerd do
4
+ desc "Generate Mermaid ER diagram from ActiveRecord models"
5
+ task generate: :environment do
6
+ Schemerd.generate
7
+ end
8
+ end
9
+
10
+ # Auto-run after migrations in development
11
+ if defined?(Rails) && Rails.env.development?
12
+ %w[db:migrate db:migrate:up db:migrate:down db:migrate:redo].each do |task_name|
13
+ next unless Rake::Task.task_defined?(task_name)
14
+
15
+ Rake::Task[task_name].enhance do
16
+ if Schemerd.configuration.auto_generate
17
+ Rake::Task["schemerd:generate"].invoke
18
+ end
19
+ end
20
+ end
21
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: schemerd
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Pablo Monfort
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: activerecord
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '6.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '6.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: railties
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '6.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '6.0'
40
+ description: Auto-generate Mermaid ERD markdown files from your ActiveRecord schema.
41
+ Hooks into db:migrate to keep diagrams in sync.
42
+ executables: []
43
+ extensions: []
44
+ extra_rdoc_files: []
45
+ files:
46
+ - LICENSE.txt
47
+ - README.md
48
+ - lib/generators/schemerd/install_generator.rb
49
+ - lib/generators/schemerd/templates/initializer.rb
50
+ - lib/schemerd.rb
51
+ - lib/schemerd/configuration.rb
52
+ - lib/schemerd/generator.rb
53
+ - lib/schemerd/railtie.rb
54
+ - lib/schemerd/version.rb
55
+ - lib/tasks/schemerd.rake
56
+ homepage: https://github.com/pablo/schemerd
57
+ licenses:
58
+ - MIT
59
+ metadata: {}
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '3.0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubygems_version: 3.6.9
75
+ specification_version: 4
76
+ summary: Generate Mermaid ER diagrams from ActiveRecord models
77
+ test_files: []