The definitive guide to tackle technical deb in the Rails project

{{domain}}/organizations/{{organization_id}}/projects/{{project_id}}/reports/{{report_id}}/notes
{{domain}}/v1/organizations/{{organization_id}}/reports?include=project&filter[project_name_in]=Annual-Report-2017,Annual-Report-2018&fields[project]=name

The host app

rails _6.0.1_ new annotable_app --api \
--database=postgresql \
--skip-action-mailer \
--skip-action-mailbox \
--skip-action-text \
--skip-puma \
--skip-action-cable \
--skip-sprockets \
--skip-javascript \
--skip-turbolinks \
--skip-test \
--skip-webpack-install
# Gemfile# gem 'rails', '~> 6.0.1'gem 'actionpack', '~> 6.0.1'
gem 'activemodel', '~> 6.0.1'
gem 'activerecord', '~> 6.0.1'
gem 'activesupport', '~> 6.0.1'
gem 'railties', '~> 6.0.1'
bin/rails middleware
# config/application.rbconfig.middleware.delete ActionDispatch::Cookies
config.middleware.delete Rack::Sendfile
config.middleware.delete ActionDispatch::Static
config.middleware.delete ActiveSupport::Cache::Strategy::LocalCache::Middleware
config.middleware.delete ActionDispatch::ActionableExceptions
config.middleware.delete ActionDispatch::Callbacks
# config/initializers/autoloader.rb
Rails.autoloaders.logger = Rails.logger
bin/rails generate generator — help
bin/rails generate scaffold organization name:string
bin/rails g scaffold user name:string email:string organization:references
bin/rails g scaffold project name:string organization:references
bin/rails g scaffold report name:string content:text project:references
bin/rails g scaffold note title:string content:text report:references

The Rails Engine

rails _6.0.1_ plugin new annotable --mountable --api \
--database=postgresql \
--skip-action-mailer \
--skip-action-mailbox \
--skip-action-text \
--skip-active-storage \
--skip-puma \
--skip-action-cable \
--skip-sprockets \
--skip-javascript \
--skip-turbolinks \
--skip-test \
--dummy-path=spec/dummy
# annotable.gemspec# spec.add_dependency "rails", "~> 6.0.1"# Load only what you need
spec.add_dependency "actionpack", "~> 6.0.1"
spec.add_dependency "activemodel", "~> 6.0.1"
spec.add_dependency "activerecord", "~> 6.0.1"
spec.add_dependency "activesupport", "~> 6.0.1"
spec.add_dependency "activejob", "~> 6.0.1"
spec.add_dependency "railties", "~> 6.0.1"
# lib/annotable/engine.rbconfig.generators do |g|
g.test_framework :rspec, fixture: true
g.fixture_replacement :fabrication
g.api_only = true
g.orm :active_record, primary_key_type: :uuid
g.templates << File.expand_path('../templates', __dir__)
end
{{domain}}/annotable/organizations/{{organization_id}}/reports?filter[name_cont]=Annual-Report

Mount the Rails Engine

# Gemfile
gem ‘annotable’, path: '../annotable' # local development
# config/routes.rbRails.application.routes.draw do
resources :users
resources :organizations do
resources :projects do
resources :reports do
resources :notes
end
end
end
mount Annotable::Engine, at: 'v1'
end
bin/rails railties:install:migrations
curl -X GET \
'http://localhost:3000/v1/users
{
"links": {
"self": "http://localhost:3000/v1/users?include=organization",
"current": "http://localhost:3000/v1/users?include=organization&page[number]=1"
},
"data": []
}
# app/models/organization.rbAnnotable::Organization.class_eval do
has_many :projects, class_name: 'Project', primary_key: :legacy_id
end
Organization = Annotable::Organization
add_column :annotable_organizations, :legacy_id, :bigint
organizations_id_seq_value = select_value("SELECT NEXTVAL('organizations_id_seq')")
sql = <<-SQL.squish
CREATE SEQUENCE public.annotable_organizations_legacy_id_seq
START WITH #{organizations_id_seq_value}
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1
SQL
execute(sql)
sql = <<-SQL.squish
ALTER TABLE annotable_organizations ADD CONSTRAINT annotable_organizations_legacy_id_uniq UNIQUE (legacy_id)
SQL
execute(sql)
remove_foreign_key :projects, :organizations
class Project < ApplicationRecord
belongs_to(
:organization,
class_name: 'Annotable::Organization',
primary_key: :legacy_id,
required: true
)
end
# config/initializers/annotable.rbrequire 'annotable'require Annotable::Engine.root
.join('app/controllers/annotable/organizations_controller.rb')
module OrganizationControllerCustomFields
module ClassMethods
def allowed_includes
(super.dup + [:projects]).freeze
end
def allowed_filterables
(super.dup + ['projects_name']).freeze
end
end
def self.prepended(base)
class << base
prepend(ClassMethods)
end
end
end
Annotable::OrganizationsController
.prepend(OrganizationControllerCustomFields)
curl -X GET \
'http://localhost:3000/v1/organizations?include=users,projects&filter[name_eq]=Big Corp&fields[organization]=name&fields[user]=email&fields[project]=name
NoMethodError (undefined method `projects' for #<Annotable::Organization:0x00007fe78008b078>)
# config/initializers/zeitwerk.rbrequire 'annotable'require Annotable::Engine.root
.join('app/models/annotable/organization.rb')
require Annotable::Engine.root
.join('app/serializers/annotable/organization_serializer.rb')
require Annotable::Engine.root
.join('app/controllers/annotable/organizations_controller.rb')
Rails.autoloaders.main.ignore(
Annotable::Engine.root.join('app/models/annotable/organization.rb')
)
Rails.autoloaders.main.ignore(
Annotable::Engine.root
.join('app/serializers/annotable/organization_serializer.rb')
)
Rails.autoloaders.main.ignore(
Annotable::Engine.root
.join('app/controllers/annotable/organizations_controller.rb')
)
require_dependency
Rails.root.join('app/models/organization.rb')
require_dependency
Rails.root.join('config/initializers/annotable.rb')

Summary

Conclusion

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store