webring-rails 1.1.0 → 1.2.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 (28) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -544
  3. data/app/controllers/webring/members_controller.rb +13 -12
  4. data/app/controllers/webring/membership_requests_controller.rb +23 -0
  5. data/app/models/concerns/webring/membership_request_actions.rb +28 -0
  6. data/app/models/webring/member.rb +1 -6
  7. data/app/models/webring/membership_request.rb +16 -0
  8. data/lib/generators/USAGE +17 -4
  9. data/lib/generators/webring/install/templates/AFTER_INSTALL +22 -3
  10. data/lib/generators/webring/member/member_generator.rb +1 -0
  11. data/lib/generators/webring/member/templates/AFTER_INSTALL +4 -11
  12. data/lib/generators/webring/member/templates/migration.rb +6 -5
  13. data/lib/generators/webring/member/templates/model.rb +1 -6
  14. data/lib/generators/webring/membership_request/membership_request_generator.rb +73 -0
  15. data/lib/generators/webring/membership_request/templates/AFTER_INSTALL +41 -0
  16. data/lib/generators/webring/membership_request/templates/migration.rb +15 -0
  17. data/lib/generators/webring/membership_request/templates/model.rb +29 -0
  18. data/lib/generators/webring/membership_request/templates/relations_migration.rb +5 -0
  19. data/lib/generators/webring/membership_requests_controller/membership_requests_controller_generator.rb +44 -0
  20. data/lib/generators/webring/membership_requests_controller/templates/membership_requests_controller.rb +23 -0
  21. data/lib/generators/webring/{controller/controller_generator.rb → navigation_controller/navigation_controller_generator.rb} +1 -1
  22. data/lib/generators/webring_generator.rb +1 -1
  23. data/lib/webring/version.rb +1 -1
  24. metadata +15 -7
  25. data/app/jobs/webring/application_job.rb +0 -4
  26. data/app/mailers/webring/application_mailer.rb +0 -6
  27. /data/lib/generators/webring/{controller → navigation_controller}/templates/navigation_controller.rb +0 -0
  28. /data/lib/{webring_rails.rb → webring-rails.rb} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b6068bf5f22b55c8e30fe5499cbafb35ac14bd888343fa43b130d8efc04f6e6
4
- data.tar.gz: b38877901600d82768c718481768236f2b11a892e2a1906c1e5bae2c56763374
3
+ metadata.gz: 85902566f8117884214215614a8dc3b89c0065d797fb0c4ab2863e4af823185b
4
+ data.tar.gz: 0c783b549c5e21ad829677d17d6413a083bed29b47b3dc75eacf08dd6ec205b8
5
5
  SHA512:
6
- metadata.gz: 883bea826f837c1b7e4a3fe6279a218bfb2741bc0617f0c3a8617eec2b72c3b05b23e3ace09429b35e7a1561ac0505cf612a7c574d45bc2291832e9c02c063d0
7
- data.tar.gz: 617bd8e61c560dd586ed445f0e3fb0c27a615dc4503bf2dc2436f0567cb3c17d4990c0f2d225ae7030ac7f40d1bc0f51115d0832fbd36ca63abad60f1a1718ac
6
+ metadata.gz: 9f7df96b07dcbacf10d092ffd1a9c3419c2a096517ae1986b1a13970fa3bb85114aff19bb05b6e9002ee87d9710201569c16254a0fbcf8ca31936938ad6b19de
7
+ data.tar.gz: 076dbbe45eacad535ae1940e229c6fc2abfa94f7afcea0f13aee58b437a62bfdb445e120dc090748f34960f8d09eca45fd8f9cbd8fc2ec94f6755f9a90757fac
data/README.md CHANGED
@@ -4,71 +4,19 @@
4
4
 
5
5
  Webring for Rails (webring_rails) is a flexible engine for creating and managing a webring system in your Ruby on Rails application. A webring is a collection of websites linked together in a circular structure, allowing visitors to navigate from one site to another.
6
6
 
7
- ## Table of Contents
8
-
9
- - [Features](#features)
10
- - [Requirements](#requirements)
11
- - [Installation](#installation)
12
- - [Quick Start](#quick-start)
13
- - [Manual Setup](#manual-setup)
14
- - [Configuration Options](#configuration-options)
15
- - [Setting Up Your Host Application](#setting-up-your-host-application)
16
- - [Usage](#usage)
17
- - [Generators](#generators)
18
- - [Installation Generator](#installation-generator)
19
- - [Member Model Generator](#member-model-generator)
20
- - [Navigation Controller Generator](#navigation-controller-generator)
21
- - [Basic Implementation](#basic-implementation)
22
- - [Customizing Your Webring](#customizing-your-webring)
23
- - [Models](#models)
24
- - [Webring::Member](#webringmember)
25
- - [Modules](#modules)
26
- - [Webring::Navigation](#webringnavigation)
27
- - [Key Features](#key-features)
28
- - [Available Methods](#available-methods)
29
- - [Usage in Models](#usage-in-models)
30
- - [Implementation Example](#implementation-example)
31
- - [Customizing Navigation Behavior](#customizing-navigation-behavior)
32
- - [Controllers](#controllers)
33
- - [Webring::NavigationController](#webringnavigationcontroller)
34
- - [How to Use the Navigation Controller](#how-to-use-the-navigation-controller)
35
- - [Customizing Navigation Behavior](#customizing-navigation-behavior-1)
36
- - [Webring::MembersController](#webringmemberscontroller)
37
- - [Widget](#widget)
38
- - [Webring::WidgetController](#webringwidgetcontroller)
39
- - [Widget.js](#widgetjs)
40
- - [How to Use the Widget](#how-to-use-the-widget)
41
- - [Widget Customization Options](#widget-customization-options)
42
- - [Example With All Options](#example-with-all-options)
43
- - [Multiple Widgets on the Same Page](#multiple-widgets-on-the-same-page)
44
- - [Development](#development)
45
- - [Migrations](#migrations)
46
- - [Testing](#testing)
47
- - [Contributing](#contributing)
48
- - [License](#license)
49
-
50
7
  ## Features
51
8
 
52
- Webring for Rails provides the following features:
53
-
54
9
  - Complete MVC structure for managing webring members
55
10
  - Circular navigation system between member websites
56
11
  - UID-based member identification for security
57
12
  - Embeddable JavaScript widget for easy member site integration
58
13
  - Customizable widget appearance and behavior
14
+ - Optional membership request system for sites to apply to join the webring
59
15
  - Generators for easy setup and customization
60
16
  - Extensible architecture for adding custom features
61
17
 
62
- ## Requirements
63
-
64
- - Ruby 2.7.0 or later
65
- - Rails 6.0 or later
66
- - ActiveRecord-compatible database (MySQL, PostgreSQL, SQLite)
67
-
68
18
  ## Installation
69
19
 
70
- ### Quick Start
71
-
72
20
  Add this line to your application's Gemfile:
73
21
 
74
22
  ```ruby
@@ -88,510 +36,28 @@ rails generate webring:install
88
36
  rails generate webring:member
89
37
 
90
38
  # Create the navigation controller
91
- rails generate webring:controller
92
-
93
- # Run migrations
94
- rails db:migrate
95
- ```
96
-
97
- ### Manual Setup
98
-
99
- If you prefer to set things up step by step:
100
-
101
- 1. Add the gem to your Gemfile:
102
-
103
- ```ruby
104
- gem 'webring_rails'
105
- ```
106
-
107
- 2. Install the gem:
108
-
109
- ```bash
110
- bundle install
111
- ```
112
-
113
- 3. Mount the engine in your routes:
114
-
115
- ```ruby
116
- # config/routes.rb
117
- Rails.application.routes.draw do
118
- mount Webring::Engine => "/webring"
119
-
120
- # Your other routes...
121
- end
39
+ rails generate webring:controller:navigation
122
40
  ```
123
41
 
124
- 4. Create necessary migrations:
42
+ ### Optional Features
125
43
 
126
44
  ```bash
127
- rails generate migration CreateWebringMembers uid:string name:string url:string
128
- ```
129
-
130
- 5. Configure your migration:
131
-
132
- ```ruby
133
- class CreateWebringMembers < ActiveRecord::Migration[6.0]
134
- def change
135
- create_table :webring_members do |t|
136
- t.string :uid, null: false, index: { unique: true }
137
- t.string :name, null: false, index: { unique: true }
138
- t.string :url, null: false, index: { unique: true }
45
+ # Enable the membership request system
46
+ rails generate webring:membership_request
139
47
 
140
- t.timestamps
141
- end
142
- end
143
- end
48
+ # Add membership request controller and routes
49
+ rails generate webring:controller:membership_requests
144
50
  ```
145
51
 
146
- 6. Run migrations:
52
+ Finally, run the migrations:
147
53
 
148
54
  ```bash
149
55
  rails db:migrate
150
56
  ```
151
57
 
152
- ### Configuration Options
153
-
154
- You can configure the webring engine by creating an initializer:
155
-
156
- ```ruby
157
- # config/initializers/webring.rb
158
- Webring.setup do |config|
159
- # Set the primary key type (uuid recommended for production)
160
- config.primary_key_type = :uuid
161
- end
162
- ```
163
-
164
- ### Setting Up Your Host Application
165
-
166
- To properly integrate the webring in your host application:
167
-
168
- 1. Set up default URL options for mailers in each environment:
169
-
170
- ```ruby
171
- # config/environments/development.rb
172
- config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
173
- ```
174
-
175
- 2. Add root route in your application:
176
-
177
- ```ruby
178
- # config/routes.rb
179
- root to: 'home#index'
180
- ```
181
-
182
- ## Usage
183
-
184
- ### Generators
185
-
186
- WebringRails provides several generators to help you set up webring functionality in your application.
187
-
188
- > [!IMPORTANT]
189
- > Make sure to run the generators in the order listed below for the best setup experience.
190
-
191
- #### Installation Generator
192
-
193
- ```bash
194
- rails generate webring:install
195
- ```
196
-
197
- This generator:
198
- - Mounts the Webring engine in your routes.rb
199
-
200
- #### Member Model Generator
201
-
202
- ```bash
203
- rails generate webring:member
204
- ```
205
-
206
- This generator:
207
- - Creates the Webring::Member model with uid, name and url fields
208
- - Creates a migration for the members table
209
- - Adds member routes to your routes.rb file
210
-
211
- #### Navigation Controller Generator
212
-
213
- ```bash
214
- rails generate webring:controller
215
- ```
216
-
217
- This generator:
218
- - Creates the Webring::NavigationController with '/next', '/previous', and '/random' actions
219
- - Adds navigation routes to your routes.rb file
220
-
221
- ### Basic Implementation
222
-
223
- After setting up the gem, you'll need to add members to your webring:
224
-
225
- ```ruby
226
- # Through the console or in a seed file
227
- Webring::Member.create(name: "My Awesome Site", url: "https://myawesomesite.com")
228
- Webring::Member.create(name: "Another Cool Site", url: "https://anothercoolsite.com")
229
- ```
230
-
231
- ### Customizing Your Webring
232
-
233
- You can customize various aspects of your webring:
234
-
235
- 1. Create custom views:
236
-
237
- ```bash
238
- rails generate webring:views
239
- ```
240
-
241
- 2. Create a custom navigation controller:
242
-
243
- ```ruby
244
- # app/controllers/custom_navigation_controller.rb
245
- class CustomNavigationController < Webring::NavigationController
246
- def next
247
- # Your custom implementation
248
- super
249
- end
250
- end
251
-
252
- # config/routes.rb
253
- get 'webring/next', to: 'custom_navigation#next'
254
- ```
255
-
256
- ## Models
257
-
258
- ### Webring::Member
259
-
260
- > [!NOTE]
261
- > The Member model is the core component of your webring, representing each site in the ring.
262
-
263
- The Member model includes:
264
-
265
- - Validations for presence and uniqueness of `url`, uniqueness of `name`, and presence and uniqueness of `uid`
266
- - Automatic generation of unique UID (32-character hex string) for each member
267
- - Automatic population of `name` from `url` if name is not provided
268
- - Navigation methods for finding next, previous, and random members:
269
- - `find_next(source_member_uid)` - Finds the next member after the given UID
270
- - `find_previous(source_member_uid)` - Finds the previous member before the given UID
271
- - `find_random(source_member_uid: nil)` - Finds a random member, excluding the source member
272
-
273
- #### Example Usage
274
-
275
- ```ruby
276
- # Creating a new member
277
- member = Webring::Member.create(url: "https://example.com", name: "Example Site")
278
-
279
- # Finding the next member
280
- next_member = Webring::Member.find_next(member.uid)
281
-
282
- # Finding the previous member
283
- prev_member = Webring::Member.find_previous(member.uid)
284
-
285
- # Finding a random member (excluding the current one)
286
- random_member = Webring::Member.find_random(source_member_uid: member.uid)
287
- ```
288
-
289
- ## Modules
290
-
291
- ### Webring::Navigation
292
-
293
- > [!TIP]
294
- > The Navigation module is designed to be highly extensible. You can easily override its methods to customize the navigation behavior for your specific needs.
295
-
296
- The Navigation module provides methods for navigating through members in a webring pattern. It's designed to be extended by models that act as webring members.
297
-
298
- #### Key Features
299
-
300
- - **Webring-style navigation**: Implements the circular navigation pattern typical of webrings
301
- - **UID-based member identification**: Uses unique identifiers instead of numeric IDs for security
302
- - **Creation time ordering**: Uses `created_at` timestamps to determine the sequence of members
303
- - **Edge case handling**: Properly handles wraparound at the beginning and end of the ring
304
-
305
- #### Available Methods
306
-
307
- The module provides three core methods:
308
-
309
- - `find_next(source_member_uid)`: Finds the next member in the ring after the specified member
310
- - If the source member is the last in the ring, it returns the first member
311
- - Uses creation time for navigation ordering
312
-
313
- - `find_previous(source_member_uid)`: Finds the previous member in the ring before the specified member
314
- - If the source member is the first in the ring, it returns the last member
315
- - Uses creation time for navigation ordering
316
-
317
- - `find_random(source_member_uid: nil)`: Finds a random member in the ring
318
- - If a source member UID is provided, it excludes that member from the selection
319
- - If the source member is the only one in the ring, it returns that same member
320
-
321
- #### Usage in Models
322
-
323
- > [!WARNING]
324
- > Make sure your model has `uid` and `created_at` columns before extending the Navigation module, as they're required for the default implementation.
325
-
326
- The module is designed to be extended in your model:
327
-
328
- ```ruby
329
- class YourMember < ApplicationRecord
330
- extend Webring::Navigation
331
-
332
- # Your model code...
333
- end
334
- ```
335
-
336
- #### Implementation Example
337
-
338
- The `Webring::Member` model extends the Navigation module:
339
-
340
- ```ruby
341
- module Webring
342
- class Member < ApplicationRecord
343
- extend Webring::Navigation
344
-
345
- # Member model implementation...
346
- end
347
- end
348
- ```
349
-
350
- This allows you to call navigation methods on the class:
351
-
352
- ```ruby
353
- # Find the next member after member with UID 'abc123...'
354
- next_member = Webring::Member.find_next('abc123...')
355
-
356
- # Find the previous member before member with UID 'abc123...'
357
- previous_member = Webring::Member.find_previous('abc123...')
358
-
359
- # Find a random member (excluding member with UID 'abc123...')
360
- random_member = Webring::Member.find_random(source_member_uid: 'abc123...')
361
- ```
362
-
363
- #### Customizing Navigation Behavior
364
-
365
- You can override the default navigation behavior by creating your own module that includes or extends the Webring::Navigation module:
366
-
367
- ```ruby
368
- module YourApp
369
- module CustomNavigation
370
- include Webring::Navigation
371
-
372
- # Override methods as needed
373
- def find_next(source_member_uid)
374
- # Custom implementation for finding the next member
375
- # For example, you might want to order by name instead of created_at
376
- source_member = find_by(uid: source_member_uid)
377
- return order(:name).first unless source_member
378
-
379
- where('name > ?', source_member.name)
380
- .order(:name)
381
- .first || order(:name).first
382
- end
383
- end
384
- end
385
- ```
386
-
387
- Then apply your custom navigation module to your model:
388
-
389
- ```ruby
390
- class YourMember < ApplicationRecord
391
- extend YourApp::CustomNavigation
392
-
393
- # Your model code...
394
- end
395
- ```
396
-
397
- **Note**: The Navigation module requires models to have `uid` and `created_at` columns for its default implementation.
398
-
399
- ## Controllers
400
-
401
- ### Webring::NavigationController
402
-
403
- > [!IMPORTANT]
404
- > The Navigation controller is responsible for handling all webring navigation requests and redirecting users between member sites.
405
-
406
- Navigation controller manages the webring navigation flow for end users visiting member sites:
407
-
408
- - `next` - Redirects to the next member in the webring
409
- - `previous` - Redirects to the previous member in the webring
410
- - `random` - Redirects to a random member in the webring
411
-
412
- This controller handles requests to navigate between members. When a navigation request is received with a source member's UID, the controller determines the appropriate member to navigate to and redirects the user to that member's URL.
413
-
414
- #### How to Use the Navigation Controller
415
-
416
- To use the navigation controller in your webring implementation, you need to create navigation links that point to your application's navigation routes with the `source_member_uid` parameter.
417
-
418
- Example routes:
419
- ```
420
- https://your-app.com/webring/previous?source_member_uid=abc123...
421
- https://your-app.com/webring/random?source_member_uid=abc123...
422
- https://your-app.com/webring/next?source_member_uid=abc123...
423
- ```
424
-
425
- Replace `abc123...` with the actual member UID and `https://your-app.com` with your application's domain.
426
-
427
- The controller handles several cases:
428
- - If the source member UID is invalid or not found, it returns a 404 "Member not found" response
429
- - If there are no members in the webring, it returns a 404 "No members in the webring" response
430
- - For `next` and `previous` actions, if the current member is the last or first respectively, it wraps around to the other end of the ring
431
- - For `random` action, it ensures the random member is not the same as the source member (unless there's only one member)
432
-
433
- #### Customizing Navigation Behavior
434
-
435
- If you want to customize the navigation behavior, you can create your own controller that inherits from the default navigation controller:
436
-
437
- ```ruby
438
- # app/controllers/my_navigation_controller.rb
439
- class MyNavigationController < Webring::NavigationController
440
- # Override methods and call super if needed
441
- def next
442
- # Your custom logic before default behavior
443
- super
444
- # `super` will redirect to member.url, so no code will be executed after this
445
- end
446
-
447
- def previous
448
- # Completely custom implementation
449
- @previous_member = Webring::Member.find_previous(params[:source_member_uid])
450
- redirect_to @previous_member.url, allow_other_host: true
451
- end
452
- end
453
- ```
454
-
455
- Then update your routes to use your custom controller:
456
-
457
- ```ruby
458
- # config/routes.rb
459
- Rails.application.routes.draw do
460
- # ... other routes ...
461
-
462
- # Override default webring routes with your custom controller
463
- get 'webring/next', to: 'my_navigation#next'
464
- get 'webring/previous', to: 'my_navigation#previous'
465
- get 'webring/random', to: 'my_navigation#random'
466
- end
467
- ```
468
-
469
- ### Webring::MembersController
470
-
471
- Members controller provides full CRUD functionality for managing webring members:
472
-
473
- - `index` - Lists all webring members
474
- - `show` - Displays details for a specific member
475
- - `new` - Form for creating a new member
476
- - `create` - Creates a new webring member
477
- - `edit` - Form for editing an existing member
478
- - `update` - Updates an existing webring member
479
- - `destroy` - Deletes a webring member
480
-
481
- This controller provides the basic functionality for your application to manage webring membership.
482
-
483
- ## Widget
484
-
485
- ### Webring::WidgetController
486
-
487
- The Widget controller serves a JavaScript widget that can be embedded on member sites to provide navigation through the webring.
488
-
489
- - `show` - Serves the widget.js JavaScript file with proper CORS headers
490
-
491
- ### Widget.js
492
-
493
- The widget.js file is a standalone JavaScript snippet that creates a navigation widget on member sites. It's designed to be easily embedded with a single script tag and offers various customization options.
494
-
495
- #### How to Use the Widget
496
-
497
- To add the webring widget to a member's site, include the following code:
498
-
499
- ```html
500
- <!-- Webring Widget -->
501
- <script src='https://yourhub.com/webring/widget.js' data-member-uid='MEMBER_UID'></script>
502
- <div id='webring-widget'></div>
503
- <!-- End Webring Widget -->
504
- ```
505
-
506
- Replace `https://yourhub.com` with your application's domain and `MEMBER_UID` with the member's unique identifier.
507
-
508
- #### Widget Customization Options
509
-
510
- The widget can be customized through data attributes on the script tag:
511
-
512
- - **Widget Type** (`data-widget-type`):
513
- - `full`: Text, back button, random button, forward button (default)
514
- - `no-text`: Back button, random button, forward button (no text)
515
- - `two-way`: Back and forward buttons (no random)
516
- - `one-way`: Forward button only
517
-
518
- - **Button Text** (`data-button-text`):
519
- - `true`: Show text labels on buttons (default)
520
- - `false`: Show only symbols (no text)
521
-
522
- - **Styling** (`data-styles`):
523
- - `full`: Apply all styles (default)
524
- - `layout`: Only layout styles, no visual design
525
- - `none`: No styles applied
526
-
527
- - **Target Element** (`data-target-id`):
528
- - Sets a custom ID for the target container (default: `webring-widget`)
529
-
530
- #### Example With All Options
531
-
532
- ```html
533
- <script src='https://yourhub.com/webring/widget.js'
534
- data-member-uid='MEMBER_UID'
535
- data-widget-type='full'
536
- data-button-text='true'
537
- data-styles='full'
538
- data-target-id='custom-widget-id'></script>
539
- <div id='custom-widget-id'></div>
540
- ```
541
-
542
- #### Multiple Widgets on the Same Page
543
-
544
- You can include multiple widgets on the same page by specifying different target IDs:
545
-
546
- ```html
547
- <!-- First Widget -->
548
- <script src='https://yourhub.com/webring/widget.js'
549
- data-member-uid='MEMBER_UID'
550
- data-widget-type='full'
551
- data-target-id='webring-widget-1'></script>
552
- <div id='webring-widget-1'></div>
553
-
554
- <!-- Second Widget -->
555
- <script src='https://yourhub.com/webring/widget.js'
556
- data-member-uid='MEMBER_UID'
557
- data-widget-type='no-text'
558
- data-target-id='webring-widget-2'></script>
559
- <div id='webring-widget-2'></div>
560
- ```
561
-
562
- ## Development
563
-
564
- To set up the development environment:
565
-
566
- 1. Clone this repository
567
- 2. Run `bundle install`
568
- 3. Run `bin/setup_dummy` to prepare the test dummy app
569
-
570
- ### Migrations
571
-
572
- After creating a new migration in the engine, you need to run:
573
- ```bash
574
- cd test/dummy
575
- bin/rails db:migrate
576
- ```
577
- Which will run the migrations in the dummy app.
578
- You don't need to copy migrations to the dummy app, it looks for them in the engine.
579
-
580
- ### Testing
581
-
582
- > [!WARNING]
583
- > As of now, there are no tests. Contributions to add test coverage are welcome!
584
-
585
- ## Contributing
586
-
587
- We welcome contributions to Webring for Rails! Here's how you can help:
58
+ ## Documentation
588
59
 
589
- 1. Fork the repository
590
- 2. Create a feature branch: `git checkout -b my-new-feature`
591
- 3. Make your changes and add tests if possible
592
- 4. Commit your changes: `git commit -am 'Add some feature'`
593
- 5. Push to the branch: `git push origin my-new-feature`
594
- 6. Submit a pull request
60
+ For detailed documentation on models, controllers, modules, widget customization, and more advanced installation options, please visit the [Webring Rails Wiki](https://github.com/lstpsche/webring-rails/wiki).
595
61
 
596
62
  ## License
597
63
 
@@ -1,11 +1,10 @@
1
1
  module Webring
2
2
  class MembersController < ::ApplicationController
3
- before_action :set_member, only: %i[show edit update destroy]
3
+ before_action :members, only: %i[index]
4
+ before_action :member, only: %i[show edit update destroy]
4
5
 
5
6
  # GET /webring/members
6
- def index
7
- @members = Member.all.order(created_at: :desc)
8
- end
7
+ def index; end
9
8
 
10
9
  # GET /webring/members/1
11
10
  def show; end
@@ -31,8 +30,8 @@ module Webring
31
30
 
32
31
  # PATCH/PUT /webring/members/1
33
32
  def update
34
- if @member.update(member_params)
35
- redirect_to admin_panel_member_path(@member), notice: 'Member was successfully updated.'
33
+ if member.update(member_params)
34
+ redirect_to admin_panel_member_path(member), notice: 'Member was successfully updated.'
36
35
  else
37
36
  render :edit, status: :unprocessable_entity
38
37
  end
@@ -40,21 +39,23 @@ module Webring
40
39
 
41
40
  # DELETE /webring/members/1
42
41
  def destroy
43
- @member.destroy
42
+ member.destroy
44
43
 
45
44
  redirect_to admin_panel_members_url, notice: 'Member was successfully destroyed.'
46
45
  end
47
46
 
48
47
  private
49
48
 
50
- # Use callbacks to share common setup or constraints between actions.
51
- def set_member
52
- @member = Member.find_by!(uid: params[:id])
49
+ def members
50
+ @members ||= Member.all.order(created_at: :desc)
51
+ end
52
+
53
+ def member
54
+ @member ||= Member.find_by!(uid: params[:id])
53
55
  end
54
56
 
55
- # Only allow a list of trusted parameters through.
56
57
  def member_params
57
- params.require(:member).permit(:name, :url)
58
+ params.require(:member).permit(:name, :url, :description)
58
59
  end
59
60
  end
60
61
  end
@@ -0,0 +1,23 @@
1
+ module Webring
2
+ class MembershipRequestsController < ApplicationController
3
+ def new
4
+ @membership_request = MembershipRequest.new
5
+ end
6
+
7
+ def create
8
+ @membership_request = MembershipRequest.new(membership_request_params)
9
+
10
+ if @membership_request.save
11
+ redirect_to root_path
12
+ else
13
+ render :new, status: :unprocessable_entity
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def membership_request_params
20
+ params.require(:membership_request).permit(:name, :url, :callback_email, :description)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ module Webring
2
+ module MembershipRequestActions
3
+ extend ActiveSupport::Concern
4
+
5
+ def approve!
6
+ return if approved?
7
+
8
+ transaction do
9
+ update!(status: :approved)
10
+
11
+ Webring::Member.create!(
12
+ name: name,
13
+ url: url,
14
+ description: description,
15
+ webring_membership_request_id: id
16
+ )
17
+ end
18
+ end
19
+
20
+ def reject!
21
+ return if rejected?
22
+
23
+ transaction do
24
+ update!(status: :rejected)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -5,11 +5,10 @@ module Webring
5
5
  UID_LENGTH = 16 # 32-character hex string
6
6
 
7
7
  validates :url, presence: true, uniqueness: true
8
- validates :name, uniqueness: true, if: -> { name.present? }
8
+ validates :name, presence: true, uniqueness: true
9
9
  validates :uid, presence: true, uniqueness: true, length: { is: UID_LENGTH * 2 }
10
10
 
11
11
  before_validation :generate_uid, if: -> { uid.blank? }
12
- before_validation :set_name_from_url, if: -> { name.blank? && url.present? }
13
12
 
14
13
  def to_param
15
14
  uid
@@ -23,9 +22,5 @@ module Webring
23
22
  break unless self.class.exists?(uid: uid)
24
23
  end
25
24
  end
26
-
27
- def set_name_from_url
28
- self.name = url
29
- end
30
25
  end
31
26
  end
@@ -0,0 +1,16 @@
1
+ module Webring
2
+ class MembershipRequest < ApplicationRecord
3
+ include Webring::MembershipRequestActions
4
+
5
+ has_one :member, class_name: 'Webring::Member',
6
+ foreign_key: :webring_membership_request_id,
7
+ dependent: :nullify,
8
+ inverse_of: :membership_request
9
+
10
+ validates :url, presence: true, uniqueness: true
11
+ validates :name, presence: true
12
+ validates :description, presence: true
13
+
14
+ enum :status, { pending: 0, approved: 1, rejected: 2 }, default: :pending
15
+ end
16
+ end
data/lib/generators/USAGE CHANGED
@@ -6,8 +6,7 @@ Example:
6
6
  rails generate webring:install
7
7
 
8
8
  This will create:
9
- config/initializers/webring.rb
10
- # And mount the Webring engine in your routes.rb file
9
+ # Mount the Webring engine in your routes.rb file
11
10
 
12
11
  Example:
13
12
  rails generate webring:member
@@ -15,11 +14,25 @@ Example:
15
14
  This will create:
16
15
  app/models/webring/member.rb
17
16
  db/migrate/TIMESTAMP_create_webring_members.rb
18
- # And add member routes to your routes.rb file
19
17
 
20
18
  Example:
21
- rails generate webring:controller
19
+ rails generate webring:membership_request
20
+
21
+ This will create:
22
+ app/models/webring/membership_request.rb
23
+ db/migrate/TIMESTAMP_create_webring_membership_requests.rb
24
+ db/migrate/TIMESTAMP_add_membership_request_to_webring_members.rb
25
+
26
+ Example:
27
+ rails generate webring:navigation_controller
22
28
 
23
29
  This will create:
24
30
  app/controllers/webring/navigation_controller.rb
25
31
  # And add navigation routes to your routes.rb file
32
+
33
+ Example:
34
+ rails generate webring:membership_requests_controller
35
+
36
+ This will create:
37
+ app/controllers/webring/membership_requests_controller.rb
38
+ # And add membership request routes to your routes.rb file
@@ -33,7 +33,26 @@ This will:
33
33
  - GET /webring/previous - Navigate to the previous site
34
34
  - GET /webring/random - Navigate to a random site
35
35
 
36
- 3. Customize Your Routes [Optional]
36
+ 3. Enable Membership Requests [Optional]
37
+ ----------------------------------------
38
+ Generate a membership request model to allow sites to request joining your webring:
39
+
40
+ $ rails generate webring:membership_request
41
+
42
+ This will:
43
+ - Create a Webring::MembershipRequest model in app/models/webring/membership_request.rb
44
+ - Create migrations to set up the webring_membership_requests table
45
+ - Add appropriate database indexes and relations to Webring::Member
46
+
47
+ After generating the model, run the migration:
48
+
49
+ $ rails db:migrate
50
+
51
+ Then, optionally generate a controller to handle membership requests:
52
+
53
+ $ rails generate webring:controller:membership_requests
54
+
55
+ 4. Customize Your Routes [Optional]
37
56
  ----------------------------------
38
57
  The engine is mounted at '/webring' by default. You can change this in your routes.rb file:
39
58
 
@@ -43,7 +62,7 @@ The engine is mounted at '/webring' by default. You can change this in your rout
43
62
  # Example custom mount point
44
63
  mount Webring::Engine => '/network', as: 'webring'
45
64
 
46
- 4. Add Webring Navigation UI Elements
65
+ 5. Add Webring Navigation UI Elements
47
66
  ------------------------------------
48
67
  Add navigation links to your views to enable users to navigate through the webring:
49
68
 
@@ -54,7 +73,7 @@ Add navigation links to your views to enable users to navigate through the webri
54
73
  Note: Replace 'your_member.uid' with the actual UID of your site in the webring.
55
74
  If you're building a standalone webring hub, you can omit the source_member_uid parameter.
56
75
 
57
- 5. Managing Webring Members
76
+ 6. Managing Webring Members
58
77
  --------------------------
59
78
  You can add members to your webring using the Webring::Member model:
60
79
 
@@ -13,6 +13,7 @@ module Webring
13
13
  # # uid - A unique identifier for the member (automatically generated)
14
14
  # # name - The name of the member site (defaults to URL if not provided)
15
15
  # # url - The URL of the member site (required)
16
+ # # description - The description of the member site (required)
16
17
  #
17
18
  # @note After running this generator, you should run the migration with:
18
19
  # rails db:migrate
@@ -18,21 +18,14 @@ The generated model is located at:
18
18
 
19
19
  The Member model has the following attributes:
20
20
  - uid: A unique identifier automatically generated for each member (32-character hex)
21
- - name: The name of the member site (optional, defaults to URL if not provided)
21
+ - name: The name of the member site (required)
22
22
  - url: The URL of the member site (required)
23
+ - description: The description of the member site (required)
23
24
 
24
25
  3. Adding Members to Your Webring
25
26
  -------------------------------
26
27
  You can add members to your webring using the Webring::Member model:
27
28
 
28
- Webring::Member.create(name: 'Example Site', url: 'https://example.com')
29
+ Webring::Member.create(name: 'Example Site', url: 'https://example.com', description: 'Example description')
29
30
 
30
- The uid will be automatically generated if not provided.
31
-
32
- 4. Next Steps
33
- -----------
34
- Consider generating the navigation controller to enable navigation between webring members:
35
-
36
- $ rails generate webring:controller
37
-
38
- This will create routes and controller actions for webring navigation.
31
+ The uid will be automatically generated if not provided. It is recommended to leave the UID generation to the model.
@@ -2,14 +2,15 @@ class CreateWebringMembers < ActiveRecord::Migration<%= "[#{Rails::VERSION::MAJO
2
2
  def change
3
3
  create_table :webring_members do |t|
4
4
  t.string :uid, null: false, limit: 32
5
- t.string :name
5
+ t.string :name, null: false
6
6
  t.string :url, null: false
7
+ t.text :description, null: false
8
+
9
+ t.index :uid, unique: true
10
+ t.index :name, unique: true
11
+ t.index :url, unique: true
7
12
 
8
13
  t.timestamps
9
14
  end
10
-
11
- add_index :webring_members, :uid, unique: true
12
- add_index :webring_members, :name, unique: true
13
- add_index :webring_members, :url, unique: true
14
15
  end
15
16
  end
@@ -5,11 +5,10 @@ module Webring
5
5
  UID_LENGTH = 16 # 32-character hex string
6
6
 
7
7
  validates :url, presence: true, uniqueness: true
8
- validates :name, uniqueness: true, if: -> { name.present? }
8
+ validates :name, presence: true, uniqueness: true
9
9
  validates :uid, presence: true, uniqueness: true, length: { is: UID_LENGTH * 2 }
10
10
 
11
11
  before_validation :generate_uid, if: -> { uid.blank? }
12
- before_validation :set_name_from_url, if: -> { name.blank? && url.present? }
13
12
 
14
13
  def to_param
15
14
  uid
@@ -23,9 +22,5 @@ module Webring
23
22
  break unless self.class.exists?(uid: uid)
24
23
  end
25
24
  end
26
-
27
- def set_name_from_url
28
- self.name = url
29
- end
30
25
  end
31
26
  end
@@ -0,0 +1,73 @@
1
+ require 'rails/generators/active_record'
2
+ require 'rails/generators/named_base'
3
+ require_relative '../shared/route_injector'
4
+
5
+ module Webring
6
+ module Generators
7
+ # @description The MembershipRequestGenerator creates the MembershipRequest model for handling webring membership requests
8
+ # It creates both the model file and a migration to create the database table
9
+ #
10
+ # @usage Run: rails generate webring:membership_request
11
+ #
12
+ # @example Generated MembershipRequest model has the following attributes:
13
+ # # name - The name of the member site
14
+ # # url - The URL of the member site (required)
15
+ # # callback_email - Email for notifications
16
+ # # description - Description of the site
17
+ #
18
+ # @note After running this generator, you should run the migration with:
19
+ # rails db:migrate
20
+ class MembershipRequestGenerator < Rails::Generators::Base
21
+ include Rails::Generators::Migration
22
+ include Shared::RouteInjector
23
+
24
+ source_root File.expand_path('templates', __dir__)
25
+
26
+ desc 'Creates a Webring::MembershipRequest model and necessary migration for storing webring membership requests'
27
+
28
+ # Creates a migration file to create the webring_membership_requests table
29
+ # @return [void]
30
+ def create_migration_file
31
+ migration_template 'migration.rb', 'db/migrate/create_webring_membership_requests.rb'
32
+ migration_template 'relations_migration.rb', 'db/migrate/add_membership_request_to_webring_members.rb'
33
+ end
34
+
35
+ # Creates the MembershipRequest model file based on the template
36
+ # @return [void]
37
+ def create_model_file
38
+ template 'model.rb', 'app/models/webring/membership_request.rb'
39
+ end
40
+
41
+ # Injects the belongs_to relationship into the Member model
42
+ # @return [void]
43
+ def inject_into_member_model
44
+ member_model_path = 'app/models/webring/member.rb'
45
+
46
+ if File.exist?(member_model_path)
47
+ inject_into_file member_model_path, after: "class Member\n" do
48
+ " belongs_to :membership_request,\n" \
49
+ " class_name: 'Webring::MembershipRequest',\n" \
50
+ " foreign_key: :webring_membership_request_id,\n" \
51
+ " optional: true,\n" \
52
+ " inverse_of: :member\n"
53
+ end
54
+ end
55
+ end
56
+
57
+ # Generates the next migration number for the migration file
58
+ # This is required by Rails::Generators::Migration
59
+ # @param dirname [String] The directory where migrations are stored
60
+ # @return [Integer] The next migration number
61
+ def self.next_migration_number(dirname)
62
+ next_migration_number = current_migration_number(dirname) + 1
63
+ ActiveRecord::Migration.next_migration_number(next_migration_number)
64
+ end
65
+
66
+ # Displays the README with next steps after installation
67
+ # @return [void]
68
+ def show_readme
69
+ readme 'AFTER_INSTALL' if behavior == :invoke
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,41 @@
1
+ ==============================================
2
+ Webring::MembershipRequest Model Has Been Generated!
3
+ ==============================================
4
+
5
+ The MembershipRequest model is the core component for handling new site requests to join your webring. Follow these steps to complete the setup:
6
+
7
+ 1. Run Database Migration
8
+ ------------------------
9
+ Apply the migration to create the webring_membership_requests table:
10
+
11
+ $ rails db:migrate
12
+
13
+ 2. Review and Customize the Model
14
+ -------------------------------
15
+ The generated model is located at:
16
+ app/models/webring/membership_request.rb
17
+
18
+ The MembershipRequest model has the following attributes:
19
+ - name: The name of the requesting site (required)
20
+ - url: The URL of the requesting site (required)
21
+ - callback_email: The email address to send notifications to (required)
22
+ - description: The description of the requesting site (required)
23
+ - status: The status of the request (pending, approved, rejected)
24
+
25
+ 3. Generate Controller [Optional]
26
+ --------------------------------
27
+ Generate a controller to handle membership requests:
28
+
29
+ $ rails generate webring:controller:membership_requests
30
+
31
+ This will:
32
+ - Create the Webring::MembershipRequestsController in app/controllers/webring/membership_requests_controller.rb
33
+ - Add membership request routes to your routes.rb file:
34
+ - GET /webring/membership_requests/new - Renders the view for submitting a new membership request (view is your responsibility)
35
+ - POST /webring/membership_requests - Creates a new membership request
36
+
37
+ 4. Managing Membership Requests
38
+ -------------------------------
39
+ You can manage membership requests using the Webring::MembershipRequest model:
40
+
41
+ Webring::MembershipRequest.create(name: 'Example Site', url: 'https://example.com', description: 'Example description', callback_email: 'example@example.com')
@@ -0,0 +1,15 @@
1
+ class CreateWebringMembershipRequests < ActiveRecord::Migration<%= "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" %>
2
+ def change
3
+ create_table :webring_membership_requests do |t|
4
+ t.string :name, null: false
5
+ t.string :url, null: false
6
+ t.text :description, null: false
7
+ t.string :callback_email, null: false
8
+ t.integer :status, default: 0
9
+
10
+ t.index :url, unique: true
11
+
12
+ t.timestamps
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ module Webring
2
+ class MembershipRequest < ApplicationRecord
3
+ has_one :member, class_name: 'Webring::Member',
4
+ foreign_key: :webring_membership_request_id,
5
+ dependent: :nullify,
6
+ inverse_of: :membership_request
7
+
8
+ validates :url, presence: true, uniqueness: true
9
+ validates :name, presence: true
10
+ validates :description, presence: true
11
+
12
+ enum :status, { pending: 0, approved: 1, rejected: 2 }, default: :pending
13
+
14
+ def approve!
15
+ return if approved?
16
+
17
+ transaction do
18
+ update!(status: :approved)
19
+
20
+ Webring::Member.create!(
21
+ name: name,
22
+ url: url,
23
+ description: description,
24
+ webring_membership_request_id: id
25
+ )
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,5 @@
1
+ class AddMembershipRequestToWebringMembers < ActiveRecord::Migration<%= "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" %>
2
+ def change
3
+ add_reference :webring_members, :webring_membership_request, null: true, foreign_key: true
4
+ end
5
+ end
@@ -0,0 +1,44 @@
1
+ require_relative '../shared/route_injector'
2
+
3
+ module Webring
4
+ module Generators
5
+ # @description The MembershipRequestsControllerGenerator creates a controller to handle webring membership requests
6
+ # This generator creates both the controller file and adds the required routes
7
+ #
8
+ # @usage Run: rails generate webring:controller:membership_requests
9
+ #
10
+ # @example The generated controller provides endpoints for creating membership requests:
11
+ # # GET /webring/membership_requests/new - Controller for creating a new membership request
12
+ # # POST /webring/membership_requests - Create a new membership request
13
+ #
14
+ # @note This generator should be run after installing the Webring engine and
15
+ # generating the MembershipRequest model with webring:membership_request
16
+ class MembershipRequestsControllerGenerator < Rails::Generators::Base
17
+ include Shared::RouteInjector
18
+
19
+ source_root File.expand_path('templates', __dir__)
20
+
21
+ desc 'Creates a Webring::MembershipRequestsController and necessary routes for membership requests'
22
+
23
+ # Creates the MembershipRequestsController file based on the template
24
+ # @return [void]
25
+ def create_controller_file
26
+ template 'membership_requests_controller.rb', 'app/controllers/webring/membership_requests_controller.rb'
27
+ end
28
+
29
+ # Adds membership request routes to the application's routes.rb file
30
+ # These routes are used to create new membership requests
31
+ # @return [void]
32
+ def create_membership_request_routes
33
+ route_content = <<~ROUTE
34
+ # Webring membership request routes
35
+ namespace :webring do
36
+ resources :membership_requests, only: [:new, :create]
37
+ end
38
+ ROUTE
39
+
40
+ inject_webring_routes(route_content)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,23 @@
1
+ module Webring
2
+ class MembershipRequestsController < ApplicationController
3
+ def new
4
+ @membership_request = MembershipRequest.new
5
+ end
6
+
7
+ def create
8
+ @membership_request = MembershipRequest.new(membership_request_params)
9
+
10
+ if @membership_request.save
11
+ redirect_to root_path, notice: 'Your membership request has been submitted!'
12
+ else
13
+ render :new, status: :unprocessable_entity
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def membership_request_params
20
+ params.require(:membership_request).permit(:name, :url, :callback_email, :description)
21
+ end
22
+ end
23
+ end
@@ -14,7 +14,7 @@ module Webring
14
14
  #
15
15
  # @note This generator should be run after installing the Webring engine and
16
16
  # generating the Member model with webring:member
17
- class ControllerGenerator < Rails::Generators::Base
17
+ class NavigationControllerGenerator < Rails::Generators::Base
18
18
  include Shared::RouteInjector
19
19
 
20
20
  source_root File.expand_path('templates', __dir__)
@@ -10,7 +10,7 @@ module Webring
10
10
  end
11
11
 
12
12
  def invoke_webring_controller
13
- invoke 'webring:controller'
13
+ invoke 'webring:member_controller'
14
14
  end
15
15
  end
16
16
  end
@@ -1,3 +1,3 @@
1
1
  module Webring
2
- VERSION = '1.1.0'.freeze
2
+ VERSION = '1.2.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: webring-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nikita Shkoda
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-06-04 00:00:00.000000000 Z
11
+ date: 2025-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -65,29 +65,37 @@ files:
65
65
  - app/assets/javascripts/webring/widget.js
66
66
  - app/controllers/webring/application_controller.rb
67
67
  - app/controllers/webring/members_controller.rb
68
+ - app/controllers/webring/membership_requests_controller.rb
68
69
  - app/controllers/webring/navigation_controller.rb
69
70
  - app/controllers/webring/widget_controller.rb
70
71
  - app/helpers/webring/application_helper.rb
71
- - app/jobs/webring/application_job.rb
72
- - app/mailers/webring/application_mailer.rb
72
+ - app/models/concerns/webring/membership_request_actions.rb
73
73
  - app/models/concerns/webring/navigation.rb
74
74
  - app/models/webring/application_record.rb
75
75
  - app/models/webring/member.rb
76
+ - app/models/webring/membership_request.rb
76
77
  - config/routes.rb
77
78
  - lib/generators/USAGE
78
- - lib/generators/webring/controller/controller_generator.rb
79
- - lib/generators/webring/controller/templates/navigation_controller.rb
80
79
  - lib/generators/webring/install/install_generator.rb
81
80
  - lib/generators/webring/install/templates/AFTER_INSTALL
82
81
  - lib/generators/webring/member/member_generator.rb
83
82
  - lib/generators/webring/member/templates/AFTER_INSTALL
84
83
  - lib/generators/webring/member/templates/migration.rb
85
84
  - lib/generators/webring/member/templates/model.rb
85
+ - lib/generators/webring/membership_request/membership_request_generator.rb
86
+ - lib/generators/webring/membership_request/templates/AFTER_INSTALL
87
+ - lib/generators/webring/membership_request/templates/migration.rb
88
+ - lib/generators/webring/membership_request/templates/model.rb
89
+ - lib/generators/webring/membership_request/templates/relations_migration.rb
90
+ - lib/generators/webring/membership_requests_controller/membership_requests_controller_generator.rb
91
+ - lib/generators/webring/membership_requests_controller/templates/membership_requests_controller.rb
92
+ - lib/generators/webring/navigation_controller/navigation_controller_generator.rb
93
+ - lib/generators/webring/navigation_controller/templates/navigation_controller.rb
86
94
  - lib/generators/webring/shared/route_injector.rb
87
95
  - lib/generators/webring_generator.rb
96
+ - lib/webring-rails.rb
88
97
  - lib/webring/engine.rb
89
98
  - lib/webring/version.rb
90
- - lib/webring_rails.rb
91
99
  homepage: https://github.com/lstpsche/webring_rails
92
100
  licenses:
93
101
  - MIT
@@ -1,4 +0,0 @@
1
- module Webring
2
- class ApplicationJob < ActiveJob::Base
3
- end
4
- end
@@ -1,6 +0,0 @@
1
- module Webring
2
- class ApplicationMailer < ActionMailer::Base
3
- default from: 'from@example.com'
4
- layout 'mailer'
5
- end
6
- end
File without changes