Skip to content

Instantly share code, notes, and snippets.

@dyanagi
Last active October 5, 2019 23:29
Show Gist options
  • Save dyanagi/353cc99fb67128cb3aa45cb6dc96268c to your computer and use it in GitHub Desktop.
Save dyanagi/353cc99fb67128cb3aa45cb6dc96268c to your computer and use it in GitHub Desktop.
Ideas for organizing macro style methods on Rails
# frozen_string_literal: true
# Macro Style Methods style guide (DRAFT)
#
# Let me know if you have a good idea! This is still a draft.
#
# Reference: https://github.com/rubocop-hq/rails-style-guide#macro-style-methods
class User < ActiveRecord::Base
include Sortable
extend SomethingSpecial
# keep the default scope first (if any)
default_scope { where(active: true) }
# constants come up next
COLORS = %w(red green blue)
# afterwards we put attr related macros
attribute :follower_count, :integer, default: nil
attribute :followee_count, :integer, default: nil
attr_accessor :formatted_date_of_birth
# delegate
delegate :name, :bio, :preferences, to: :profile
# has_secure_password (renaming the field supported in Rails 6)
has_secure_password
has_secure_password :transaction_password
# has_secure_token
has_secure_token
has_secure_token :access_token
# Rails 4+ enums after attr macros
enum gender: { female: 0, male: 1, other: 2 }
# followed by association macros
# belongs_to
belongs_to :country
belongs_to :company
# has_one
has_one :profile, dependent: :destroy, class_name: 'UserProfile', autosave: true
# has_many
has_many :authentications, dependent: :destroy
has_many :articles, dependent: :destroy, inverse_of: :author, foreign_key: :author_id
has_many :follows, dependent: :destroy
has_many :followers, class_name: 'SampleUser', through: :follows
has_many :followed_users, foreign_key: :follower_id, class_name: 'Follow'
has_many :followees, class_name: 'SampleUser', through: :followed_users
# then accepts_nested_attributes_for (this has to define after "belongs_to")
accepts_nested_attributes_for :profile
# and validation macros
validates :email, presence: true
validate :email_has_been_invited
validates :username, presence: true,
uniqueness: { case_sensitive: false },
format: { with: /\A[A-Za-z][A-Za-z0-9._-]{2,19}\z/ }
validates :username, format: { without: /--/ } # Additional format validator - do not contain two dashes
validate :username_not_in_reserved_word_list
validates :password, format: { with: /\A\S{8,128}\z/, allow_nil: true }
validate :password_not_easy_to_guess
# validation associated
validates_associated :profile
# next we have callbacks
before_validation :build_default_profile
after_validation :do_something
before_save :update_username_lower
after_save :do_something
before_create :do_something
after_create :do_something
before_update :do_something
after_update :do_something
before_destroy :do_something
after_destroy :do_something
# other macros (like devise's) should be placed after the callbacks
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:confirmable, :lockable, :timeoutable, :trackable
# Scope definitions
scope :newest_first, -> { order(created_at: :desc) }
# Scope definitions (class methods) for unchainable/complex scopes
def self.with_follower_count
left_outer_joins(:followers)
.group('followers.id')
.select('followers.*', 'count(followers.id) as follower_count')
end
# other methods
def self.other_class_methods
do_something
end
def profile
super || build_profile
end
private
def build_default_profile
profile
end
def update_username_lower
do_something
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment