{"id":13878705,"url":"https://github.com/tranquangvu/go-rails-template","last_synced_at":"2025-07-16T14:32:49.784Z","repository":{"id":40220710,"uuid":"198870028","full_name":"tranquangvu/go-rails-template","owner":"tranquangvu","description":"A template to build large scale web applications in Ruby On Rails. Focus on extending, performance and best practices","archived":true,"fork":false,"pushed_at":"2023-01-19T13:56:44.000Z","size":432,"stargazers_count":10,"open_issues_count":16,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-08-07T08:11:36.838Z","etag":null,"topics":["calculation-objects","decorators","form-objects","golden-owl","patterns","policy-objects","query-objects","rails","rails-templates","ruby","ruby-on-rails","service-objects","value-objects"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tranquangvu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-07-25T16:55:45.000Z","updated_at":"2024-03-18T21:50:51.000Z","dependencies_parsed_at":"2023-02-11T03:00:41.993Z","dependency_job_id":null,"html_url":"https://github.com/tranquangvu/go-rails-template","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tranquangvu%2Fgo-rails-template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tranquangvu%2Fgo-rails-template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tranquangvu%2Fgo-rails-template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tranquangvu%2Fgo-rails-template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tranquangvu","download_url":"https://codeload.github.com/tranquangvu/go-rails-template/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226138849,"owners_count":17579496,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["calculation-objects","decorators","form-objects","golden-owl","patterns","policy-objects","query-objects","rails","rails-templates","ruby","ruby-on-rails","service-objects","value-objects"],"created_at":"2024-08-06T08:01:57.257Z","updated_at":"2024-11-24T07:31:15.458Z","avatar_url":"https://github.com/tranquangvu.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# GO Rails Template\n\nA template to build large scale web applications in Ruby On Rails. Focus on extending, performance and best practices by applying patterns: Service Objects, Form Objects, Query Objects, Calculation Objects, Value Objects, Policy Objects, Decorators, etc.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/GoldenOwlAsia/go_rails_template/master/app/assets/images/welcome.png\" alt=\"react boilerplate banner\" width=\"600\" /\u003e\n\u003cp\u003e\n\n\u003cbr /\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003csub\u003eCreated by \u003ca href=\"https://github.com/tranquangvu\"\u003eBen Tran\u003c/a\u003e with ❤️\u003c/sub\u003e\n\u003c/div\u003e\n\n## General Information\n\n- Ruby version: `ruby 2.6.3`\n- Rails version: `rails 5.2.3`\n- Database: `postgresql`\n\n## Features\n\n- Rubocop config\n- Codeclimate config\n- Basic [devise](https://github.com/plataformatec/devise) authentication setup\n- View template render by [slim](http://slim-lang.com/)\n- Support Javascript ES6 in Assets Pipeline\n- Page-specific Javascript with [punchbox](https://github.com/GoldenOwlAsia/punchbox)\n- Easier form helpers with [simple_form](https://github.com/plataformatec/simple_form)\n- Pagination with [kaminari](https://github.com/kaminari/kaminari)\n- PDF generator with [wicked_pdf](https://github.com/mileszs/wicked_pdf)\n- Email preview in the browser instead of sending with [letter_opener](https://github.com/ryanb/letter_opener)\n- CSS styled email without the hassle with [premailer-rails](https://github.com/fphilipe/premailer-rails)\n- Annotate rails classes with schema and routes info [annotate_models](https://github.com/ctran/annotate_models)\n- Performance checking in development environment with [bullet](https://github.com/flyerhzm/bullet) and [rack-mini-profiler](https://github.com/MiniProfiler/rack-mini-profiler)\n- Environment variables loading with [dotenv](https://github.com/bkeepers/dotenv)\n- [Sidekiq](https://github.com/mperham/sidekiq) default for Active Job queue adapter\n- [Carrierwave](https://github.com/carrierwaveuploader/carrierwave) file upload (development, test evironments: local file storage - staging, production: AWS S3 fog storage)\n- Full settings for testing application: [rspec](https://rspec.info/), [factory_bot_rails](https://github.com/thoughtbot/factory_bot_rails), [faker](https://github.com/stympy/faker), [shoulda-matchers](https://github.com/thoughtbot/shoulda-matchers), [webmock](https://github.com/bblimke/webmock), [vcr](https://github.com/vcr/vcr)\n- Error tracking config in production with Sentry\n- Base class init for common patterns in rails application: Service Objects, Form Objects, Query Objects, Calculation Objects, Value Objects, Policy Objects, Decorators, etc\n\n## Quick Start\n\n1. Make sure that you have installed ruby, rails, redis and postgresql. Read [this guide](https://gorails.com/setup) to install if you don't have.\n2. Clone this repo using `git clone --depth=1 git@github.com:GoldenOwlAsia/go_rails_template.git \u003cYOUR_PROJECT_NAME\u003e`\n3. Move to the appropriate directory: `cd \u003cYOUR_PROJECT_NAME\u003e`\n4. Install correct ruby version for our project. If you have `rbenv`, use these commands:\n\n```\nrbenv install 2.6.3\nrbenv local 2.6.3\n```\n\n5. Install bundler: `gem install bundler`\n6. Install gems: `bundle install`\n7. Add database config: create `config/database.yml` file (refer from `config/database.yml.example`)\n8. Add environment variables: create `.env` file (refer from `.env.example`)\n9. Database setup: `bundle exec rake db:setup`\n10. Run sidekiq (make sure redis service is running): `bundle exec sidekiq`\n11. Start server: `rails s`\n12. Visit `http://localhost:3000` and start your development\n\n## Testing\n\n1. Start to run your specs by: `bundle exec rspec`\n2. See coverage by open `coverage/index.html` in web browser\n\n## Main Structure\n\n```\n  app\n  ├── assets\n  │   ├── javascripts\n  │   │   ├── application.js.es6\n  │   │   ├── cable.js.es6\n  │   │   ├── channels\n  │   │   └── views\n  │   │       └── home.js.es6\n  │   └── stylesheets\n  │       └── views\n  │       │   ├── home.scss\n  │       │   └── variables.scss\n  │       ├── common\n  │       │   ├── fonts.scss\n  │       │   └── variables.scss\n  │       └── application.scss\n  ├── calculations\n  │   └── application_calculation.rb\n  ├── controllers\n  │   ├── concerns\n  │   ├── application_controller.rb\n  │   └── home_controller.rb\n  ├── decorators\n  │   ├── application_decorator.rb\n  │   └── paginating_decorator.rb\n  ├── forms\n  │   └── application_form.rb\n  ├── helpers\n  │   └── application_helper.rb\n  ├── jobs\n  │   └── application_job.rb\n  ├── mailers\n  │   └── application_mailer.rb\n  ├── models\n  │   ├── concerns\n  │   └── application_record.rb\n  ├── policies\n  │   └── application_policy.rb\n  ├── queries\n  │   └── application_query.rb\n  ├── services\n  │   └── application_service.rb\n  ├── value_objects\n  │   └── application_value_object.rb\n  └── views\n      ├── devise\n      ├── home\n      ├── layouts\n      └── shared\n```\n\n## Common Patterns\n\nIn software engineering, a software design pattern is a general, reusable solution to a commonly occurring problem within a given context in software design. It is not a finished design that can be transformed directly into source or machine code. It is a description or template for how to solve a problem that can be used in many different situations. Design patterns are formalized best practices that the programmer can use to solve common problems when designing an application or system.\n\n#### Service Objects\n\nService objects are commonly used to mitigate problems with model callbacks that interact with external classes ([read more](https://samuelmullen.com/2013/05/the-problem-with-rails-callbacks/)). Service objects are also useful for handling processes involving multiple steps. E.g. a controller that performs more than one operation on its subject (usually a model instance) is a possible candidate for Extract ServiceObject (or Extract FormObject) refactoring. In many cases service object can be used as scaffolding for [replace method with object refactoring](https://sourcemaking.com/refactoring/replace-method-with-method-object). Some more information on using services can be found in [this article](https://medium.com/selleo/essential-rubyonrails-patterns-part-1-service-objects-1af9f9573ca1).\n\nDefining:\n\n```\nclass ActivateUserService \u003c ApplicationService\n  attr_reader :user\n\n  def initialize(user)\n    @user = user\n  end\n\n  def call\n    user.activate!\n    NotificationsMailer.user_activation_notification(user).deliver_later\n    user\n  end\nend\n```\n\nUsage:\n\n```\nuser = User.find(params[:id])\nActivateUserService.call(user)\n```\n\n#### Form Objects\n\nForm objects, just like service objects, are commonly used to mitigate problems with model callbacks that interact with external classes ([read more](https://samuelmullen.com/2013/05/the-problem-with-rails-callbacks/)). Form objects can be used as wrappers for virtual (with no model representation) or composite (saving multiple models at once) resources. In the latter case this may act as replacement for ActiveRecord::NestedAttributes. In some cases FormObject can be used as scaffolding for [replace method with object refactoring](https://sourcemaking.com/refactoring/replace-method-with-method-object). Some more information on using form objects can be found [in this article](https://medium.com/selleo/essential-rubyonrails-patterns-form-objects-b199aada6ec9).\n\nDefining:\n\n```\nclass UserRegistrationForm \u003c ApplicationForm\n  attr_accessor :user, :terms_of_service\n\n  delegate :attributes=, to: :user, prefix: true\n\n  validates :terms_of_service, acceptance: true\n\n  def initialize(user, params = {})\n    @user = user\n    super(params)\n  end\n\n  def submit\n    return false if invalid?\n    user.save\n  end\n\n  def persisted?\n    user.persisted?\n  end\nend\n```\n\nUsage:\n\n```\nuser = User.new\nform = UserRegistrationForm.new(user, permitted_params)\nform.submit\n```\n\n#### Query Objects\n\nOne should consider using query objects pattern when in need to perform complex querying on active record relation. Usually one should avoid using scopes for such purpose. As a rule of thumb, if scope interacts with more than one column and/or joins in other tables, it should be moved to query object. Also whenever a chain of scopes is to be used, one should consider using query object too. Some more information on using query objects can be found in [this article](https://medium.com/selleo/essential-rubyonrails-patterns-part-2-query-objects-4b253f4f4539).\n\nDefining:\n```\nclass RecentlyActivatedUsersQuery \u003c ApplicationQuery\n  query_on 'User'\n\n  def call\n    relation.active.where(activated_at: date_range)\n  end\n\n  private\n\n  def date_range\n    options.fetch(:date_range, default_date_range)\n  end\n\n  def default_date_range\n    Date.yesterday.beginning_of_day..Date.current.end_of_day\n  end\nend\n```\n\nUsage:\n```\nRecentlyActivatedUsersQuery.call\nRecentlyActivatedUsersQuery.call(date_range: Date.today.beginning_of_day..Date.today.end_of_day)\nRecentlyActivatedUsersQuery.call(User.male, date_range: Date.today.beginning_of_day..Date.today.end_of_day)\n```\n\n#### Calculation Objects\n\nCalculation objects provide a place to calculate simple values (i.e. numeric, arrays, hashes), especially when calculations require interacting with multiple classes, and thus do not fit into any particular one.\n\nDefining:\n```\nclass AverageHotelDailyRevenueCalculation \u003c ApplicationCalculation\n  def call\n    reservations.sum(:price) / number_of_days_in_year\n  end\n\n  private\n\n  def reservations\n    Reservation.where(\n      date: (beginning_of_year..end_of_year),\n      hotel_id: options[:hotel_id]\n    )\n  end\n\n  def number_of_days_in_year\n    end_of_year.yday\n  end\n\n  def year\n    options[:year] || Date.current.year\n  end\n\n  def beginning_of_year\n    Date.new(year).beginning_of_year\n  end\n\n  def end_of_year\n    Date.new(year).end_of_year\n  end\nend\n```\n\nUsage:\n```\nhotel = current_user.owned_hotel\nAverageHotelDailyRevenueCalculation.call(hotel_id: hotel.id)\nAverageHotelDailyRevenueCalculation.call(hotel_id: hotel.id, year: 2018)\n```\n\n#### Value Objects\n\nThe Value Object design pattern encourages simple, small objects (which usually just contain given values), and lets you compare these objects according to a given logic or simply based on specific attributes (and not on their identity).\n\nRead more at [value_objects document](https://github.com/GoldenOwlAsia/value_objects).\n\nDefining:\n```\nclass AddressValueObject \u003c ApplicationValueObject\n  attr_accessor :street, :postcode, :city\n\n  validates :postcode, presence: true\n  validates :city, presence: true\nend\n```\n\nUsage:\n\n```\naddress = AddressValueObject.new(street: '123 Big Street', city: 'Metropolis')\naddress.valid? # =\u003e false\naddress.errors.to_h # =\u003e {:postcode=\u003e\"can't be blank\"}\naddress.postcode = '12345' # =\u003e \"12345\"\naddress.valid? # =\u003e true\naddress.errors.to_h # =\u003e {}\n```\n\nUsage in Active Record:\n```\nclass User \u003c ActiveRecord::Base\n  include ValueObjects::ActiveRecord\n\n  value_object :company_addresses, AddressValueObject::Collection\n  value_object :home_address, AddressValueObject\nend\n```\n\n#### Policy Objects\n\nThe Policy Objects design pattern is similar to Service Objects, but is responsible for read operations while Service Objects are responsible for write operations. Policy Objects encapsulate complex business rules and can easily be replaced by other Policy Objects with different rules. For example, we can check if a guest user is able to retrieve certain resources using a guest Policy Object. If the user is an admin, we can easily change this guest Policy Object to an admin Policy Object that contains admin rules.\n\nRead more at [pundit document](https://github.com/varvet/pundit).\n\nDefining:\n```\nclass ArticlePolicy \u003c ApplicationPolicy\n  def create?\n    user.admin?\n  end\n\n  def update?\n    user.admin? \u0026\u0026 !record.published?\n  end\nend\n```\n\nUsage:\n```\n@article = Article.find(params[:id])\nauthorize @article, :update?\n@article.update(article_params)\n```\n\n\n#### Decorators\n\nThe Decorator Pattern allows us to add any kind of auxiliary behavior to individual objects without affecting other objects of the same class. This design pattern is widely used to divide functionality across different classes, and is a good alternative to subclasses for adhering to the Single Responsibility Principle.\n\nRead more at [draper document](https://github.com/drapergem/draper).\n\nDefine:\n```\nclass ArticleDecorator \u003c Draper::Decorator\n  delegate_all\n\n  def publication_status\n    if published?\n      \"Published at #{published_at}\"\n    else\n      \"Unpublished\"\n    end\n  end\n\n  def published_at\n    object.published_at.strftime(\"%A, %B %e\")\n  end\nend\n```\n\nUsage:\n```\narticle = Article.find(params[:id]).decorate\narticle.publication_status\narticle.published_at\n```\n\n## Deployment\n\nFor deployment, you can use AWS Elastic Beanstalk Service (AWS EB) which have most interesting parts from my experiences:\n- Handles the deployment process, you just need to bundle the app and send to EB then you’re free to do others thing. Don’t need to wait for processing tasks via SSH connection, as result in speed up your development.\n- Handles load balancing, auto-scaling by triggered to the app health monitoring. You can easy to setting up from EB management console.\n- And finally, no additional charge for using EB. If you are using the same resources (EC2, CloudFront, S3, RDS, Route 53, ElasticCache,…) then EB won’t add more charges to your bill.\n\nI have written [a complete guide to deploy rails application to AWS EB](https://github.com/tranquangvu/dev-notes/tree/master/deploys-rails-to-awseb). Please take a look on it.\n\n## License\n\nLicensed under the MIT license, see the separate LICENSE.md file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftranquangvu%2Fgo-rails-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftranquangvu%2Fgo-rails-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftranquangvu%2Fgo-rails-template/lists"}