TaxonWorks Docs
Guide
Develop
About
Start a project
Cite
API
Bug report
Contact
  • Docs
  • Code
  • API
  • English
  • Español
Guide
Develop
About
Start a project
Cite
API
Bug report
Contact
  • Docs
  • Code
  • API
  • English
  • Español
  • Develop
    • Overview
    • Contributing
    • Install
      • Development
        • Overview
        • Native
          • Operating Systems
          • Arch Linux
          • macOS
          • Ubuntu 22.04
          • Ubuntu 24.04
          • Windows 10 with WSL2
        • Docker
          • Build and push the development container to Docker
          • Docker
          • Windows 10 with WSL2
      • Production
        • Overview
        • Kubernetes
          • Overview
        • Capistrano
          • Overview
      • Overview
    • Code
      • Overview
      • Development Environment
      • Features
        • Scenarios
        • Scaffolding
        • Help bubbles
        • Rake task
        • Radial
        • TaxonWorks task
        • Tests
      • Conventions
      • Contributing
      • Troubleshoot
      • Roadmap
    • Data
      • Overview
      • Models
      • Tables
This site uses VuePress, a static site generator

Scaffolding

Scaffolding is an automated process to generate a model's code and basic infrastructure. If you are adding a new model at the level of TaxonWorks "Data" cards, you'll start here.

Scaffolding is a well documented Rails convention. In TaxonWorks we use the scaffolding process whenever we add a new data model that becomes the basis for a wide range of new features. This gives us the basic infrastucture to both see the class of data, and to populate TaxonWorks conventions in preparation for adding more advanced interfaces and utility, such as a TaxonWorks Task.

Tips

Scaffolding creates the code that will create the database table and the model that sits on top of it, builds basic views, and creates the various supporting conventions' files (controllers, views, test files). It's a quick way to do a lot of basic housekeeping work.

Warning

Within TaxonWorks scaffolding is now used infrequently. It is much more common to generate a new Task.

Here is a brief overview of a TaxonWorks flavoured scaffold, in this case extending our data model to include Organizations (this has already been done, with some important differences). Our intention here is to further draw attention to TaxonWorks-specific steps.

Using generate scaffold

Run the commands within the TaxonWorks directory in a shell. See Rails docs for a full explation, rails generate scaffold --help is also very useful.

rails generate scaffold Organization name:text description:text address:text project:references created_by_id:integer updated_by_id:integer --no-helper-specs --no-routing-specs --no-view-specs --no-request-specs -fixture -p

Tips

Always start with the -p flag, for "practice". This will display what will happen without actually creating the files. Run generate with this flag as many times as you need.

After file generation you'll make some edits.

Migration

Scaffolding creates a database migration that will create the databse table. This file is added to db/migrate/.

You'll need to add a couple things to the file that looks like db/migrate/20241207211545_create_organization.

On creation your file looks somethihng like this:

class CreateOrganizations < ActiveRecord::Migration[7.2]
  def change
    create_table :organizations do |t|
      t.text :name
      t.text :description
      t.text :address
      t.references :project, null: false, foreign_key: true
      t.integer :created_by_id
      t.integer :updated_by_id

      t.timestamps
    end
  end
end

There are some TaxonWorks specific tweaks to make:

  • Add null: false and index: true to the created/updated fields
  • After the create_table call, add foreign key references to User like add_foreign_key :organizations, :users, column: :updated_by_id

After these editions your file looks something like this:

class CreateOrganizations < ActiveRecord::Migration[7.2]
  def change
    create_table :organizations do |t|
      t.text :name
      t.text :description
      t.text :address
      t.references :project, null: false, foreign_key: true
      t.integer :created_by_id, null: false, index: true
      t.integer :updated_by_id, null: false, index: true

      t.timestamps
    end

    add_foreign_key :organizations, :users, column: :created_by_id
    add_foreign_key :organizations, :users, column: :updated_by_id
  end
end

To test you migration run it against the test database:

rake db:migrate RAILS_ENV=test

Refer to Rails migration documentation for further options.

Routes

Within config/routes.rb find the resource: block for your new model then:

  • Cut it from that file
  • Paste it alphabetically into config/routes/data.rb
  • Ensure the resources: block is present if it wasn't scaffolded
  • Include concerns [:data_routes]

Your block should look like:

  resources :organizations do
    concerns [:data_routes]
  end

Factory

See factory README.md.

Model

In app/models/organization.rb:

  • Document the model's attributes with @!attribute statements, see any model for examples. Note that as implemented Organization in TaxonWorks is 1:1 with another schema.* Add Concerns (extensions) to the model. All data models require Shared::IsData usually as the last concern
    • If the model has a project_id (is project specific) this will look like:
      include Housekeeping
      # ... others
      include Shared::IsData
    
    • You'll need to remove belongs_to :project_id if present, it's covered in the Housekeeping
    • If the model is community:
      include Housekeeping::Users
      include Housekeeping::Timestamps
      # ... others
      include Shared::IsData
    
    • If the model has a project_id, register the model in the MANIFEST constant in app/models/project.rb
    • Add at least 1-2 model tests (e.g. in spec/models/organization_spec.rb)

Config

  • Register the model in config/interface/hub/data.yml. See details therein.

Tips

At this point when you restart the server you should see a clickable card on Data

Controller

Controllers are at app/controller/, for example app/controllers/organizations_controller.rb. See the otus_controller.rb for example patterns.

Tips

Rails has naming conventions for pluralization. Models are singular, controllers are plural.

  • Add Concerns
    • If the model has a project_id:
class OrganizationsController < ApplicationController
  include DataControllerConfiguration::ProjectDataControllerConfiguration
  • If it is community (without project_id):
  include DataControllerConfiguration::SharedDataControllerConfiguration
  • Add the pagination after_action pattern if required
  • Update def index to stub in some variables for an html and json response (these become more sophisticated down the road):
    • Use @recent_objects in the html response
    • Use @organizations (or the model name) in the json response
    • Together it will look something like this:
  def index
    respond_to do |format|
      format.html do
        @recent_objects = Organization.where(project_id: sessions_current_project_id).order(updated_at: :desc).limit(10)
        render '/shared/data/all/index'
      end
      format.json {
        @organizations = Organization.where(project_id: sessions_current_project_id)
          .page(params[:page])
          .per(params[:per])
      }
    end
  end
  • Ensure .permit() calls permit writable attributes
  • Add anautocomplete action (method)
  • Add alist action

Tips

At this point of updating the controller, running the application and clicking through the model's card will raise errors on things missed or stubbed in error above.

Controller tests

We do not add new controller specs, but the generated specs are easily modified to work, so we typically modify them to follow an overall pattern, then leave them alone. The pattern has variously evolved, but generally it follows something like that below.

Add the logon to the spec:

  before(:each) {
    sign_in
  }

Use the corresponding factory to provide valid attributes:

  let(:valid_attributes) { strip_housekeeping_attributes(FactoryBot.build(:valid_organization).attributes) }

We don't use invalid_attributes.

Modify the #index call to reference @recent_objects:

  describe "GET #index" do
    it "assigns organizations to @recent_objects" do
      o = Organization.create! valid_attributes
      get :index, params: {}, session: valid_session
      expect(assigns(:recent_objects)).to include(o)
    end
  end

Add a #list spec:

  describe 'GET list' do
    it 'with no other parameters, assigns a page of @organizations' do
      o = Organization.create! valid_attributes
      get :list, params: {}, session: valid_session
      expect(assigns(:organizations)).to include(o)
    end

    it 'renders the list template' do
      get :list, params: {}, session: valid_session
      expect(response).to render_template('list')
    end
  end
  • Supply invalid_attributes where needed (the model validation rules will help determine invalid values)
  • Run the controller spec and correct as necessary.

Views

Tips

In these steps reference existing patterns in app/views/otus/ for details on how to conform the scaffolded files to TaxonWorks' conventions.

  • Delete index.html.erb
  • Follow Rails conventions for _form.html.erb. See any other _form.html.erb for TaxonWorks conventions and markup. Replace the partials as needed (e.g. /shared/errors)
  • Ensure you have JSON responses for show and index actions
    • Rename the _organization.json.jbuilder to _attributes.json.jbuilder, edit accordingly.
    • Rename _organizations.html.erb to _attributes.html.erb so that shared view can render attributes.
  • Update show.html.erb to use a shared view replacing everything with <%= render(partial: 'shared/data/project/show', locals: {object: @organization}) -%>
  • Add a list.html.erb

Tips

To disable the new link in the shared index view add the model name to the constant in Workbench::NavigationHelper module

Autocomplete

Most models use an autocomplete in various places.

  • Add def autocomplete to the controller
    • Return no more than 20 records, autocompletes reference user input like (name: params.require(:term))
  • Add autocomplete.json.jbuilder to views/, typically you can copy from /app/views/otus/ and adapt
  • !! Add _quick_search_form.html.erb to views/, this is used in auto-generated show, index, etc., again see otus and adapt
  • Add a method in helpers/:
  def sounds_search_form
    render('/organizations/quick_search_form')
  end

Warning

Not seeing autocomplete forms on scaffolded views? You are likely missing the <model>s_search_form method in helpers/.

After scaffolding, as implementation advances, see also the /lib/queries/<model>/autocomplete.rb pattern for advanced autocomplete logic.

View specs

We do not use view specs, remove if you missed applying the flag in the generate scaffold commnd.

Queries

  • You may want/need to add a filter or autocomplete query for the model. See README.md

Helpers

  • There are some must-do here, see helper README
Edit this page
Last Updated:
Contributors: mjy, Matt, Tom Klein
Prev
Scenarios
Next
Help bubbles