# Katapult `Katapult` is a kickstart generator for Rails applications. It creates new Rails applications with [lots of pre-configuration](https://github.com/makandra/katapult/blob/master/lib/generators/katapult/basics/basics_generator.rb) and offers [makandra-flavored](https://leanpub.com/growing-rails) code generation from an application model. These two features significally speed up the initial phase of a Rails project by doing in minutes what otherwise would cost you weeks. After modeling your application, which typically takes about an hour, you can instantly start implementing the meat of your application. `Katapult` only supports a single Ruby and Rails version, currently it's Rails 5.1.4 and Ruby 2.5.0. ## Prerequisites Katapult uses *PostgreSQL* as database, so you'll need to install that upfront. Also, it drops the asset pipeline in favor of *Webpacker*, so you'll need Node and Yarn (see ). Also, it requires the *Bundler* and *Rake* Ruby gems, which are probably already installed on your system. ## Installation Install the `katapult` gem with gem install katapult If you intend to extend an existing application, add it to the development group in your Gemfile. ## Usage `katapult` does two distinct things for you: 1. It creates a new Rails application, set up with many standard gems, snippets, useful configuration, databases, testing libraries etc. 2. It generates code from an application model, i.e. it creates models and views, controllers, styles etc. You may use both or only one of them. Read on for details. ## 1) Creating a new Rails application Run the following command: katapult new $APPLICATION_NAME This will create a new Rails application and prepare it in more than 20 steps. Read the [BasicsGenerator](https://github.com/makandra/katapult/blob/master/lib/generators/katapult/basics/basics_generator.rb) for details: Its methods are executed one-by-one, while the method names are a description of what they do. ### Alternative: Using Katapult in an existing Rails application `katapult` expects a fresh application (which it would usually generate itself). If you have an existing Rails application, you *may* use `katapult`, but be warned: it is not designed to respect existing files, although it will usually ask before overwriting something. After adding it to the Gemfile, run the basics generator manually: bin/rails generate katapult:basics ## 2) Generating code from an application model After running `katapult new`, you will find a default application model in `lib/katapult/application_model.rb`. It contains a full example of `katapult`'s features that you should replace with _your_ application model. When you are done, transform the model using: katapult fire [path/to/application_model] The path is optional and only needs to be specified when you've renamed the application model file. Note that you may well use separate model files for separate runs. See an overview of the DSL below. The respective sections hold examples of what options are available to each element. For details, dive into `lib/generators/katapult` where all generators are stored. The method names of a generator tell what it does. ### Generic DSL syntax example The DSL consists of _elements_, e.g. `Model` or `WebUI`. Each `katapult` element has the following syntax, taking a name, options, and a block: element_type 'name', options: 'example' do |element| element.some_method end ### Model Takes a name and a block. Generates a Rails model, a migration, and a spec. model 'Customer' do |customer| # customer.attr :name etc, see Attribute element # customer.belongs_to :group, see Association element end #### Attribute Defined on Model; takes a name and options. Adds a database field in the model migration, form fields in a WebUI, and configuration code in the model as needed. # Default type :string model.attr :name # Inferred type :email (when attr name matches /email/) model.attr :email # Inferred type :password. Password fields are rendered as password_field in # forms, but never rendered in show views. model.attr :password # Specify assignable values. Available options: allow_blank, default model.attr :age, type: :integer, assignable_values: 18..99, allow_blank: true # Will be rendered as number_field in forms, and with a € sign in show views model.attr :income, type: :money # All attribute types take an optional default model.attr :homepage, type: :url, default: 'http://www.makandra.de' # Boolean fields are modeled as flags. Default required! model.attr :locked, type: :flag, default: false # JSON fields are supported model.attr :prefer, type: :json # PostgreSQL "jsonb" model.attr :avoid, type: :plain_json # PostgreSQL "json" ### Association Defined on Model; takes the name of another model just like you called it in the model. Adds a foreign key attribute to the model and `belongs_to`/`has_many` calls to the respective models. model 'Customer' do |customer| customer.belongs_to 'Group' end model 'Group' ### WebUI Takes a name, options and a block. Generates controller, routes, views, and a passing Cucumber feature. web_ui 'Customer', model: 'User' do |web_ui| web_ui.crud # Create all the standard rails actions # web_ui.action :custom etc, see Action element end # Short syntax with inferred model name 'User' web_ui 'User', &:crud #### Action Defined on WebUI; takes a name and options. Adds an action to the controller, and a route as needed. # Select single Rails actions web_ui.action :index web_ui.action :show web_ui.action :create # also creates :new web_ui.action :update # also creates :edit web_ui.action :destroy # Add custom actions web_ui.action :custom_action, method: :post, scope: :member web_ui.action :other_action, method: :get, scope: :collection ### Crud Shortcut for creating a model together with a WebUI with CRUD actions. crud 'Customer' do |customer| customer.attr :name end ### Navigation Generates a main menu with links to the index pages of all WebUIs. navigation ### Authenticate Takes the name of the user model (currently only `User` is supported) and an email address. Generates authentication with [Clearance](https://github.com/thoughtbot/clearance). authenticate 'User', system_email: 'system@example.com' The given email address will be configured as the sender for all mails sent by Rails, including Clearance mails like password reset requests. ## Development ### Getting started + continuing development `Katapult` is tested with [RSpec](http://rspec.info/) and [Cucumber](https://cucumber.io/) + [Aruba](https://github.com/cucumber/aruba) ([API-Doc](http://www.rubydoc.info/github/cucumber/aruba/master/)). For its full-stack integration tests, `katapult` requires a PostgreSQL account. Create a dedicated account on your local PostgreSQL server: $> sudo -u postgres psql postgres=# CREATE ROLE katapult WITH createdb LOGIN; Whenever you start working on `katapult`, you should run `script/update`, which will guide you through a quick update process. ### Architecture `katapult` is roughly split into three parts: the `katapult` binary in bin/, the model in lib/katapult/ and the generators in lib/generators. Also, there is a script/ directory that holds some scripts to ease development. It is not part of the `katapult` gem, however. The generators of `katapult` base on the `rails/generators` you probably know from generating migration files or scaffolds; however, it lifts their usage on a new level by invoking generators programmatically with a "model object". Instead of plain text input, the `katapult` generators can explore the whole application model. They are all to be run from within a Rails application. There are three base generators that can be considered the next-lower level API of `katapult`: - `basics` generator: Enhances a pristine Rails app with all of the basic configuration `katapult` brings. - `app_model` generator: Installs a boilerplate application model that serves as a starting point for modeling your own application. - `transform` generator: Parses the application model into an internal representation, which will be turned into code by all the other generators. Note that the `katapult` binary is the only Rails-independent part of Katapult; everything else runs in the context of the Rails appplication. ### Suggested workflow When adding a feature to `katapult`, it will usually take you some time to figure out how exactly the generated code should look like. You'll be switching between `katapult`'s tests, its generators and the generated code. Here's a the suggested process: 1) Run a scenario (create one if needed) 2) Tag that scenario with @no-clobber. This will leave the generated test app untouched in subsequent test runs. 3) Make a commit inside the generated test application, so you'll have a clean working directory: `script/kta git add --all && script/kta git commit -m 'x'` 4) Modify the test app as needed. Boot a development server with `script/kta rails s` if you like. 5) Re-run the @no-clobber scenario (modify it as needed) until test and test app meet the expectations. 6) Now look at the git diff in the test app and model everything with katapult's generators. 7) Remove the @no-clobber tag and run the scenario normally to see if it's still green. Remember to stop the development server first. ### Guidelines Please respect the following guidelines during development: - The application model should be order-agnostic. There is a #prepare_render method in `ApplicationModel` for things that need to happen between parsing and rendering the application model. ### Debugging Add the `@announce-output` tag to `katapult` features in order to have any output logged to your terminal. Note that each step will print all output to date, so you will see things multiple times. To precisely debug errors occurring _inside_ the generated application, use `script/kta`. You could also just cd to the test app directory, but since it is destroyed between test runs, you'd need to `cd ../../aruba/katapult_test_app` after each test. When fixing issues in the generated app, make a commit in the app first. When you've fixed it, the diff will show you what you need to port back to katapult. ### Typical issues Be sure to check this list when you encounter strange issues during development. - Spring was running in a directory that does not exist any more. This will screw subsequent spring invocations. Run `ps aux | grep spring` and `kill` suspect processes. - An outdated Rails application in `tmp/cached_*` - Timeout error because of a script waiting for user input ### Fast tests Generating basics and transforming the application model take quite some time (about 20s), because it boots the Rails application, resolves Yarn dependencies and migrates databases. To speed that up, `katapult` tests cache prepared Rails applications in tmp/. When debugging test suite speed, `bundle exec cucumber --format usage` is your friend. ## Credits Development: Dominik Schöler from [makandra](makandra.com)
Katapult image: [Nemo](http://pixabay.com/de/katapult-30061)