concerns_on_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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +9 -0
- data/CODE_OF_CONDUCT.md +52 -0
- data/README.md +160 -0
- data/lib/concerns_on_rails/publishable.rb +61 -0
- data/lib/concerns_on_rails/sluggable.rb +63 -0
- data/lib/concerns_on_rails/sortable.rb +80 -0
- data/lib/concerns_on_rails/version.rb +3 -0
- data/lib/concerns_on_rails.rb +8 -0
- metadata +97 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '0679c4f78651169ae85fbf3f27d0a301d6ca33c148eb3e01c766252145c5be4c'
|
4
|
+
data.tar.gz: cec6963e3ba583e9072b03de751adbd1362dc100b041d5addf8cb37ebacd1d41
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c2ffaa1b86bd6c7952857c88e326009a85699bb77364e0b42e6cd8bfa11614917c3cf2fbae94af08bf2262210968878b65c37b22856bb8bac95345d81533fa57
|
7
|
+
data.tar.gz: e387756b366d70efff292dd40b1a8cf34dfeb04321140c2e4e03833c754975afb1686df499fd6ddefc4e83e7c7347851e62c1bc2a8f7d1f9e51d0c7f3cae50ca
|
data/CHANGELOG.md
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Code of Conduct
|
2
|
+
|
3
|
+
We are committed to providing a friendly, safe, and welcoming environment for all, regardless of gender, sexual orientation, disability, physical appearance, body size, race, ethnicity, age, religion, or technology choices.
|
4
|
+
|
5
|
+
### Our Standards
|
6
|
+
|
7
|
+
Examples of behavior that contributes to creating a positive environment include:
|
8
|
+
|
9
|
+
- Being kind and respectful to others.
|
10
|
+
- Behaving in a welcoming and inclusive manner.
|
11
|
+
- Showing empathy towards other community members.
|
12
|
+
- Communicating in a respectful and constructive way.
|
13
|
+
- Acknowledging and respecting the different viewpoints of others.
|
14
|
+
|
15
|
+
Examples of unacceptable behavior by participants include:
|
16
|
+
|
17
|
+
- The use of sexualized language or imagery, and unwelcome sexual attention or advances.
|
18
|
+
- Trolling, insulting/derogatory comments, and personal or political attacks.
|
19
|
+
- Public or private harassment.
|
20
|
+
- Any form of discrimination or marginalization.
|
21
|
+
- Sharing private information without permission, including personal addresses or contact details.
|
22
|
+
- Intimidation, bullying, or deliberate exclusion of others.
|
23
|
+
- Disruptive behavior in discussions, issues, or pull requests.
|
24
|
+
|
25
|
+
### Reporting
|
26
|
+
|
27
|
+
If you are being harassed, notice that someone else is being harassed, or have any other concerns, please contact us immediately at [doctorit@gmail.com](mailto:doctorit@gmail.com).
|
28
|
+
|
29
|
+
You can also use the [GitHub issue tracker](https://github.com/VSN2015/concerns_on_rails/issues) to report any incidents or violations of the code of conduct.
|
30
|
+
|
31
|
+
We take all reports seriously and will respond promptly.
|
32
|
+
|
33
|
+
### Enforcement
|
34
|
+
|
35
|
+
Participants who violate this Code of Conduct may be warned or expelled from the community at the discretion of the project maintainers.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the project maintainers at the contact information above. All complaints will be reviewed and investigated promptly and in a fair manner.
|
38
|
+
|
39
|
+
If necessary, the project maintainers will take appropriate action, which may include a temporary or permanent ban from the community.
|
40
|
+
|
41
|
+
### Our Commitment
|
42
|
+
|
43
|
+
By participating in this project, you are agreeing to adhere to this Code of Conduct. We expect all members of this community to be respectful and considerate to others at all times.
|
44
|
+
|
45
|
+
### Scope
|
46
|
+
|
47
|
+
This Code of Conduct applies to all spaces related to this project, including the project's GitHub repository, chat rooms, mailing lists, and other communication channels.
|
48
|
+
|
49
|
+
---
|
50
|
+
|
51
|
+
**Adapted from the Contributor Covenant**, version 1.4, available at https://www.contributor-covenant.org
|
52
|
+
|
data/README.md
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
# ConcernsOnRails
|
2
|
+
|
3
|
+
**Note: Hoàng Sa and Trường Sa belong to Việt Nam.** 🇻🇳
|
4
|
+
|
5
|
+
A simple collection of reusable Rails concerns to keep your models clean and DRY.
|
6
|
+
|
7
|
+
## Features
|
8
|
+
|
9
|
+
- ✅ `Sluggable`: Generate friendly slugs from a specified field
|
10
|
+
- ✅ `Sortable`: Sort records based on a field using `acts_as_list`, with flexible sorting field and direction
|
11
|
+
- ✅ `Publishable`: Easily manage published/unpublished records using a simple `published_at` field
|
12
|
+
|
13
|
+
---
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'concerns_on_rails', github: 'VSN2015/concerns_on_rails'
|
21
|
+
```
|
22
|
+
|
23
|
+
Then execute:
|
24
|
+
|
25
|
+
```sh
|
26
|
+
bundle install
|
27
|
+
```
|
28
|
+
|
29
|
+
---
|
30
|
+
|
31
|
+
## Usage
|
32
|
+
|
33
|
+
### 1. Sluggable
|
34
|
+
|
35
|
+
Add slugs based on a specified attribute.
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
class Post < ApplicationRecord
|
39
|
+
include ConcernsOnRails::Sluggable
|
40
|
+
|
41
|
+
sluggable_by :title
|
42
|
+
end
|
43
|
+
|
44
|
+
post = Post.create!(title: "Hello World")
|
45
|
+
post.slug # => "hello-world"
|
46
|
+
```
|
47
|
+
|
48
|
+
If the slug source is changed, the slug will auto-update.
|
49
|
+
|
50
|
+
---
|
51
|
+
|
52
|
+
### 2. Sortable
|
53
|
+
|
54
|
+
Use for models that need ordering.
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
class Task < ApplicationRecord
|
58
|
+
include ConcernsOnRails::Sortable
|
59
|
+
|
60
|
+
sortable_by :position
|
61
|
+
end
|
62
|
+
|
63
|
+
Task.create!(name: "B")
|
64
|
+
Task.create!(name: "A")
|
65
|
+
Task.first.name # => "B" (sorted by position ASC)
|
66
|
+
```
|
67
|
+
|
68
|
+
You can customize the sort field and direction:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
class PriorityTask < ApplicationRecord
|
72
|
+
include ConcernsOnRails::Sortable
|
73
|
+
|
74
|
+
sortable_by priority: :desc
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
78
|
+
Additional features:
|
79
|
+
- Automatically sets `acts_as_list` on the configured column
|
80
|
+
- Adds default sorting scope to your model
|
81
|
+
- Supports custom direction: `:asc` or `:desc`
|
82
|
+
- Validates that the sortable field exists in the table schema
|
83
|
+
- Compatible with scopes and ActiveRecord queries
|
84
|
+
- Can be reconfigured dynamically within the model using `sortable_by`
|
85
|
+
|
86
|
+
---
|
87
|
+
|
88
|
+
### 3. Publishable
|
89
|
+
|
90
|
+
Manage published/unpublished records using a `published_at` field.
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
class Article < ApplicationRecord
|
94
|
+
include ConcernsOnRails::Publishable
|
95
|
+
end
|
96
|
+
|
97
|
+
Article.published # => returns only published articles
|
98
|
+
Article.unpublished # => returns only unpublished articles
|
99
|
+
|
100
|
+
article = Article.create!(title: "Draft")
|
101
|
+
article.published? # => false
|
102
|
+
|
103
|
+
article.publish!
|
104
|
+
article.published? # => true
|
105
|
+
|
106
|
+
article.unpublish!
|
107
|
+
article.published? # => false
|
108
|
+
```
|
109
|
+
|
110
|
+
Additional features:
|
111
|
+
- `published?` returns true if `published_at` is present and in the past
|
112
|
+
- `publish!` sets `published_at` to current time
|
113
|
+
- `unpublish!` sets `published_at` to `nil`
|
114
|
+
- Add scopes: `.published`, `.unpublished`, and a default scope (optional)
|
115
|
+
- Ideal for blog posts, articles, or any content that toggles visibility
|
116
|
+
- Lightweight and non-invasive
|
117
|
+
- Easy to test and override in custom implementations
|
118
|
+
|
119
|
+
---
|
120
|
+
|
121
|
+
## Development
|
122
|
+
|
123
|
+
To build the gem:
|
124
|
+
|
125
|
+
```sh
|
126
|
+
gem build concerns_on_rails.gemspec
|
127
|
+
```
|
128
|
+
|
129
|
+
To install locally:
|
130
|
+
|
131
|
+
```sh
|
132
|
+
gem install ./concerns_on_rails-1.0.0.gem
|
133
|
+
```
|
134
|
+
|
135
|
+
---
|
136
|
+
|
137
|
+
## Contributing
|
138
|
+
|
139
|
+
Bug reports and pull requests are welcome!
|
140
|
+
|
141
|
+
---
|
142
|
+
|
143
|
+
## License
|
144
|
+
|
145
|
+
This project is licensed under the MIT License.
|
146
|
+
|
147
|
+
---
|
148
|
+
|
149
|
+
🇻🇳 **Hoàng Sa and Trường Sa belong to Việt Nam.**
|
150
|
+
|
151
|
+
---
|
152
|
+
|
153
|
+
### 📦 Source Code
|
154
|
+
|
155
|
+
The source code is available on GitHub:
|
156
|
+
|
157
|
+
🔗 [https://github.com/VSN2015/concerns_on_rails](https://github.com/VSN2015/concerns_on_rails)
|
158
|
+
|
159
|
+
Feel free to star ⭐️, fork 🍴, or contribute with issues and PRs.
|
160
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
|
3
|
+
module ConcernsOnRails
|
4
|
+
module Publishable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
# instance methods
|
8
|
+
included do
|
9
|
+
# declare class attributes and set default values
|
10
|
+
class_attribute :publishable_field
|
11
|
+
self.publishable_field ||= :published_at
|
12
|
+
end
|
13
|
+
|
14
|
+
# class methods
|
15
|
+
class_methods do
|
16
|
+
# Define publishable field
|
17
|
+
# Example:
|
18
|
+
# publishable_by :published_at
|
19
|
+
def publishable_by(field = nil)
|
20
|
+
self.publishable_field = field || :published_at
|
21
|
+
|
22
|
+
# validate publishable_field exists in database
|
23
|
+
unless column_names.include?(publishable_field.to_s)
|
24
|
+
raise ArgumentError, "ConcernsOnRails::Publishable: publishable_field '#{publishable_field}' does not exist in the database"
|
25
|
+
end
|
26
|
+
|
27
|
+
scope :published, -> { where(arel_table[publishable_field].not_eq(nil)) }
|
28
|
+
scope :unpublished, -> { where(arel_table[publishable_field].eq(nil)) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Instance methods
|
33
|
+
# Publish the record
|
34
|
+
# Example:
|
35
|
+
# record.publish!
|
36
|
+
def publish!
|
37
|
+
update(self.class.publishable_field => Time.zone.now)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Unpublish the record
|
41
|
+
# Example:
|
42
|
+
# record.unpublish!
|
43
|
+
def unpublish!
|
44
|
+
update(self.class.publishable_field => nil)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Check if the record is published
|
48
|
+
# Example:
|
49
|
+
# record.published?
|
50
|
+
def published?
|
51
|
+
self[self.class.publishable_field].present?
|
52
|
+
end
|
53
|
+
|
54
|
+
# Check if the record is unpublished
|
55
|
+
# Example:
|
56
|
+
# record.unpublished?
|
57
|
+
def unpublished?
|
58
|
+
!published?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
require "friendly_id"
|
3
|
+
|
4
|
+
module ConcernsOnRails
|
5
|
+
module Sluggable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
# instance methods
|
9
|
+
included do
|
10
|
+
# declare class attributes and set default values
|
11
|
+
class_attribute :sluggable_field
|
12
|
+
self.sluggable_field ||= :name
|
13
|
+
|
14
|
+
extend FriendlyId
|
15
|
+
# we need use a lambda to access the instance variable
|
16
|
+
# instead of friendly_id :slug_source, use: :slugged
|
17
|
+
friendly_id :slug_source, use: :slugged
|
18
|
+
# friendly_id ->(record) { record.slug_source }, use: :slugged
|
19
|
+
|
20
|
+
# we must override should_generate_new_friendly_id? to support update slug
|
21
|
+
# if we don't override this method, friendly_id will not generate the new slug when update
|
22
|
+
define_method :should_generate_new_friendly_id? do
|
23
|
+
field = self.class.sluggable_field
|
24
|
+
respond_to?("will_save_change_to_#{field}?") && send("will_save_change_to_#{field}?")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# class methods
|
29
|
+
class_methods do
|
30
|
+
# Define sluggable field
|
31
|
+
# Example:
|
32
|
+
# sluggable_by :wonderful_name
|
33
|
+
def sluggable_by(field)
|
34
|
+
self.sluggable_field = field.to_sym
|
35
|
+
|
36
|
+
validate_sluggable_field!
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
# Validate sluggable_field exists in database
|
41
|
+
def validate_sluggable_field!
|
42
|
+
unless column_names.include?(sluggable_field.to_s)
|
43
|
+
raise ArgumentError, "ConcernsOnRails::Sluggable: sluggable_field '#{sluggable_field}' does not exist in the database"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Instance methods
|
49
|
+
# Returns the source for the slug
|
50
|
+
# we are calling the class attribute, so we can use it in the lambda
|
51
|
+
# Example:
|
52
|
+
# record.slug_source
|
53
|
+
def slug_source
|
54
|
+
if self.class.sluggable_field.present? && respond_to?(self.class.sluggable_field)
|
55
|
+
send(self.class.sluggable_field)
|
56
|
+
elsif respond_to?(:title)
|
57
|
+
title
|
58
|
+
else
|
59
|
+
to_s
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
require "acts_as_list"
|
3
|
+
|
4
|
+
module ConcernsOnRails
|
5
|
+
module Sortable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
# instance methods
|
9
|
+
# include Sortable in model to enable sorting
|
10
|
+
# Example:
|
11
|
+
# class Task < ApplicationRecord
|
12
|
+
# include Sortable
|
13
|
+
# sortable_by :priority
|
14
|
+
# end
|
15
|
+
included do
|
16
|
+
# declare class attributes
|
17
|
+
class_attribute :sortable_field
|
18
|
+
class_attribute :sortable_direction
|
19
|
+
|
20
|
+
# set default values
|
21
|
+
self.sortable_field ||= :position
|
22
|
+
self.sortable_direction ||= :asc
|
23
|
+
|
24
|
+
# we cannot use acts_as_list here
|
25
|
+
default_scope { order(sortable_field => sortable_direction) }
|
26
|
+
end
|
27
|
+
|
28
|
+
# class methods
|
29
|
+
# Example: Task.sortable_by(priority: :asc)
|
30
|
+
class_methods do
|
31
|
+
# Define sortable field and direction
|
32
|
+
# Example:
|
33
|
+
# sortable_by :position
|
34
|
+
# sortable_by position: :asc
|
35
|
+
# sortable_by position: :desc
|
36
|
+
#
|
37
|
+
# sortable_by :position, use_acts_as_list: false
|
38
|
+
def sortable_by(field_config, use_acts_as_list: true)
|
39
|
+
# parse field_config
|
40
|
+
field, direction = parse_sortable_config(field_config)
|
41
|
+
|
42
|
+
# validate direction and must be :asc or :desc
|
43
|
+
direction = :asc unless %i[asc desc].include?(direction)
|
44
|
+
|
45
|
+
# set class attributes
|
46
|
+
self.sortable_field = field
|
47
|
+
self.sortable_direction = direction
|
48
|
+
|
49
|
+
validate_sortable_field!
|
50
|
+
|
51
|
+
# add acts_as_list and default scope
|
52
|
+
# Setup sorting behaviors
|
53
|
+
acts_as_list column: sortable_field if use_acts_as_list
|
54
|
+
|
55
|
+
# add default scope: position => asc
|
56
|
+
default_scope { order(sortable_field => sortable_direction) }
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
def parse_sortable_config(config)
|
61
|
+
if config.is_a?(Hash)
|
62
|
+
# extract key and value
|
63
|
+
# when we call .first, we get the first key-value pair
|
64
|
+
# Example: { position: :asc }.first => ["position", :asc]
|
65
|
+
key, value = config.first
|
66
|
+
[key.to_sym, value.to_sym]
|
67
|
+
else
|
68
|
+
[config.to_sym, :asc]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Validate sortable_field exists in database
|
73
|
+
def validate_sortable_field!
|
74
|
+
unless column_names.include?(sortable_field.to_s)
|
75
|
+
raise ArgumentError, "ConcernsOnRails::Sortable: sortable_field '#{sortable_field}' does not exist in the database"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: concerns_on_rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ethan Nguyen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-04-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: acts_as_list
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.7.5
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.7.5
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: friendly_id
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.4'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.4'
|
55
|
+
description: A collection of plug-and-play ActiveSupport concerns for Rails models
|
56
|
+
and Rails controllers
|
57
|
+
email:
|
58
|
+
- doctorit@gmail.com
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- CHANGELOG.md
|
64
|
+
- CODE_OF_CONDUCT.md
|
65
|
+
- README.md
|
66
|
+
- lib/concerns_on_rails.rb
|
67
|
+
- lib/concerns_on_rails/publishable.rb
|
68
|
+
- lib/concerns_on_rails/sluggable.rb
|
69
|
+
- lib/concerns_on_rails/sortable.rb
|
70
|
+
- lib/concerns_on_rails/version.rb
|
71
|
+
homepage: https://github.com/VSN2015/concerns_on_rails
|
72
|
+
licenses:
|
73
|
+
- MIT
|
74
|
+
metadata:
|
75
|
+
homepage_uri: https://github.com/VSN2015/concerns_on_rails
|
76
|
+
source_code_uri: https://github.com/VSN2015/concerns_on_rails
|
77
|
+
changelog_uri: https://github.com/VSN2015/concerns_on_rails/CHANGELOG.md
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: 2.7.0
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
requirements: []
|
93
|
+
rubygems_version: 3.1.6
|
94
|
+
signing_key:
|
95
|
+
specification_version: 4
|
96
|
+
summary: Reusable Rails concerns like Sortable, Publishable, and Sluggable
|
97
|
+
test_files: []
|