concerns_on_rails 1.6.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +104 -3
- data/lib/concerns_on_rails/controllers/error_handleable.rb +72 -0
- data/lib/concerns_on_rails/legacy_aliases.rb +2 -0
- data/lib/concerns_on_rails/models/activatable.rb +65 -0
- data/lib/concerns_on_rails/models/searchable.rb +58 -0
- data/lib/concerns_on_rails/version.rb +1 -1
- data/lib/concerns_on_rails.rb +3 -0
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7bdaf4512b253a9c3b7307ace69f16d616f180d38ce3a3f5292099f0c97dedb5
|
|
4
|
+
data.tar.gz: de2843d32ebd1cea0e3c9fce3e8f5d6d18adaa6eb6e92ce4056af3bb2ab4d368
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 315ff80a677ef032630d0b81f8c0b5c3b88d132a288ad075ed00732e2d676ccef954725c8966231af793ee89dc2485cb48e6b0c1de03ea93d47c7f283545815d
|
|
7
|
+
data.tar.gz: 13c59b78fc2b9dbd8dfca9877550a3a7fcd778fca493dd1b97c9151f9c4d111f94aa8eb3d8692b9b7e0299ccd52bf270944dee2a6987aae3c88917532cce2404
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
<!-- CHANGELOG.md -->
|
|
2
2
|
|
|
3
|
+
## 1.7.0 (2026-05-21)
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **Models::Searchable**: LIKE-based search across one or more columns via `searchable_by :title, :body`. Adds a `.search(query)` scope that uses Arel's `matches` (emits `ILIKE` on Postgres, `LIKE` elsewhere), ORs predicates across all configured fields, escapes user input so `%` / `_` / `\` are treated as literals, and returns the full relation for blank queries.
|
|
7
|
+
- **Models::Activatable**: Boolean active/inactive toggle via `activatable_by` (defaults to the `:active` column). Adds `.active` / `.inactive` scopes (treats `NULL` as inactive), predicates (`active?`, `inactive?`), and mutators (`activate!`, `deactivate!`, `toggle_active!`).
|
|
8
|
+
- **Controllers::ErrorHandleable**: `rescue_from` handlers for `ActiveRecord::RecordNotFound` (404), `ActionController::ParameterMissing` (400), and `ActiveRecord::RecordInvalid` (422) that render the same JSON envelope as `Respondable#render_error`. Each handler is overridable for custom wording. Pairs naturally with `Respondable`.
|
|
9
|
+
|
|
3
10
|
## 1.6.0 (2026-05-19)
|
|
4
11
|
|
|
5
12
|
### Added
|
data/README.md
CHANGED
|
@@ -33,11 +33,14 @@ Article.published.without_deleted.find("hello-world")
|
|
|
33
33
|
- [Schedulable](#-schedulable) — `starts_at` / `ends_at` time windows
|
|
34
34
|
- [Expirable](#-expirable) — single-timestamp expiry
|
|
35
35
|
- [Normalizable](#-normalizable) — attribute normalization (`:email`, `:phone`, …)
|
|
36
|
+
- [Searchable](#-searchable) — LIKE/ILIKE search across configured columns
|
|
37
|
+
- [Activatable](#-activatable) — boolean active/inactive toggle
|
|
36
38
|
- **Controller concerns**
|
|
37
39
|
- [Paginatable](#-paginatable) — offset pagination with headers
|
|
38
40
|
- [Filterable](#-filterable) — declarative URL-param filters
|
|
39
41
|
- [Sortable (controller)](#-sortable-controller) — URL-param ordering with allow-list
|
|
40
42
|
- [Respondable](#-respondable) — standardized JSON envelopes
|
|
43
|
+
- [ErrorHandleable](#-errorhandleable) — JSON `rescue_from` handlers for common controller errors
|
|
41
44
|
- [Module paths & namespacing](#-module-paths--namespacing)
|
|
42
45
|
- [Development](#-development)
|
|
43
46
|
- [Contributing](#-contributing)
|
|
@@ -47,7 +50,7 @@ Article.published.without_deleted.find("hello-world")
|
|
|
47
50
|
|
|
48
51
|
## ✨ Why this gem?
|
|
49
52
|
|
|
50
|
-
- **
|
|
53
|
+
- **Ten model concerns + five controller concerns**, all production-ready
|
|
51
54
|
- **One include, one macro** — no boilerplate, no glue code
|
|
52
55
|
- **Lean dependencies** — only `acts_as_list` (Sortable) and `friendly_id` (Sluggable); controller concerns have zero extra deps
|
|
53
56
|
- **Schema-validated configuration** — every macro checks that the configured column exists and raises `ArgumentError` early
|
|
@@ -60,7 +63,7 @@ Article.published.without_deleted.find("hello-world")
|
|
|
60
63
|
Add to your application's `Gemfile`:
|
|
61
64
|
|
|
62
65
|
```ruby
|
|
63
|
-
gem "concerns_on_rails", "~> 1.
|
|
66
|
+
gem "concerns_on_rails", "~> 1.7"
|
|
64
67
|
```
|
|
65
68
|
|
|
66
69
|
Or pull the latest from GitHub:
|
|
@@ -429,6 +432,59 @@ User.create(phone: "+1 (415) 555-1234").phone # => "14155551234"
|
|
|
429
432
|
|
|
430
433
|
---
|
|
431
434
|
|
|
435
|
+
## 🔍 Searchable
|
|
436
|
+
|
|
437
|
+
LIKE-based search across one or more columns — no external search engine, no extra gems.
|
|
438
|
+
|
|
439
|
+
```ruby
|
|
440
|
+
class Article < ApplicationRecord
|
|
441
|
+
include ConcernsOnRails::Searchable
|
|
442
|
+
|
|
443
|
+
searchable_by :title, :body
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
Article.search("hello") # WHERE title ILIKE '%hello%' OR body ILIKE '%hello%'
|
|
447
|
+
Article.search("") # no-op — returns the full relation
|
|
448
|
+
Article.search("foo").where(state: 1) # chainable like any scope
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
**Notes**
|
|
452
|
+
- Uses Arel's `matches`, which emits `ILIKE` on Postgres (case-insensitive) and `LIKE` elsewhere.
|
|
453
|
+
- The query is escaped before interpolation — `%`, `_`, and `\` from user input are treated as literals, not wildcards.
|
|
454
|
+
- Blank or nil queries return the relation unchanged so it's safe to drop into a controller pipeline.
|
|
455
|
+
- Single-term substring match by design; reach for `pg_search` / Elasticsearch when you need ranking, stemming, or multi-term queries.
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
## ✅ Activatable
|
|
460
|
+
|
|
461
|
+
Boolean active/inactive toggle backed by a single column.
|
|
462
|
+
|
|
463
|
+
```ruby
|
|
464
|
+
class Subscription < ApplicationRecord
|
|
465
|
+
include ConcernsOnRails::Activatable
|
|
466
|
+
|
|
467
|
+
activatable_by # defaults to :active
|
|
468
|
+
# activatable_by :enabled # custom column name
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
sub = Subscription.create!(active: true)
|
|
472
|
+
sub.active? # => true
|
|
473
|
+
sub.deactivate!
|
|
474
|
+
sub.inactive? # => true
|
|
475
|
+
sub.toggle_active! # flips back to true
|
|
476
|
+
|
|
477
|
+
Subscription.active # WHERE active = TRUE
|
|
478
|
+
Subscription.inactive # WHERE active = FALSE OR active IS NULL
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
**Notes**
|
|
482
|
+
- `NULL` is treated as inactive (same convention as most apps' "unset = off").
|
|
483
|
+
- The configured column must exist; `activatable_by` raises `ArgumentError` otherwise.
|
|
484
|
+
- `SoftDeletable` also defines a `.active` scope (alias of `.without_deleted`). If both concerns are included on the same model, the later one wins — include the one whose `.active` semantics you want last, or stick to one of them.
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
432
488
|
# 🎮 Controller Concerns
|
|
433
489
|
|
|
434
490
|
Pure ActionController + ActiveRecord — **zero extra runtime dependencies** (no Kaminari, Pundit, or Ransack).
|
|
@@ -567,6 +623,51 @@ end
|
|
|
567
623
|
|
|
568
624
|
---
|
|
569
625
|
|
|
626
|
+
## 🛟 ErrorHandleable
|
|
627
|
+
|
|
628
|
+
Install `rescue_from` handlers for the three most common controller exceptions and render them as the same JSON envelope used by Respondable.
|
|
629
|
+
|
|
630
|
+
```ruby
|
|
631
|
+
class Api::BaseController < ApplicationController
|
|
632
|
+
include ConcernsOnRails::Controllers::Respondable # recommended
|
|
633
|
+
include ConcernsOnRails::Controllers::ErrorHandleable
|
|
634
|
+
end
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
**Handled exceptions**
|
|
638
|
+
|
|
639
|
+
| Exception | Status | `code` |
|
|
640
|
+
|----------------------------------------|--------|-----------------------|
|
|
641
|
+
| `ActiveRecord::RecordNotFound` | 404 | `"not_found"` |
|
|
642
|
+
| `ActionController::ParameterMissing` | 400 | `"parameter_missing"` |
|
|
643
|
+
| `ActiveRecord::RecordInvalid` | 422 | `"record_invalid"` |
|
|
644
|
+
|
|
645
|
+
Response shape (matches `Respondable#render_error`):
|
|
646
|
+
|
|
647
|
+
```json
|
|
648
|
+
{ "success": false, "error": { "message": "...", "code": "...", "details": [...] } }
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
**Overriding a handler**
|
|
652
|
+
|
|
653
|
+
Each handler is a public instance method, so subclasses can customize the message or response shape without re-declaring the `rescue_from`:
|
|
654
|
+
|
|
655
|
+
```ruby
|
|
656
|
+
class Api::BaseController < ApplicationController
|
|
657
|
+
include ConcernsOnRails::Controllers::ErrorHandleable
|
|
658
|
+
|
|
659
|
+
def handle_record_not_found(error)
|
|
660
|
+
render json: { success: false, error: { message: "Not here, friend." } }, status: :not_found
|
|
661
|
+
end
|
|
662
|
+
end
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
**Notes**
|
|
666
|
+
- When `Respondable` is also included, the handlers delegate to `render_error` so the envelope shape stays in one place. Otherwise they render the same envelope inline.
|
|
667
|
+
- `RecordInvalid.details` are populated from `error.record.errors.full_messages`.
|
|
668
|
+
|
|
669
|
+
---
|
|
670
|
+
|
|
570
671
|
## 🗂️ Module paths & namespacing
|
|
571
672
|
|
|
572
673
|
Every concern is available under two paths:
|
|
@@ -598,7 +699,7 @@ Both forms reference the same module, so you can freely mix them.
|
|
|
598
699
|
bundle install # install dev dependencies
|
|
599
700
|
bundle exec rspec # run the test suite
|
|
600
701
|
gem build concerns_on_rails.gemspec # build the gem
|
|
601
|
-
gem install ./concerns_on_rails-1.
|
|
702
|
+
gem install ./concerns_on_rails-1.7.0.gem # install locally
|
|
602
703
|
```
|
|
603
704
|
|
|
604
705
|
The test suite uses an in-memory SQLite database and a lightweight `FakeController` harness for controller-concern specs — no Rails routes or boot required.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require "active_support/concern"
|
|
2
|
+
|
|
3
|
+
module ConcernsOnRails
|
|
4
|
+
module Controllers
|
|
5
|
+
# Installs `rescue_from` handlers for the three most common controller
|
|
6
|
+
# exceptions and renders them as the JSON error envelope used by Respondable.
|
|
7
|
+
#
|
|
8
|
+
# class Api::BaseController < ApplicationController
|
|
9
|
+
# include ConcernsOnRails::Controllers::Respondable # optional, but recommended
|
|
10
|
+
# include ConcernsOnRails::Controllers::ErrorHandleable
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
# Handled:
|
|
14
|
+
# * ActiveRecord::RecordNotFound → 404 not_found
|
|
15
|
+
# * ActionController::ParameterMissing → 400 parameter_missing
|
|
16
|
+
# * ActiveRecord::RecordInvalid → 422 record_invalid (with field errors)
|
|
17
|
+
#
|
|
18
|
+
# If Respondable is also included on the controller, the handlers delegate
|
|
19
|
+
# to `render_error` so the envelope shape stays in one place. Otherwise the
|
|
20
|
+
# handlers render the same envelope inline.
|
|
21
|
+
#
|
|
22
|
+
# Each handler is a public instance method, so subclasses can override the
|
|
23
|
+
# message wording or response shape without re-declaring the `rescue_from`.
|
|
24
|
+
module ErrorHandleable
|
|
25
|
+
extend ActiveSupport::Concern
|
|
26
|
+
|
|
27
|
+
included do
|
|
28
|
+
rescue_from "ActiveRecord::RecordNotFound", with: :handle_record_not_found
|
|
29
|
+
rescue_from "ActionController::ParameterMissing", with: :handle_parameter_missing
|
|
30
|
+
rescue_from "ActiveRecord::RecordInvalid", with: :handle_record_invalid
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def handle_record_not_found(error)
|
|
34
|
+
render_error_envelope(
|
|
35
|
+
message: error.message,
|
|
36
|
+
code: "not_found",
|
|
37
|
+
status: :not_found
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def handle_parameter_missing(error)
|
|
42
|
+
render_error_envelope(
|
|
43
|
+
message: "Parameter missing: #{error.param}",
|
|
44
|
+
code: "parameter_missing",
|
|
45
|
+
status: :bad_request
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def handle_record_invalid(error)
|
|
50
|
+
record = error.respond_to?(:record) ? error.record : nil
|
|
51
|
+
details = record.respond_to?(:errors) ? record.errors.full_messages : nil
|
|
52
|
+
|
|
53
|
+
render_error_envelope(
|
|
54
|
+
message: error.message,
|
|
55
|
+
code: "record_invalid",
|
|
56
|
+
status: :unprocessable_entity,
|
|
57
|
+
errors: details
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def render_error_envelope(message:, code:, status:, errors: nil)
|
|
64
|
+
return render_error(message: message, code: code, status: status, errors: errors) if respond_to?(:render_error)
|
|
65
|
+
|
|
66
|
+
error = { message: message, code: code }
|
|
67
|
+
error[:details] = errors if errors
|
|
68
|
+
render json: { success: false, error: error }, status: status
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require "active_support/concern"
|
|
2
|
+
|
|
3
|
+
module ConcernsOnRails
|
|
4
|
+
module Models
|
|
5
|
+
# Boolean active/inactive toggle backed by a single column.
|
|
6
|
+
#
|
|
7
|
+
# class Subscription < ApplicationRecord
|
|
8
|
+
# include ConcernsOnRails::Activatable
|
|
9
|
+
#
|
|
10
|
+
# activatable_by # defaults to :active
|
|
11
|
+
# # activatable_by :enabled # custom column name
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# Subscription.active # WHERE active = TRUE
|
|
15
|
+
# Subscription.inactive # WHERE active = FALSE OR active IS NULL
|
|
16
|
+
#
|
|
17
|
+
# NULL is treated as inactive, mirroring how unset booleans behave in most apps.
|
|
18
|
+
#
|
|
19
|
+
# Note: SoftDeletable also defines a `.active` scope (alias of `.without_deleted`).
|
|
20
|
+
# If both concerns are included on the same model, the later one wins.
|
|
21
|
+
module Activatable
|
|
22
|
+
extend ActiveSupport::Concern
|
|
23
|
+
|
|
24
|
+
DEFAULT_FIELD = :active
|
|
25
|
+
|
|
26
|
+
included do
|
|
27
|
+
class_attribute :activatable_field, instance_accessor: false, default: DEFAULT_FIELD
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class_methods do
|
|
31
|
+
def activatable_by(field = DEFAULT_FIELD)
|
|
32
|
+
self.activatable_field = field.to_sym
|
|
33
|
+
|
|
34
|
+
unless column_names.include?(activatable_field.to_s)
|
|
35
|
+
raise ArgumentError,
|
|
36
|
+
"ConcernsOnRails::Models::Activatable: activatable_field '#{activatable_field}' does not exist in the database"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
scope :active, -> { where(activatable_field => true) }
|
|
40
|
+
scope :inactive, -> { where(activatable_field => [false, nil]) }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def active?
|
|
45
|
+
self[self.class.activatable_field] == true
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def inactive?
|
|
49
|
+
!active?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def activate!
|
|
53
|
+
update(self.class.activatable_field => true)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def deactivate!
|
|
57
|
+
update(self.class.activatable_field => false)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def toggle_active!
|
|
61
|
+
active? ? deactivate! : activate!
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require "active_support/concern"
|
|
2
|
+
|
|
3
|
+
module ConcernsOnRails
|
|
4
|
+
module Models
|
|
5
|
+
# LIKE-based search across one or more columns.
|
|
6
|
+
#
|
|
7
|
+
# class Article < ApplicationRecord
|
|
8
|
+
# include ConcernsOnRails::Searchable
|
|
9
|
+
#
|
|
10
|
+
# searchable_by :title, :body
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
# Article.search("hello") # WHERE title ILIKE '%hello%' OR body ILIKE '%hello%'
|
|
14
|
+
# Article.search("") # no-op — returns the full relation
|
|
15
|
+
# Article.search("foo").where(...) # chainable like any scope
|
|
16
|
+
#
|
|
17
|
+
# Uses Arel's `matches`, which emits ILIKE on Postgres and LIKE elsewhere —
|
|
18
|
+
# so case-insensitivity comes for free on PG. The query is escaped before
|
|
19
|
+
# interpolation, so `%` / `_` / `\` from user input are treated as literals.
|
|
20
|
+
module Searchable
|
|
21
|
+
extend ActiveSupport::Concern
|
|
22
|
+
|
|
23
|
+
LIKE_ESCAPE = "\\".freeze
|
|
24
|
+
LIKE_SPECIAL = /[\\%_]/
|
|
25
|
+
|
|
26
|
+
included do
|
|
27
|
+
class_attribute :searchable_fields, instance_accessor: false, default: []
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class_methods do
|
|
31
|
+
def searchable_by(*fields)
|
|
32
|
+
raise ArgumentError, "ConcernsOnRails::Models::Searchable: at least one field is required" if fields.empty?
|
|
33
|
+
|
|
34
|
+
fields.each do |field|
|
|
35
|
+
unless column_names.include?(field.to_s)
|
|
36
|
+
raise ArgumentError,
|
|
37
|
+
"ConcernsOnRails::Models::Searchable: field '#{field}' does not exist in the database"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
self.searchable_fields = fields.map(&:to_sym)
|
|
42
|
+
|
|
43
|
+
scope :search, ->(query) { search_relation(query) }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def search_relation(query)
|
|
47
|
+
return all if query.nil? || query.to_s.strip.empty?
|
|
48
|
+
|
|
49
|
+
escaped = query.to_s.gsub(LIKE_SPECIAL) { |c| "#{LIKE_ESCAPE}#{c}" }
|
|
50
|
+
pattern = "%#{escaped}%"
|
|
51
|
+
|
|
52
|
+
predicates = searchable_fields.map { |field| arel_table[field].matches(pattern, LIKE_ESCAPE) }
|
|
53
|
+
where(predicates.reduce { |memo, p| memo.or(p) })
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
data/lib/concerns_on_rails.rb
CHANGED
|
@@ -15,12 +15,15 @@ require "concerns_on_rails/models/hashable"
|
|
|
15
15
|
require "concerns_on_rails/models/schedulable"
|
|
16
16
|
require "concerns_on_rails/models/expirable"
|
|
17
17
|
require "concerns_on_rails/models/normalizable"
|
|
18
|
+
require "concerns_on_rails/models/searchable"
|
|
19
|
+
require "concerns_on_rails/models/activatable"
|
|
18
20
|
|
|
19
21
|
# Controller concerns
|
|
20
22
|
require "concerns_on_rails/controllers/paginatable"
|
|
21
23
|
require "concerns_on_rails/controllers/filterable"
|
|
22
24
|
require "concerns_on_rails/controllers/sortable"
|
|
23
25
|
require "concerns_on_rails/controllers/respondable"
|
|
26
|
+
require "concerns_on_rails/controllers/error_handleable"
|
|
24
27
|
|
|
25
28
|
# Backwards compatibility (top-level aliases for pre-1.6 module paths)
|
|
26
29
|
require "concerns_on_rails/legacy_aliases"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: concerns_on_rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ethan Nguyen
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-05-
|
|
11
|
+
date: 2026-05-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -70,16 +70,19 @@ files:
|
|
|
70
70
|
- CODE_OF_CONDUCT.md
|
|
71
71
|
- README.md
|
|
72
72
|
- lib/concerns_on_rails.rb
|
|
73
|
+
- lib/concerns_on_rails/controllers/error_handleable.rb
|
|
73
74
|
- lib/concerns_on_rails/controllers/filterable.rb
|
|
74
75
|
- lib/concerns_on_rails/controllers/paginatable.rb
|
|
75
76
|
- lib/concerns_on_rails/controllers/respondable.rb
|
|
76
77
|
- lib/concerns_on_rails/controllers/sortable.rb
|
|
77
78
|
- lib/concerns_on_rails/legacy_aliases.rb
|
|
79
|
+
- lib/concerns_on_rails/models/activatable.rb
|
|
78
80
|
- lib/concerns_on_rails/models/expirable.rb
|
|
79
81
|
- lib/concerns_on_rails/models/hashable.rb
|
|
80
82
|
- lib/concerns_on_rails/models/normalizable.rb
|
|
81
83
|
- lib/concerns_on_rails/models/publishable.rb
|
|
82
84
|
- lib/concerns_on_rails/models/schedulable.rb
|
|
85
|
+
- lib/concerns_on_rails/models/searchable.rb
|
|
83
86
|
- lib/concerns_on_rails/models/sluggable.rb
|
|
84
87
|
- lib/concerns_on_rails/models/soft_deletable.rb
|
|
85
88
|
- lib/concerns_on_rails/models/sortable.rb
|