├── .gitignore ├── .travis.yml ├── Appraisals ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── cljs-rails.gemspec ├── gemfiles ├── rails_4.gemfile └── rails_5.gemfile ├── lib ├── cljs-rails.rb ├── cljs │ ├── rails.rb │ ├── rails │ │ ├── helper.rb │ │ └── version.rb │ └── railtie.rb ├── generators │ ├── cljs_rails │ │ └── install_generator.rb │ └── templates │ │ ├── Procfile │ │ ├── boot.properties │ │ ├── build.boot │ │ ├── core.cljs │ │ └── main.cljs.edn └── tasks │ └── cljs.rake └── spec ├── helper_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | /log 11 | *.bundle 12 | *.so 13 | *.o 14 | *.a 15 | mkmf.log 16 | *.gem 17 | *.gemfile.lock 18 | 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - "2.3.3" 4 | - "2.2.6" 5 | - 2.1 6 | gemfile: 7 | - gemfiles/rails_4.gemfile 8 | - gemfiles/rails_5.gemfile 9 | 10 | matrix: 11 | fast_finish: true 12 | allow_failures: 13 | - gemfile: gemfiles/rails_5.gemfile 14 | rvm: 2.1 15 | 16 | script: bundle exec rspec 17 | 18 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | appraise "rails-4" do 2 | gem "rails", "4.2.7" 3 | end 4 | 5 | appraise "rails-5" do 6 | gem "rails", "5.0.1" 7 | end 8 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in cljs-rails.gemspec 4 | gem 'rspec' 5 | gem 'rake' 6 | gem 'appraisal' 7 | gemspec 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Bogdan Dumitru 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/bogdan-dumitru/cljs-rails.svg?branch=master)](https://travis-ci.org/bogdan-dumitru/cljs-rails) [![Gem Version](https://badge.fury.io/rb/cljs-rails.svg)](http://badge.fury.io/rb/cljs-rails) [![Gem Downloads](https://img.shields.io/gem/dtv/cljs-rails.svg)](https://rubygems.org/gems/cljs-rails) 2 | 3 | # ![logo](http://i.imgur.com/jUig9Ck.png) cljs-rails 4 | 5 | **Join the functional bandwagon now in just a few easy steps!** 6 | 7 | If you're reading this you're either: 8 | 9 | - (a) sitting in lawnchair relaxed because you've [found happiness](https://www.youtube.com/watch?v=A0VaIKK2ijM) 10 | - (b) on your way to becoming fundamentally a better person 11 | 12 | **cljs-rails** wants to help you integrate clojurescript into an existing Rails application without too much hassle. It depends on [boot](https://github.com/boot-clj/boot) to compile your clojurescript and provides a minimal template to get up and running fast with that functional goodness. 13 | 14 | > My drive towards clojurescript was cristalized by [re-frame](https://github.com/Day8/re-frame). So I'm working on an updated version with an option that sets up a re-frame app scheleton with potential [db schemas](https://github.com/Day8/re-frame/blob/master/docs/ApplicationState.md#create-a-leveragable-schema) derived from the Rails models. Stay tuned! 15 | 16 | ## Boot vs Leiningen 17 | 18 | > Potential for flamewar, check. 19 | 20 | I'm new to the clojurescript universe and, after playing around a bit with the tools, I found boot to provide a smoother startup experience. Especially for people who are just getting started with the ecosystem. Granted the design choices diverge a bit from the *data all the way down* that we hold dear, their [arguments](https://news.ycombinator.com/item?id=8553189) seem to hold water. 21 | 22 | ## Installation 23 | 24 | First install boot. See the [install guide](https://github.com/boot-clj/boot#install) for more info. 25 | 26 | Add this line to your application's Gemfile: 27 | 28 | ```ruby 29 | gem 'cljs-rails' 30 | ``` 31 | 32 | And then execute: 33 | 34 | $ bundle 35 | 36 | Or install it yourself as: 37 | 38 | $ gem install cljs-rails 39 | 40 | Run the generator 41 | 42 | $ bundle exec rails generate cljs_rails:install 43 | 44 | Bundle install again because the generator adds a new dependency (foreman) 45 | 46 | $ bundle 47 | 48 | ## Post-Install 49 | 50 | Because the cljs build (powered by boot) needs to be run in parallel with the rails server, ``foreman`` was added to the gemfile and a basic ``Procfile`` that starts both these processes. So from now on instead of ``bundle exec rails server`` you should do: 51 | 52 | $ foreman start 53 | 54 | > Because of the way clojure works the first time you start foreman (or run ``boot dev``) it will download all dependencies and also build your project. This is the equivalent, in webpack world, of doing both the ``npm install`` and ``webpack build``. Subsequent builds will not download depndencies and of course the dev tasks starts a watch that does hot-reloading and incremental builds (2k17). 55 | 56 | Currently the bundle isn't loaded anywhere in your Rails app, you must add it to your layout using the ``cljs_main_path`` helper: 57 | 58 | ```erb 59 | <%= javascript_include_tag cljs_main_path %> 60 | ``` 61 | 62 | After doing this you should navigate to an action and see clojurescript devtools messages in your browser console. 63 | 64 | Also, the generated core/main function injects "Hello world" into the document body. 65 | You can go to ``cljs/src//core.cljs`` and edit the text there. It should automagically recompile and run again in the browser! Yey! 66 | 67 | ## Production 68 | 69 | The production build is configured by default to output to ``app/assets/cljs-build/``. This means that the sprockets can now find it. 70 | The ``cljs_main_path`` helper will just return "main.js" when in production so sprokets will pickup the build file. In development/test it uses the dev-server settings. 71 | 72 | You should add the precompile path to ``production.rb``: 73 | ```ruby 74 | config.assets.precompile += [ 'main.js' ] 75 | ``` 76 | 77 | > By default ``app/assets/cljs-build`` is added to gitignore just in case, but you might want to (due to some limitations in your environemnt, but you shouldn't) commit your build artefacts to the repo. 78 | 79 | #### Deployment to Heroku 80 | 81 | > See the [sample-cljs-rails-app](https://github.com/bogdan-dumitru/sample-cljs-rails-app). 82 | 83 | > [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/bogdan-dumitru/sample-cljs-rails-app) 84 | 85 | The relevent parts of ``app.json`` are: 86 | ```json 87 | "env": { 88 | "BOOTBUILD_CMD": { 89 | "description": "The command used by the boot buildback to compile cljs", 90 | "value": "boot prod" 91 | } 92 | }, 93 | "buildpacks": [ 94 | { "url": "https://github.com/taylorSando/heroku-buildpack-boot" }, 95 | { "url": "https://github.com/heroku/heroku-buildpack-ruby" } 96 | ] 97 | ``` 98 | 99 | To deploy to Heroku your app needs: 100 | - ``BOOTBUILD_CMD`` env var that specifies the build command (``boot prod`` in the default case) 101 | - a [boot buildpack](https://github.com/taylorSando/heroku-buildpack-boot) next to the ruby one 102 | 103 | ##### Procfile 104 | 105 | Heroku also uses the Procfile in production to spin up your web/worker dynos. To avoid this clash you can create``Procfile.dev`` that contains the processes that need to run on development. Then you can either: 106 | 107 | - (a) run ``foreman start -f Procfile.dev`` or 108 | - (b) create a ``.foreman`` file (that's .gitignored). and add ``procfile: Procfile.dev`` in it. You can then do ``foreman start`` as usual. 109 | 110 | #### Custom deployment 111 | 112 | There's a rake tasks provided ``cljs:compile`` that builds for production (with advanced optimisations). 113 | It just runs ``boot #{production_task}``. Production task defaults to "prod" and is defined in the ``build.boot`` template, but you can configure it via ``config.cljs.production_build_task``. 114 | 115 | You can enhance the ``assets:precompile`` task so that it runs ``cljs:compile`` every time. Add this to a rake file: 116 | 117 | ```ruby 118 | Rake::Task['assets:precompile'].enhance ['cljs:compile'] 119 | ``` 120 | 121 | Just make sure that the server that's precompiling your assets has ``boot`` setup. 122 | 123 | ## Notes 124 | 125 | ### Structure 126 | 127 | The generator sets up a ``cljs`` folder with the source, a main and a namespace derived from the rails app name. 128 | 129 | ``` 130 | ▾ cljs/ 131 | ▾ src/ 132 | ▾ / 133 | core.cljs 134 | main.cljs.edn 135 | ``` 136 | 137 | > You can provide a different name as the first argument of the install generator. 138 | 139 | ## Prior art 140 | 141 | - [webpack-rails](https://github.com/mipearson/webpack-rails) 142 | 143 | ## Contributing 144 | 145 | 1. Fork it ( https://github.com/bogdan-dumitru/cljs-rails/fork ) 146 | 2. Create your feature branch (`git checkout -b my-new-feature`) 147 | 3. Commit your changes (`git commit -am 'Add some feature'`) 148 | 4. Push to the branch (`git push origin my-new-feature`) 149 | 5. Create a new Pull Request 150 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | -------------------------------------------------------------------------------- /cljs-rails.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'cljs/rails/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "cljs-rails" 8 | spec.version = Cljs::Rails::VERSION 9 | spec.authors = ["Bogdan Dumitru"] 10 | spec.email = ["dumbogdan@gmail.com"] 11 | spec.summary = %q{Clojurescript integration for Rails, inspired by webpack-rails} 12 | spec.description = %q{Boot powered helpers to get up and running fast with Clojurescript in your Rails application} 13 | spec.homepage = "" 14 | spec.license = "MIT" 15 | 16 | spec.required_ruby_version = '>= 2.1.0' 17 | 18 | spec.files = `git ls-files -z`.split("\x0") 19 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 20 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 21 | spec.require_paths = ["lib"] 22 | 23 | spec.add_dependency "railties", ">= 4.2.7" 24 | spec.add_development_dependency "rails", ">= 4.2.7" 25 | end 26 | -------------------------------------------------------------------------------- /gemfiles/rails_4.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "rspec" 6 | gem "rake" 7 | gem "appraisal" 8 | gem "rails", "4.2.7" 9 | 10 | gemspec :path => "../" 11 | -------------------------------------------------------------------------------- /gemfiles/rails_5.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "rspec" 6 | gem "rake" 7 | gem "appraisal" 8 | gem "rails", "5.0.1" 9 | 10 | gemspec :path => "../" 11 | -------------------------------------------------------------------------------- /lib/cljs-rails.rb: -------------------------------------------------------------------------------- 1 | require 'cljs/rails' 2 | -------------------------------------------------------------------------------- /lib/cljs/rails.rb: -------------------------------------------------------------------------------- 1 | require "cljs/rails/version" 2 | require 'cljs/railtie' if defined? ::Rails::Railtie 3 | 4 | -------------------------------------------------------------------------------- /lib/cljs/rails/helper.rb: -------------------------------------------------------------------------------- 1 | require 'action_view' 2 | 3 | module Cljs 4 | module Rails 5 | module Helper 6 | def cljs_main_path 7 | if ::Rails.env.production? 8 | cljs_main_prod_path 9 | else 10 | cljs_main_dev_path 11 | end 12 | end 13 | 14 | def cljs_main_prod_path 15 | # ``app/assets/cljs-build`` has to be added to the asset paths 16 | main = ::Rails.configuration.cljs.main_target 17 | "#{main}.js" 18 | end 19 | 20 | def cljs_main_dev_path 21 | port = ::Rails.configuration.cljs.dev_server.port 22 | protocol = ::Rails.configuration.cljs.dev_server.https ? 'https' : 'http' 23 | main = ::Rails.configuration.cljs.main_target 24 | 25 | host = ::Rails.configuration.cljs.dev_server.host 26 | host = instance_eval(&host) if host.respond_to?(:call) 27 | 28 | "#{protocol}://#{host}:#{port}/#{main}.js" 29 | end 30 | 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/cljs/rails/version.rb: -------------------------------------------------------------------------------- 1 | module Cljs 2 | module Rails 3 | VERSION = "0.1.0" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/cljs/railtie.rb: -------------------------------------------------------------------------------- 1 | require 'rails' 2 | require 'rails/railtie' 3 | require 'cljs/rails/helper' 4 | 5 | module Cljs 6 | # :nodoc: 7 | class Railtie < ::Rails::Railtie 8 | config.after_initialize do 9 | ActiveSupport.on_load(:action_view) do 10 | include Cljs::Rails::Helper 11 | end 12 | end 13 | 14 | config.cljs = ActiveSupport::OrderedOptions.new 15 | config.cljs.dev_server = ActiveSupport::OrderedOptions.new 16 | 17 | config.cljs.production_build_task = "prod" 18 | config.cljs.dev_server.port = 5555 19 | config.cljs.dev_server.host = 'localhost' 20 | config.cljs.main_target = 'main' 21 | 22 | rake_tasks do 23 | load "tasks/cljs.rake" 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/generators/cljs_rails/install_generator.rb: -------------------------------------------------------------------------------- 1 | module CljsRails 2 | # :nodoc: 3 | class InstallGenerator < ::Rails::Generators::Base 4 | source_root File.expand_path("../../templates", __FILE__) 5 | 6 | desc "Install everything you need for a basic cljs-rails integration" 7 | 8 | argument :app_name, 9 | type: :string, 10 | default: ::Rails.application.class.parent_name.downcase, 11 | banner: "app_name" 12 | 13 | 14 | def add_foreman_to_gemfile 15 | gem 'foreman' 16 | end 17 | 18 | def copy_procfile 19 | copy_file "Procfile", "Procfile" 20 | end 21 | 22 | def copy_build_boot 23 | template("build.boot", "build.boot") 24 | end 25 | 26 | def copy_boot_properties 27 | copy_file "boot.properties", "boot.properties" 28 | end 29 | 30 | def create_core_cljs 31 | empty_directory "cljs/src/#{app_name}" 32 | template("core.cljs", "cljs/src/#{app_name}/core.cljs") 33 | end 34 | 35 | def create_main_edn 36 | template("main.cljs.edn", "cljs/src/main.cljs.edn") 37 | end 38 | 39 | def add_to_gitignore 40 | append_to_file ".gitignore" do 41 | <<-EOF.strip_heredoc 42 | # Added by cljs-rails 43 | /app/assets/cljs-build 44 | .nrepl-port 45 | EOF 46 | end 47 | end 48 | 49 | def whats_next 50 | puts <<-EOF.strip_heredoc 51 | 52 | We've set up the basics of clojurescript-rails for you, but you'll still 53 | need to: 54 | 55 | 1. Add the 'main' entry point into your layout, and 56 | 2. Run 'foreman start' to run the boot build and rails server 57 | 58 | See the README.md for this gem at 59 | https://github.com/bogdan-dumitru/cljs-rails/blob/master/README.md 60 | for more info. 61 | 62 | Have a functional day! 63 | EOF 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/generators/templates/Procfile: -------------------------------------------------------------------------------- 1 | # Run Rails & Webpack concurrently 2 | # Example file from webpack-rails gem 3 | rails: bundle exec rails server 4 | boot: boot dev 5 | -------------------------------------------------------------------------------- /lib/generators/templates/boot.properties: -------------------------------------------------------------------------------- 1 | BOOT_CLOJURE_VERSION=1.8.0 2 | BOOT_VERSION=2.7.1 3 | -------------------------------------------------------------------------------- /lib/generators/templates/build.boot: -------------------------------------------------------------------------------- 1 | (set-env! 2 | :resource-paths #{"cljs/src"} 3 | :dependencies '[[adzerk/boot-cljs "1.7.228-2" :scope "test"] 4 | [adzerk/boot-cljs-repl "0.3.3" :scope "test"] 5 | [adzerk/boot-reload "0.5.0" :scope "test"] 6 | [pandeiro/boot-http "0.7.6" :scope "test"] 7 | [crisptrutski/boot-cljs-test "0.2.2-SNAPSHOT" :scope "test"] 8 | [org.clojure/clojure "1.8.0"] 9 | [org.clojure/clojurescript "1.9.89"] 10 | [com.cemerick/piggieback "0.2.1" :scope "test"] 11 | [weasel "0.7.0" :scope "test"] 12 | [org.clojure/tools.nrepl "0.2.12" :scope "test"] 13 | [binaryage/devtools "0.8.1"]]) 14 | 15 | (require 16 | '[adzerk.boot-cljs :refer [cljs]] 17 | '[adzerk.boot-cljs-repl :refer [cljs-repl start-repl]] 18 | '[adzerk.boot-reload :refer [reload]] 19 | '[crisptrutski.boot-cljs-test :refer [exit! test-cljs]] 20 | '[pandeiro.boot-http :refer [serve]]) 21 | 22 | (deftask dev [] 23 | (comp (serve :port 5555) 24 | (watch) 25 | (speak) 26 | (reload :asset-host "http://localhost:5555" :on-jsload '<%= app_name %>.core/main) 27 | (cljs-repl) 28 | (cljs :source-map true :optimizations :none))) 29 | 30 | (deftask prod [] 31 | (comp (cljs :optimizations :advanced) 32 | (target :dir #{"app/assets/cljs-build"}))) 33 | -------------------------------------------------------------------------------- /lib/generators/templates/core.cljs: -------------------------------------------------------------------------------- 1 | (ns <%= app_name %>.core 2 | (:require [devtools.core :as devtools] 3 | [clojure.browser.dom :as dom])) 4 | 5 | ;; -- Debugging aids ---------------------------------------------------------- 6 | (devtools/install!) ;; we love https://github.com/binaryage/cljs-devtools 7 | (enable-console-print!) ;; so that println writes to `console.log` 8 | 9 | (defn ^:export main 10 | [] 11 | (dom/append (.-body js/document) (dom/element "div" "Hello world edit me now!"))) 12 | -------------------------------------------------------------------------------- /lib/generators/templates/main.cljs.edn: -------------------------------------------------------------------------------- 1 | {:require [<%= app_name %>.core] 2 | :init-fns [<%= app_name %>.core/main] 3 | :compiler-options {:asset-path "http://localhost:5555/main.out"}} 4 | -------------------------------------------------------------------------------- /lib/tasks/cljs.rake: -------------------------------------------------------------------------------- 1 | namespace :cljs do 2 | desc "Compile clojurescript for production" 3 | task compile: :environment do 4 | production_build_task = Rails.configuration.cljs.production_build_task 5 | 6 | run_build = %{ 7 | type boot >/dev/null 2>&1 || 8 | { echo >&2 "[ERROR] Boot is not installed."; exit 1; } 9 | boot #{production_build_task} 10 | } 11 | 12 | sh run_build, verbose: false 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'cljs_main_path' do 4 | include Cljs::Rails::Helper 5 | before(:each) do 6 | # Reset configuration 7 | ::Rails.configuration.cljs.main_target = "main" 8 | ::Rails.configuration.cljs.dev_server.port = 5555 9 | ::Rails.configuration.cljs.dev_server.host = 'localhost' 10 | end 11 | 12 | it "should use https protocol when https is true" do 13 | ::Rails.configuration.cljs.dev_server.https = true 14 | expect(cljs_main_path).to be_starts_with('https:') 15 | end 16 | 17 | it "should use http protocol when https is false" do 18 | ::Rails.configuration.cljs.dev_server.https = false 19 | expect(cljs_main_path).to be_starts_with('http:') 20 | end 21 | 22 | context "when in development" do 23 | before do 24 | allow(Rails.env).to receive(:production?) { false } 25 | allow(Rails.env).to receive(:development?) { true } 26 | end 27 | 28 | it "should have the user talk to the dev server" do 29 | ::Rails.configuration.cljs.dev_server.port = 4000 30 | ::Rails.configuration.cljs.dev_server.host = 'boot-dev-server.host' 31 | 32 | expect(cljs_main_path).to eq("http://boot-dev-server.host:4000/main.js") 33 | end 34 | end 35 | 36 | context "when in prouction" do 37 | before do 38 | allow(Rails.env).to receive(:production?) { true } 39 | allow(Rails.env).to receive(:development?) { false } 40 | end 41 | 42 | it "should have the user talk to the dev server" do 43 | ::Rails.configuration.cljs.dev_server.port = 4000 44 | ::Rails.configuration.cljs.dev_server.host = 'boot-dev-server.host' 45 | 46 | expect(cljs_main_path).to eq("main.js") 47 | end 48 | end 49 | 50 | it "should use the main target configuration if specified" do 51 | ::Rails.configuration.cljs.main_target = "apples" 52 | expect(cljs_main_path).to be_ends_with("apples.js") 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require "rspec" 2 | require "rails" 3 | require "cljs/rails" 4 | 5 | module Dummy 6 | # :nodoc: 7 | class Application < Rails::Application 8 | config.eager_load = false 9 | end 10 | end 11 | 12 | Rails.application.initialize! 13 | 14 | # Load support files 15 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } 16 | --------------------------------------------------------------------------------