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 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
@@ -0,0 +1,9 @@
1
+ <!-- CHANGELOG.md -->
2
+
3
+ ## 1.0.0 (2025-04-12)
4
+
5
+ ### Added
6
+ - Initial release
7
+
8
+ ### Fixed
9
+ - None
@@ -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
@@ -0,0 +1,3 @@
1
+ module ConcernsOnRails
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,8 @@
1
+ require "active_support/concern"
2
+ require "concerns_on_rails/version"
3
+ require "concerns_on_rails/sortable"
4
+ require "concerns_on_rails/publishable"
5
+ require "concerns_on_rails/sluggable"
6
+
7
+ module ConcernsOnRails
8
+ 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: []