webring-rails 1.0.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.
Files changed (30) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +598 -0
  4. data/app/assets/javascripts/webring/widget.js +224 -0
  5. data/app/controllers/webring/application_controller.rb +5 -0
  6. data/app/controllers/webring/members_controller.rb +60 -0
  7. data/app/controllers/webring/navigation_controller.rb +68 -0
  8. data/app/controllers/webring/widget_controller.rb +31 -0
  9. data/app/helpers/webring/application_helper.rb +4 -0
  10. data/app/jobs/webring/application_job.rb +4 -0
  11. data/app/mailers/webring/application_mailer.rb +6 -0
  12. data/app/models/concerns/webring/navigation.rb +60 -0
  13. data/app/models/webring/application_record.rb +5 -0
  14. data/app/models/webring/member.rb +31 -0
  15. data/config/routes.rb +8 -0
  16. data/lib/generators/USAGE +25 -0
  17. data/lib/generators/webring/controller/controller_generator.rb +49 -0
  18. data/lib/generators/webring/controller/templates/navigation_controller.rb +61 -0
  19. data/lib/generators/webring/install/install_generator.rb +31 -0
  20. data/lib/generators/webring/install/templates/AFTER_INSTALL +63 -0
  21. data/lib/generators/webring/member/member_generator.rb +55 -0
  22. data/lib/generators/webring/member/templates/AFTER_INSTALL +38 -0
  23. data/lib/generators/webring/member/templates/migration.rb +15 -0
  24. data/lib/generators/webring/member/templates/model.rb +31 -0
  25. data/lib/generators/webring/shared/route_injector.rb +40 -0
  26. data/lib/generators/webring_generator.rb +17 -0
  27. data/lib/webring/engine.rb +35 -0
  28. data/lib/webring/version.rb +3 -0
  29. data/lib/webring_rails.rb +5 -0
  30. metadata +115 -0
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Webring Navigation Widget
3
+ *
4
+ * Usage:
5
+ * <script src="https://yourhub.com/webring/widget.js" data-member-uid="YOUR_MEMBER_UID" data-widget-type="full"></script>
6
+ * <div id="webring-widget"></div>
7
+ *
8
+ * For multiple widgets on same page:
9
+ * <script src="https://yourhub.com/webring/widget.js" data-member-uid="YOUR_MEMBER_UID" data-widget-type="full" data-target-id="custom-widget-id"></script>
10
+ * <div id="custom-widget-id"></div>
11
+ *
12
+ * Widget Types:
13
+ * - full: text, back btn, random btn, forward btn (default)
14
+ * - no-text: back btn, random btn, forward btn (no text)
15
+ * - two-way: back btn, forward btn (no random)
16
+ * - one-way: forward btn only
17
+ *
18
+ * Additional Options:
19
+ * - data-button-text="true|false": If true, buttons will show text labels. If false, only symbols are shown. Default: true
20
+ * - data-styles="full|layout|none": Controls styling applied to the widget. Default: full
21
+ * - full: Apply all styles (default)
22
+ * - layout: Only layout styles, no visual design
23
+ * - none: No styles applied
24
+ */
25
+
26
+ (function() {
27
+ // Configuration constants
28
+ const WIDGET_CONFIG = {
29
+ VALID_TYPES: ['full', 'no-text', 'two-way', 'one-way'],
30
+ DEFAULT_TYPE: 'full',
31
+ DEFAULT_TARGET_ID: 'webring-widget',
32
+ STYLE_ID: 'webring-widget-styles',
33
+ VALID_STYLE_TYPES: ['full', 'layout', 'none'],
34
+ DEFAULT_STYLE_TYPE: 'full'
35
+ };
36
+
37
+ const NAVIGATION_ACTIONS = {
38
+ prev: {
39
+ symbol: '«',
40
+ text: '« Prev',
41
+ title: 'Previous site',
42
+ path: 'previous'
43
+ },
44
+ random: {
45
+ symbol: '⚡',
46
+ text: 'Random',
47
+ title: 'Random site',
48
+ path: 'random'
49
+ },
50
+ next: {
51
+ symbol: '»',
52
+ text: 'Next »',
53
+ title: 'Next site',
54
+ path: 'next'
55
+ }
56
+ };
57
+
58
+ const WIDGET_TYPE_CONFIG = {
59
+ 'full': { showTitle: true, actions: ['prev', 'random', 'next'] },
60
+ 'no-text': { showTitle: false, actions: ['prev', 'random', 'next'] },
61
+ 'two-way': { showTitle: false, actions: ['prev', 'next'] },
62
+ 'one-way': { showTitle: false, actions: ['next'] }
63
+ };
64
+
65
+ // Define styles outside the function to avoid duplication
66
+ const STYLES = {
67
+ layout: `
68
+ .webring-nav {
69
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
70
+ font-size: 16px;
71
+ display: flex;
72
+ flex-direction: column;
73
+ align-items: center;
74
+ max-width: 350px;
75
+ margin: 0 auto;
76
+ padding: 10px;
77
+ }
78
+ .webring-title {
79
+ margin-bottom: 8px;
80
+ }
81
+ .webring-nav nav {
82
+ display: flex;
83
+ gap: 10px;
84
+ width: 100%;
85
+ justify-content: center;
86
+ }
87
+ .webring-nav a.webring-btn {
88
+ display: flex;
89
+ align-items: center;
90
+ justify-content: center;
91
+ padding: 6px 12px;
92
+ text-decoration: none;
93
+ }
94
+ .webring-nav[data-widget-type="no-text"] {
95
+ padding: 8px 10px;
96
+ }
97
+ .webring-nav[data-widget-type="one-way"] {
98
+ max-width: 200px;
99
+ }
100
+ .webring-nav[data-widget-type="one-way"] nav {
101
+ justify-content: center;
102
+ }
103
+ `,
104
+ design: `
105
+ .webring-nav[data-widget-type="full"] {
106
+ border: 2.5px solid #000000;
107
+ }
108
+ .webring-title {
109
+ font-weight: 600;
110
+ }
111
+ .webring-nav a.webring-btn {
112
+ color: #000000;
113
+ font-weight: 600;
114
+ background-color: #ffffff;
115
+ border: 2.5px solid #000000;
116
+ transition: background-color 0.2s ease, color 0.2s ease;
117
+ }
118
+ .webring-nav a.webring-btn:hover {
119
+ background-color: #000000;
120
+ color: #ffffff;
121
+ }
122
+ .webring-nav a.webring-btn:focus {
123
+ outline: none;
124
+ background-color: transparent;
125
+ color: #000000;
126
+ }
127
+ .webring-nav a.webring-btn:active {
128
+ border-color: #000000;
129
+ background-color: #000000;
130
+ color: #ffffff;
131
+ }
132
+ `
133
+ };
134
+
135
+ // Run immediately for widget initialization
136
+ createWidget();
137
+
138
+ function createWidget(scriptElement) {
139
+ const script = scriptElement || document.currentScript || (function() {
140
+ const scripts = document.getElementsByTagName('script');
141
+ return scripts[scripts.length - 1];
142
+ })();
143
+
144
+ if (!script) return;
145
+
146
+ // Config from data attributes
147
+ const memberUid = script.getAttribute('data-member-uid');
148
+ const widgetType = script.getAttribute('data-widget-type') || WIDGET_CONFIG.DEFAULT_TYPE;
149
+ const targetId = script.getAttribute('data-target-id') || WIDGET_CONFIG.DEFAULT_TARGET_ID;
150
+ const buttonText = script.getAttribute('data-button-text') !== 'false';
151
+ const stylesType = script.getAttribute('data-styles') || WIDGET_CONFIG.DEFAULT_STYLE_TYPE;
152
+ const stylesOption = WIDGET_CONFIG.VALID_STYLE_TYPES.includes(stylesType) ? stylesType : WIDGET_CONFIG.DEFAULT_STYLE_TYPE;
153
+
154
+ if (!memberUid) {
155
+ console.error('Webring Widget: Missing data-member-uid attribute on script tag.');
156
+ return;
157
+ }
158
+
159
+ if (!WIDGET_CONFIG.VALID_TYPES.includes(widgetType)) {
160
+ console.error(`Webring Widget: Invalid widget type "${widgetType}". Valid types: ${WIDGET_CONFIG.VALID_TYPES.join(', ')}`);
161
+ return;
162
+ }
163
+
164
+ const scriptSrc = script.getAttribute('src');
165
+ const baseUrl = new URL(scriptSrc, window.location.href).origin;
166
+
167
+ const renderWidget = function() {
168
+ const container = document.getElementById(targetId);
169
+ if (!container) {
170
+ console.error(`Webring Widget: No element with id "${targetId}" found.`);
171
+ return;
172
+ }
173
+
174
+ // Navigation links
175
+ const config = WIDGET_TYPE_CONFIG[widgetType];
176
+ const linkElements = config.actions.map(action => {
177
+ const actionConfig = NAVIGATION_ACTIONS[action];
178
+ const url = `${baseUrl}/webring/${actionConfig.path}?source_member_uid=${memberUid}`;
179
+ const label = buttonText ? actionConfig.text : actionConfig.symbol;
180
+ return `<a href="${url}" title="${actionConfig.title}" class="webring-btn">${label}</a>`;
181
+ }).join('\n ');
182
+
183
+ // Create widget HTML
184
+ const title = config.showTitle ? '<span class="webring-title">Webring</span>' : '';
185
+ container.innerHTML = `
186
+ <div class="webring-nav" data-widget-type="${widgetType}">
187
+ ${title}
188
+ <nav class="webring-buttons">
189
+ ${linkElements}
190
+ </nav>
191
+ </div>
192
+ `;
193
+
194
+ applyStyles(stylesOption);
195
+ };
196
+
197
+ function applyStyles(styleOption) {
198
+ let styleElement = document.getElementById(WIDGET_CONFIG.STYLE_ID);
199
+ if (!styleElement) {
200
+ styleElement = document.createElement('style');
201
+ styleElement.id = WIDGET_CONFIG.STYLE_ID;
202
+ document.head.appendChild(styleElement);
203
+ }
204
+
205
+ switch (styleOption) {
206
+ case 'none':
207
+ styleElement.textContent = '';
208
+ break;
209
+ case 'layout':
210
+ styleElement.textContent = STYLES.layout;
211
+ break;
212
+ default: // 'full'
213
+ styleElement.textContent = STYLES.layout + STYLES.design;
214
+ }
215
+ }
216
+
217
+ // Run immediately or wait for DOM
218
+ if (document.readyState === 'loading') {
219
+ document.addEventListener('DOMContentLoaded', renderWidget);
220
+ } else {
221
+ renderWidget();
222
+ }
223
+ }
224
+ })();
@@ -0,0 +1,5 @@
1
+ module Webring
2
+ class ApplicationController < ActionController::Base
3
+ layout 'application'
4
+ end
5
+ end
@@ -0,0 +1,60 @@
1
+ module Webring
2
+ class MembersController < ::ApplicationController
3
+ before_action :set_member, only: %i[show edit update destroy]
4
+
5
+ # GET /webring/members
6
+ def index
7
+ @members = Member.all.order(created_at: :desc)
8
+ end
9
+
10
+ # GET /webring/members/1
11
+ def show; end
12
+
13
+ # GET /webring/members/new
14
+ def new
15
+ @member = Member.new
16
+ end
17
+
18
+ # GET /webring/members/1/edit
19
+ def edit; end
20
+
21
+ # POST /webring/members
22
+ def create
23
+ @member = Member.new(member_params)
24
+
25
+ if @member.save
26
+ redirect_to admin_panel_member_path(@member), notice: 'Member was successfully created.'
27
+ else
28
+ render :new, status: :unprocessable_entity
29
+ end
30
+ end
31
+
32
+ # PATCH/PUT /webring/members/1
33
+ def update
34
+ if @member.update(member_params)
35
+ redirect_to admin_panel_member_path(@member), notice: 'Member was successfully updated.'
36
+ else
37
+ render :edit, status: :unprocessable_entity
38
+ end
39
+ end
40
+
41
+ # DELETE /webring/members/1
42
+ def destroy
43
+ @member.destroy
44
+
45
+ redirect_to admin_panel_members_url, notice: 'Member was successfully destroyed.'
46
+ end
47
+
48
+ private
49
+
50
+ # Use callbacks to share common setup or constraints between actions.
51
+ def set_member
52
+ @member = Member.find_by!(uid: params[:id])
53
+ end
54
+
55
+ # Only allow a list of trusted parameters through.
56
+ def member_params
57
+ params.require(:member).permit(:name, :url)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,68 @@
1
+ module Webring
2
+ class NavigationController < ApplicationController
3
+ before_action :find_member, only: %i[next previous]
4
+ before_action :ensure_members_exist, :check_member_exists, only: [:random]
5
+
6
+ # GET /webring/next
7
+ def next
8
+ member = Webring::Member.find_next(@member.uid)
9
+
10
+ redirect_to_member(member)
11
+ end
12
+
13
+ # GET /webring/previous
14
+ def previous
15
+ member = Webring::Member.find_previous(@member.uid)
16
+
17
+ redirect_to_member(member)
18
+ end
19
+
20
+ # GET /webring/random
21
+ def random
22
+ member = Webring::Member.find_random(source_member_uid: permitted_params[:source_member_uid])
23
+
24
+ redirect_to_member(member)
25
+ end
26
+
27
+ private
28
+
29
+ def permitted_params
30
+ params.permit(:source_member_uid)
31
+ end
32
+
33
+ def redirect_to_member(member)
34
+ redirect_to member.url, allow_other_host: true
35
+ end
36
+
37
+ def find_member
38
+ @member = Webring::Member.find_by(uid: permitted_params[:source_member_uid])
39
+ return if @member
40
+
41
+ render_member_not_found
42
+ end
43
+
44
+ def check_member_exists
45
+ member_uid = permitted_params[:source_member_uid]
46
+ return unless member_uid.present? && !Webring::Member.exists?(uid: member_uid)
47
+
48
+ render_member_not_found
49
+ end
50
+
51
+ def ensure_members_exist
52
+ return if Webring::Member.exists?
53
+
54
+ render plain: 'No members in the webring', status: :not_found
55
+ end
56
+
57
+ def render_member_not_found
58
+ render plain: 'Member not found', status: :not_found
59
+ end
60
+
61
+ def set_cors_headers
62
+ headers['Access-Control-Allow-Origin'] = '*'
63
+ headers['Access-Control-Allow-Methods'] = 'GET, OPTIONS'
64
+ headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept'
65
+ headers['Access-Control-Max-Age'] = '86400'
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,31 @@
1
+ module Webring
2
+ class WidgetController < ::ApplicationController
3
+ # Disable CSRF protection for widget.js as it needs to be loaded from other domains
4
+ skip_forgery_protection only: :show
5
+
6
+ # Set CORS headers for the widget
7
+ before_action :set_cors_headers, only: :show
8
+
9
+ # Serve the webring navigation widget JavaScript
10
+ # GET /webring/widget.js
11
+ def show
12
+ respond_to do |format|
13
+ format.js do
14
+ response.headers['Content-Type'] = 'application/javascript'
15
+
16
+ # Serve the JavaScript file from the engine's assets
17
+ render file: Webring::Engine.root.join('app/assets/javascripts/webring/widget.js')
18
+ end
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def set_cors_headers
25
+ response.headers['Access-Control-Allow-Origin'] = '*'
26
+ response.headers['Access-Control-Allow-Methods'] = 'GET, OPTIONS'
27
+ response.headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept'
28
+ response.headers['Access-Control-Max-Age'] = '86400'
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,4 @@
1
+ module Webring
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Webring
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module Webring
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,60 @@
1
+ module Webring
2
+ # Requires model to have `uid` and `created_at` columns
3
+ module Navigation
4
+ # Find the next member in the webring after the current one
5
+ # If current member is the last, return the first member (ring concept)
6
+ def find_next(source_member_uid)
7
+ source_member = find_by(uid: source_member_uid)
8
+ return first_member_by_creation unless source_member
9
+
10
+ find_next_member(source_member) || first_member_by_creation
11
+ end
12
+
13
+ # Find the previous member in the webring before the current one
14
+ # If current member is the first, return the last member (ring concept)
15
+ def find_previous(source_member_uid)
16
+ source_member = find_by(uid: source_member_uid)
17
+ return last_member_by_creation unless source_member
18
+
19
+ find_previous_member(source_member) || last_member_by_creation
20
+ end
21
+
22
+ # Find a random member, excluding the current one if provided
23
+ # If current member is the only one, return it
24
+ def find_random(source_member_uid: nil)
25
+ return order('RANDOM()').first if source_member_uid.blank?
26
+
27
+ # Use exists? check to avoid loading records when not needed
28
+ excluded_scope = where.not(uid: source_member_uid)
29
+
30
+ if excluded_scope.exists?
31
+ excluded_scope.order('RANDOM()').first
32
+ else
33
+ # if only one member exists (the current one), return it
34
+ find_by(uid: source_member_uid)
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def first_member_by_creation
41
+ order(created_at: :asc).first
42
+ end
43
+
44
+ def last_member_by_creation
45
+ order(created_at: :desc).first
46
+ end
47
+
48
+ def find_next_member(source_member)
49
+ where('created_at > ?', source_member.created_at)
50
+ .order(created_at: :asc)
51
+ .first
52
+ end
53
+
54
+ def find_previous_member(source_member)
55
+ where('created_at < ?', source_member.created_at)
56
+ .order(created_at: :desc)
57
+ .first
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,5 @@
1
+ module Webring
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,31 @@
1
+ module Webring
2
+ class Member < ApplicationRecord
3
+ extend Webring::Navigation
4
+
5
+ UID_LENGTH = 16 # 32-character hex string
6
+
7
+ validates :url, presence: true, uniqueness: true
8
+ validates :name, uniqueness: true, if: -> { name.present? }
9
+ validates :uid, presence: true, uniqueness: true, length: { is: UID_LENGTH * 2 }
10
+
11
+ before_validation :generate_uid, if: -> { uid.blank? }
12
+ before_validation :set_name_from_url, if: -> { name.blank? && url.present? }
13
+
14
+ def to_param
15
+ uid
16
+ end
17
+
18
+ private
19
+
20
+ def generate_uid
21
+ loop do
22
+ self.uid = SecureRandom.hex(UID_LENGTH)
23
+ break unless self.class.exists?(uid: uid)
24
+ end
25
+ end
26
+
27
+ def set_name_from_url
28
+ self.name = url
29
+ end
30
+ end
31
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,8 @@
1
+ Webring::Engine.routes.draw do
2
+ get 'next', to: 'navigation#next'
3
+ get 'previous', to: 'navigation#previous'
4
+ get 'random', to: 'navigation#random'
5
+ get 'widget.js', to: 'widget#show', format: 'js', as: :widget
6
+
7
+ root to: 'navigation#random'
8
+ end
@@ -0,0 +1,25 @@
1
+ Description:
2
+ The webring generator creates the necessary structure for integrating webring
3
+ functionality into your Rails application.
4
+
5
+ Example:
6
+ rails generate webring:install
7
+
8
+ This will create:
9
+ config/initializers/webring.rb
10
+ # And mount the Webring engine in your routes.rb file
11
+
12
+ Example:
13
+ rails generate webring:member
14
+
15
+ This will create:
16
+ app/models/webring/member.rb
17
+ db/migrate/TIMESTAMP_create_webring_members.rb
18
+ # And add member routes to your routes.rb file
19
+
20
+ Example:
21
+ rails generate webring:controller
22
+
23
+ This will create:
24
+ app/controllers/webring/navigation_controller.rb
25
+ # And add navigation routes to your routes.rb file
@@ -0,0 +1,49 @@
1
+ require_relative '../shared/route_injector'
2
+
3
+ module Webring
4
+ module Generators
5
+ # @description The ControllerGenerator creates a NavigationController to handle webring navigation
6
+ # This generator creates both the controller file and adds the required routes
7
+ #
8
+ # @usage Run: rails generate webring:controller
9
+ #
10
+ # @example The generated controller provides three main navigation endpoints:
11
+ # # GET /webring/next - Navigate to the next site in the webring
12
+ # # GET /webring/previous - Navigate to the previous site in the webring
13
+ # # GET /webring/random - Navigate to a random site in the webring
14
+ #
15
+ # @note This generator should be run after installing the Webring engine and
16
+ # generating the Member model with webring:member
17
+ class ControllerGenerator < Rails::Generators::Base
18
+ include Shared::RouteInjector
19
+
20
+ source_root File.expand_path('templates', __dir__)
21
+
22
+ desc 'Creates a Webring::NavigationController and necessary routes for webring navigation'
23
+
24
+ # Creates the NavigationController file based on the template
25
+ # @return [void]
26
+ def create_controller_file
27
+ template 'navigation_controller.rb', 'app/controllers/webring/navigation_controller.rb'
28
+ end
29
+
30
+ # Adds navigation routes to the application's routes.rb file
31
+ # These routes are used to navigate between webring members
32
+ # @return [void]
33
+ def create_navigation_routes
34
+ route_content = <<~ROUTE
35
+ # Webring navigation routes
36
+ namespace :webring do
37
+ root to: 'navigation#random'
38
+
39
+ get 'next', to: 'navigation#next'
40
+ get 'previous', to: 'navigation#previous'
41
+ get 'random', to: 'navigation#random'
42
+ end
43
+ ROUTE
44
+
45
+ inject_webring_routes(route_content)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,61 @@
1
+ module Webring
2
+ class NavigationController < ApplicationController
3
+ before_action :find_member, only: %i[next previous]
4
+ before_action :ensure_members_exist, :check_member_exists, only: [:random]
5
+
6
+ # GET /webring/next
7
+ def next
8
+ member = Webring::Member.find_next(@member.uid)
9
+
10
+ redirect_to_member(member)
11
+ end
12
+
13
+ # GET /webring/previous
14
+ def previous
15
+ member = Webring::Member.find_previous(@member.uid)
16
+
17
+ redirect_to_member(member)
18
+ end
19
+
20
+ # GET /webring/random
21
+ def random
22
+ member = Webring::Member.find_random(source_member_uid: permitted_params[:source_member_uid])
23
+
24
+ redirect_to_member(member)
25
+ end
26
+
27
+ private
28
+
29
+ def permitted_params
30
+ params.permit(:source_member_uid)
31
+ end
32
+
33
+ def redirect_to_member(member)
34
+ redirect_to member.url, allow_other_host: true
35
+ end
36
+
37
+ def find_member
38
+ @member = Webring::Member.find_by(uid: permitted_params[:source_member_uid])
39
+ return if @member
40
+
41
+ render_member_not_found
42
+ end
43
+
44
+ def check_member_exists
45
+ member_uid = permitted_params[:source_member_uid]
46
+ return unless member_uid.present? && !Webring::Member.exists?(uid: member_uid)
47
+
48
+ render_member_not_found
49
+ end
50
+
51
+ def ensure_members_exist
52
+ return if Webring::Member.exists?
53
+
54
+ render plain: 'No members in the webring', status: :not_found
55
+ end
56
+
57
+ def render_member_not_found
58
+ render plain: 'Member not found', status: :not_found
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,31 @@
1
+ module Webring
2
+ module Generators
3
+ # @description The InstallGenerator is the first step to set up the Webring engine
4
+ # It mounts the Webring engine to your Rails application's routes
5
+ #
6
+ # @usage Run: rails generate webring:install
7
+ #
8
+ # @example After installation, the engine will be mounted at /webring
9
+ # # In your routes.rb
10
+ # mount Webring::Engine => '/webring', as: 'webring'
11
+ #
12
+ # @note You can change the mount path in your routes.rb file after installation
13
+ class InstallGenerator < Rails::Generators::Base
14
+ source_root File.expand_path('templates', __dir__)
15
+
16
+ desc 'Creates Webring routes and mounts the engine in your application.'
17
+
18
+ # Adds the engine mount point to the application's routes.rb file
19
+ # @return [void]
20
+ def add_webring_routes
21
+ route "mount Webring::Engine => '/webring', as: 'webring'\n\n"
22
+ end
23
+
24
+ # Displays the README with next steps after installation
25
+ # @return [void]
26
+ def show_readme
27
+ readme 'AFTER_INSTALL' if behavior == :invoke
28
+ end
29
+ end
30
+ end
31
+ end