Skip to content

Instantly share code, notes, and snippets.

@romansklenar
Last active July 11, 2023 23:12
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save romansklenar/4995594 to your computer and use it in GitHub Desktop.
Save romansklenar/4995594 to your computer and use it in GitHub Desktop.
Rails concerns

What is it?

Collection of concerns for your Rails application

Installation

Copy to your app/models/concerns directory

Licensing

Use and modificate at your will. Fork this gist to participate.

Contacts

@romansklenar

class CreateTagsAndTaggings < ActiveRecord::Migration
def change
create_table :tags do |t|
t.string :name
t.timestamps
end
create_table :taggings do |t|
t.references :tag, index: true
t.references :taggable, polymorphic: true, index: true
t.references :tagger, polymorphic: true, index: true
t.datetime :created_at
end
end
end
module Paranoid
extend ActiveSupport::Concern
included do
extend ClassMethods
include InstanceMethods
scope :only_deleted, -> { unscoped.where.not(deleted_at: nil) }
scope :with_deleted, -> { unscoped }
scope :without_deleted, -> { where(deleted_at: nil) }
default_scope { without_deleted }
end
module ClassMethods
def restore(id)
if id.is_a?(Array)
id.map { |one_id| restore(one_id) }
else
only_deleted.find(id).restore!
end
end
end
module InstanceMethods
def destroy!
run_callbacks(:destroy) { delete! }
end
alias :destroy :destroy!
def delete!
return if new_record? || destroyed?
self.class.unscoped { update_column :deleted_at, Time.now }
end
alias :delete :delete!
def restore!
self.class.unscoped { update_column :deleted_at, nil }
end
alias :restore :restore!
def destroyed?
!self.deleted_at.nil?
end
alias :deleted? :destroyed?
def paranoid?
true
end
# If a paranoid record is selected, then we only want to check
# if it's a new record, not if it is "destroyed".
def persisted?
paranoid? ? !new_record? : super
end
end
end
module Steppable
extend ActiveSupport::Concern
included do
include InstanceMethods
attr_writer :steps, :current_step
end
module ClassMethods
end
module InstanceMethods
def steps
@step ||= []
end
def current_step
@current_step ||= steps.first
end
def next_step
steps[steps.index(current_step)+1] unless last_step? && steps.any?
end
def previous_step
steps[steps.index(current_step)-1] unless first_step? && steps.any?
end
def first_step
steps.first
end
def last_step
steps.last
end
def skip_step!
@current_step = next_step
end
def current_step?(step)
current_step == step
end
def next_step?(step)
steps[steps.index(step)] == next_step if steps.include?(step)
end
def previous_step?(step)
steps[steps.index(step)] == previous_step if steps.include?(step)
end
def first_step?(step = nil)
(step || current_step) == first_step
end
def last_step?(step = nil)
(step || current_step) == last_step
end
def progress
i = steps.index(current_step)
(i+1).to_f / steps.size * 100 if steps.any?
end
end
end
module Taggable
extend ActiveSupport::Concern
included do
include InstanceMethods
has_many :taggings, as: :taggable, dependent: :destroy
has_many :tags, through: :taggings
end
module InstanceMethods
def tag(name)
name.strip!
tag = Tag.where(name: name).first_or_create!
self.taggings.where(tag: tag).first_or_create!
end
def tag_names
@tag_names ||= tags.collect(&:name).flatten.uniq.join(Tag::DELIMITER)
end
def tag_names=(names)
self.taggings.destroy_all
names.strip.split(Tag::DELIMITER).flatten.uniq.each { |name| self.tag(name) }
end
end
end
module Tagger
extend ActiveSupport::Concern
included do
include InstanceMethods
has_many :owned_taggings, as: :tagger, dependent: :destroy, class_name: "Tagging"
has_many :owned_tags, through: :owned_taggings, source: :tag, class_name: "Tag"
end
module InstanceMethods
def tag(taggable, with)
tag_names = with.flatten.uniq.map(&:strip)
tag_names.each do |name|
tag = Tag.where(name: name).first_or_create!
self.owned_taggings.where(tag: tag, taggable: taggable, tagger: self).first_or_create!
end
end
def owned_tag_names
owned_tags.pluck(:name).flatten.uniq.join(Tag::DELIMITER)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment