{"id":20676573,"url":"https://github.com/calebowens/rails-starterpack","last_synced_at":"2025-04-19T20:56:11.157Z","repository":{"id":212823033,"uuid":"732372002","full_name":"calebowens/rails-starterpack","owner":"calebowens","description":null,"archived":false,"fork":false,"pushed_at":"2024-07-10T16:03:01.000Z","size":411,"stargazers_count":6,"open_issues_count":12,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-29T13:22:21.332Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/calebowens.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-12-16T13:09:52.000Z","updated_at":"2025-01-07T03:23:13.000Z","dependencies_parsed_at":"2024-03-04T19:46:37.699Z","dependency_job_id":"2dea4135-b418-4025-8f76-1e1f85093ddc","html_url":"https://github.com/calebowens/rails-starterpack","commit_stats":null,"previous_names":["calebowens/rails-starterpack"],"tags_count":1,"template":true,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calebowens%2Frails-starterpack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calebowens%2Frails-starterpack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calebowens%2Frails-starterpack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calebowens%2Frails-starterpack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/calebowens","download_url":"https://codeload.github.com/calebowens/rails-starterpack/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249799659,"owners_count":21326980,"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":[],"created_at":"2024-11-16T21:13:01.778Z","updated_at":"2025-04-19T20:56:11.113Z","avatar_url":"https://github.com/calebowens.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# README\n\n## Production Readyness\n\nThere are two goals for this project\n1. To try and build a starting point which I can use to put together a project in a very short amount of time.\n2. To try and answer some of the \"what ifs\" that I end up thinking about at my day job, but would be wholly\n   inapropriate to implement in a work setting due to their \"unrailsy\" nature\n\nWhile this is a project that I'm using in apps that I hope will serve real customers, and I'm putting a reasonable\neffort into mantaining this. Given the fact that there are experiemental code structure choises and dev builds\nimportmaps, caution is advised!\n\n## Development environment setup\n\n### Pre-Commit Hooks\n\nEveryone should have pre-commit hooks setup!\n\n1. Run `./install_git_hooks.sh`\n\n3. Voila!\n\n## Structure\n\n- app\n  - features \u003c- Idea behind this is that we focus more on feature based groupings, rather than categorising code. Part\n    of the idea is that it will make each feature its own isolated application, with some shared common utilities\n    - pages\n    - authentication\n    - dashbaord\n  - common\n    - components \u003c- contains shared phlex components\n\n## Chosen stack\n\n- View\n  - [Phlex](https://phlex.fun) templating\n- JS and CSS\n  - Served via [Propshaft](https://github.com/rails/propshaft)\n  - JS\n    - Imports \"managed\" with [importmaps-rails](https://github.com/rails/importmap-rails)\n      - Importantly, we're using v1 of importmaps as there are a handful of issues in v2 as it moves to a vendor only approach\n    - [AlpineJS](https://alpinejs.dev) over Stimulus\n    - Routing with railsware's [js-routes](https://github.com/railsware/js-routes)\n    - [@rails/request.js](https://github.com/rails/request.js) fetch wrapper\n  - CSS\n    - [dartsass-rails](https://github.com/rails/dartsass-rails) for SCSS\n      - Nesting is becoming available in all the major browsers in pure CSS, so I could be convinced to drop dartsass at\n        some point.\n    - Custom starter styles with a \"prefer margin on bottom\" approach and custom utility classes.\n- Forms\n  - Forms are done with form_with\n  - Subjects of forms should be custom ActiveModel::Model's which handles all validations. The subject of a form should\n    never be a Database model\n- Hosting\n  - Custom development and production docker compose setup\n- Testing\n  - Minitest with rails's active support extensions [see](https://guides.rubyonrails.org/testing.html)\n  - [Capybara](https://github.com/teamcapybara/capybara) with [Cuprite](https://github.com/rubycdp/cuprite) for system testing\n  - Database records inside tests should be as they would be in the app, without factorybot or fixtures.\n- Formatting\n  - [Standard RB](https://github.com/standardrb/standard) and [Standard Rails](https://github.com/standardrb/standard-rails)\n- Mail chatching\n  - [Mailhog](https://github.com/mailhog/MailHog)\n- Jobs\n  - Handled via [Good Job](https://github.com/bensheldon/good_job)\n    - May shift over to [Solid Queue](https://github.com/basecamp/solid_queue) depending on how its feature set evolves\n- Authentication\n  - Using a bispoque authentication system based on \"has_secure_passsword\" which should be simple to build upon with\n    omniauth, or replace with another authentication system.\n- Caching\n  - [Solid Cache](https://github.com/rails/solid_cache)\n- N+1 catching via [Bullet](https://github.com/flyerhzm/bullet)\n\n## Style\n\n### AlpineJS in Phlex markup\n\nPrefer `\"x-ref\": \"foo\"` over `x_ref: \"foo\"` or `\"x-ref\" =\u003e \"foo\"`\n\n### Routes\n\nPrefer `get \u003cpath\u003e, to: \u003ccontroller\u003e` style of routes as it allows for a more consistent hash style when paired with\nstandardrb\n\n### Function calls\n\nWhen we have a function whose value is not used, IE:\n```rb\ncats.push \"tabby\"\nputs \"foobar\"\n```\nwe call it without parans, unless passing a block using curly braces, IE:\n```rb\nsubmission.ensure(:password, message: \"Password must be more than 8 character\") { _1.size \u003c= 8 }\n```\n\nWhen we use the return of the function, IE:\n```rb\np make_number(\"123\") + 123\nasdf = make_number(\"321\")\n```\nwe call it with parans\n\n\n## Thoughts and plans\n\n## Example Controller\n```rb\nclass Authentication::Pages::LoginController \u003c ApplicationController\n  class ViewContext \u003c ActiveSupport::CurrentAttributes\n    attribute :form_object\n  end\n\n  class FormObject\n    include ActiveModel::Model\n    include HasErrors\n\n    attr_accessor :email, :password\n\n    validates_presence_of :email\n    validates_presence_of :password\n\n    validate :ensure_user_exists # Must run last for security\n\n    def user\n      @user ||= User.from_login_details(email:, password:)\n    end\n\n    def ensure_user_exists\n      return unless errors.blank?\n\n      if user.nil?\n        errors.add :email, :no_user, message: \"and password provided did not match our records\"\n      end\n    end\n  end\n\n  class View \u003c ApplicationView\n    def template\n      h1 { \"Login\" }\n\n      render Form\n    end\n  end\n\n  class Form \u003c ApplicationView\n    def template\n      form_with(model: ViewContext.form_object, url: authentication_login_path, id: \"login\") do |f|\n        f.label :email, \"Email\"\n        f.text_field :email\n        render ViewContext.form_object.errors_for(:email)\n\n        f.label :password, \"Password\"\n        f.password_field :password\n        render ViewContext.form_object.errors_for(:password)\n\n        f.submit \"Login\"\n      end\n    end\n  end\n\n  def view\n    redirect_to root_path if Current.user\n\n    ViewContext.form_object = FormObject.new\n\n    render View\n  end\n\n  def submit\n    ViewContext.form_object = FormObject.new(user_params)\n\n    if ViewContext.form_object.valid?\n      ViewContext.form_object.user.add_to_session(session)\n\n      redirect_to dashboard_home_path, status: :see_other\n    else\n      render_or_replace_id(\n        page: -\u003e { View.new },\n        target_id: \"login\",\n        replacement: -\u003e { Form.new }\n      )\n    end\n  end\n\n  def user_params\n    params.require(:authentication_pages_login_controller_form_object).permit(\n      :email,\n      :password\n    )\n  end\nend\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcalebowens%2Frails-starterpack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcalebowens%2Frails-starterpack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcalebowens%2Frails-starterpack/lists"}