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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5d8bb8161ded15c412058604725f975b08e52aaeb3f4fa49c1765d91032f97d8
4
+ data.tar.gz: 3f4aae57727c9dfc298b56d58e5c99700698d457734244107353c2c67dd77f52
5
+ SHA512:
6
+ metadata.gz: c82d035d4746a60f5b3c86f85eb8c26c239cb987b589c9a278dd10882d08b583604c088d783c5de0bb9b5e50b0948421c892de34c61af31031fcc969c578e886
7
+ data.tar.gz: 8d99daf9103b6cc966a5b158ae199dc03bee8f066e8ed1e87f75d9c1f00540ecf1fa2c47ea58fc9a0b80d28ec76e814df1251765d66909ee408acf6602244bcd
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright Nikita Silivonchik
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,598 @@
1
+ ![Webring Widget Sample](widget-sample.jpeg)
2
+
3
+ # Webring for Rails
4
+
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
+
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
+ ## Features
51
+
52
+ Webring for Rails provides the following features:
53
+
54
+ - Complete MVC structure for managing webring members
55
+ - Circular navigation system between member websites
56
+ - UID-based member identification for security
57
+ - Embeddable JavaScript widget for easy member site integration
58
+ - Customizable widget appearance and behavior
59
+ - Generators for easy setup and customization
60
+ - Extensible architecture for adding custom features
61
+
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
+ ## Installation
69
+
70
+ ### Quick Start
71
+
72
+ Add this line to your application's Gemfile:
73
+
74
+ ```ruby
75
+ gem 'webring_rails'
76
+ ```
77
+
78
+ Run:
79
+
80
+ ```bash
81
+ # Install the gem
82
+ bundle install
83
+
84
+ # Run the installation generator
85
+ rails generate webring:install
86
+
87
+ # Create the member model and migrations
88
+ rails generate webring:member
89
+
90
+ # 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
122
+ ```
123
+
124
+ 4. Create necessary migrations:
125
+
126
+ ```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 }
139
+
140
+ t.timestamps
141
+ end
142
+ end
143
+ end
144
+ ```
145
+
146
+ 6. Run migrations:
147
+
148
+ ```bash
149
+ rails db:migrate
150
+ ```
151
+
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:
588
+
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
595
+
596
+ ## License
597
+
598
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).