How to create a plugin

Bram Jetten edited this page Nov 25, 2017 · 10 revisions

Plugins are a work in progress. Suggestions are welcome!

Plugins allow you to share and reuse common features. In this guide we're going to create a plugin called 'events'.

Final example app

Generate the plugin

From an existing Spina app, run the rails generator for a 'full' rails engine:

rails plugin new vendor/plugins/events --full

This will create an engine inside your 'vendor/plugins' directory. Update the newly created gemspec with your contact information and add Spina as a dependency:

# /vendor/plugins/events/events.gemspec
Gem::Specification.new do |s|
  s.name        = "events"
  s.version     = Events::VERSION
  s.authors     = ["Your name"]
  s.email       = ["yourname@domain.com"]
  s.homepage    = "http://www.spinacms.com/"
  s.summary     = "Summary of Events."
  s.description = "Description of Events."
  s.license     = "MIT"

  s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.rdoc"]
  s.test_files = Dir["test/**/*"]

  s.add_dependency "rails"
  s.add_dependency "spina"

  s.add_development_dependency "sqlite3"
end

In order to register the plugin on Spina configuration, update the vendor/plugins/events/lib/events/engine.rb with the following lines:

require 'spina'

module Events
  class Engine < ::Rails::Engine

    initializer 'spina.plugin.register.events', before: :load_config_initializers do
      ::Spina::Plugin.register do |plugin|
        plugin.name       = 'Events'
        plugin.namespace  = 'events'
      end
    end

  end
end

Creating the Event resource

Model

First generate the model events inside the engine. Go to the plugin directory and run the generator:

cd vendor/plugins/events
rails g model spina/event title:string description:text date:datetime 
Routes

We want to be able to manage our new resource inside the Spina admin. Add the following routes:

# /vendor/plugins/events/config/routes.rb
Spina::Engine.routes.draw do
  namespace :admin, path: Spina.config.backend_path do
    resources :events, except: [:show]
  end
end

Notice that you have to change Events::Engine.routes.draw to Spina::Engine.routes.draw. This will add the routes to the existing Spina engine instead of the rails app.

Controller

Create the events controller:

# /vendor/plugins/events/app/controllers/spina/admin/events_controller.rb
module Spina
  module Admin
    class EventsController < AdminController

      before_action :set_breadcrumb
      before_action :set_event, only: [:edit, :update, :destroy]

      layout 'spina/admin/admin'

      def index
        @events = Event.all
      end

      def new
        @event = Event.new
      end

      def edit
      end

      def create
        @event = Event.new(event_params)

        if @event.save
          redirect_to admin_events_path, notice: 'Event was successfully created.'
        else
          render :new
        end
      end

      def update
        if @event.update(event_params)
          redirect_to admin_events_path, notice: 'Event was successfully updated.'
        else
          render :edit
        end
      end

      def destroy
        @event.destroy
        respond_to do |format|
          redirect_to admin_events_path, notice: 'Event was successfully destroyed.'
        end
      end

      private

      def event_params
        params.require(:event).permit(:title, :description, :date)
      end

      def set_breadcrumb
        add_breadcrumb 'Events', admin_events_path
      end

      def set_event
        @event = Event.find(params[:id])
        add_breadcrumb @event.title
      end

    end
  end
end
Views

Add the following views:

-# /vendor/plugins/events/app/views/spina/admin/events/index.html.haml
.filters
  = link_to 'New event', spina.new_admin_event_path, class: 'button button-primary', data: {icon: 't'}

.table-container
  %table.table
    %thead
      %tr
        %th Title
        %th Date
        %th

    %tbody
      - if @events.any?
        - @events.each do |event|
          %tr
            %td
              = event.title
            %td
              = l(event.date)
            %td.nowrap.align-right
              = link_to spina.edit_admin_event_path(event), class: 'button button-link' do
                = icon('pencil-outline')

      - else
        %tr
          %td.align-center{colspan: 3}
            %em There are no events
-# /vendor/plugins/events/app/views/spina/admin/events/_form.html.haml
= form_for [:admin, @event], html: {autocomplete: "off"} do |f|
  - content_for :notification_alert do
    = error_explanation!(@event)

  #page_content
    .table-container
      %table.table.table-form
        %tr
          %td
            Title
          %td
            = f.text_field :title
        %tr
          %td
            Description
          %td
            = f.text_area :description
        %tr
          %td
            Date
          %td
            = f.text_field :date, value: (@event.date.strftime("%d-%m-%Y") unless @event.new_record?), class: 'datepicker'

  %button.button.button-primary{type: 'submit', data: {icon: 'o'}}
    Save

  = link_to 'Cancel', admin_events_path, class: 'button button-link'

  - unless @event.new_record?
    .pull-right= link_to 'Delete', admin_event_path(@event), method: :delete, confirm: 'Are you sure?', class: 'button button-link button-danger'
-# /vendor/plugins/events/app/views/spina/admin/events/new.html.haml
= render 'form'
-# /vendor/plugins/events/app/views/spina/admin/events/edit.html.haml
= render 'form'
Menu Navigation

Next we want to add links to our new resource. Spina provides 'hooks' to add your own views to existing layouts. During the rendering of a layout, Spina will search for a view with a specific name in the hooks directory. If such a view exists, then Spina will render the view. For now the following hooks are available:

  • [plugin-root]/app/views/spina/admin/hooks/[plugin-name]/_primary_navigation.html.haml
  • [plugin-root]/app/views/spina/admin/hooks/[plugin-name]/_website_secondary_navigation.html.haml
  • [plugin-root]/app/views/spina/admin/hooks/[plugin-name]/_settings_secondary_navigation.html

For this example we're going to add links to the 'Your website' menu by creating the following view:

-# vendor/plugins/events/app/views/spina/admin/hooks/events/_website_secondary_navigation.html.haml
%li{ class: ('active' if current_admin_path.start_with?('/events')) }
  = link_to spina.admin_events_path do
    = plugin.name

The final result will look like this:

Spina admin events

Hook Into the Application

Add the gem to the Gemfile to your main rails app:

gem 'events', path: 'vendor/plugins'

Now you will be able to install the gem with bundle install, copy and install the migrations and restart your server.

bundle install
rake events_engine:install:migrations
rake db:migrate
touch tmp/restart.txt
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.