- Keep
README.md
always updated - Grouping and order on top of
ActiveRecord
models:
class ModelName < ApplicationRecord
# includes
include ModuleName
# enums
enum type: [:a, :b, :c, :d]
# relationships
belongs_to :parent
has_many :children
# attributes
attr_accessor :fieldname
# validations
validates :name, presence: true
# scopes
scope :deleted, -> { where(deleted: true) }
# callbacks
before_save :do_something
end
- Custom code or PORO1 (validators, services, etc):
- Add application specific code under
app/
- Add code that can be extracted for use in other projects to
lib/
- Ask the question: Can I use this code in my Tinder clone? If yes, it goes in
lib
- Ask the question: Can I use this code in my Tinder clone? If yes, it goes in
- Add application specific code under
- Ensure db:migrate runs properly
- Ensure db:rollback runs properly
- Ensure db:seed runs properly (They get outdated fast!). Alternatively, provide a way to seed a newcomer's project.
- Ensure painless turnovers
- A service object is a PORO that models business logic so that it can be reused across multiple callers (for example from controllers, rake tasks, background jobs and even other services!).
- Name these classes as
NounVerber
CreditCardCharger
- Expose a constructor that sets up initialization configuration
CreditCardCharger.new(processor: :braintree)
- Expose an instance method named
call
and send data parameters to thisCreditCardCharger.new(processor: braintree).call('4111111111111111','123','11/28')
- Call returns a
Result
object which can encapsulate the success, errors and instance
result = CreditCardCharger.new(processor: :Braintree).call(card_details) if result.success? Rails.log("Successful trans: #{result.transaction}") else Rails.error("Error in transaction: #{result.errors}") end
- Prefer to use immutable-struct for this.
- Keep in
app/services
folder (see custom code above) - When initializing services, pass in instances
- Keep jobs in
app/jobs
- Keep job
perform
parameters to a minimum. Ideally, pass in[model].id
as parameter and query within the job. (This also allows the job to fail if record is not found)
- Put code into logical containers. Don't be afraid to add folders to your
app/
folder as this is autoloaded by default.app/decorators
app/utilities
app/composers
- Check out Design Patterns in Ruby
1 PORO = Plain Old Ruby Object.
The distinction between a PORO (which stands for Plain Old Ruby Object) and a not-PORO is not a technical one. All objects are technically Ruby object. Instead, the term PORO is sometimes used when talking about large frameworks like Rails which tend to use "big", "complex" objects like ActiveRecord models for much of its logic. These large objects tend to contain a large amount of logic and defined behavior.