# Padrino Framework - Complete Documentation Generated from https://github.com/padrino/padrino-docs Version: 0.16.1 --- # Introduction: Overview # Guides When getting started with Sinatra or Padrino for the first time, we recommend that you check out the [Why Learn Padrino?](/guides/introduction/why-learn-padrino "Why Learn Padrino?") guide which provides an overview of the rest of our resources. Also be sure to check out the [Blog Tutorial](/guides/getting-started/blog-tutorial "Blog Tutorial") for a step-by-step walkthrough of building your first Padrino project. Padrino consists of multiple modules which enhance Sinatra in different ways. The major components are described in detail below: - [Why Learn Padrino?](/guides/introduction/why-learn-padrino "Why Learn Padrino?") - [Installation](/guides/getting-started/installation "Installation") - [Generators](/guides/generators/overview "Generators") - [Application Helpers](/guides/application-helpers/overview "Application Helpers") - [Controllers and Routing](/guides/controllers/overview "Controllers and Routing") - [Development and Terminal Commands](/guides/features/development-commands "Development and Terminal Commands") - [Mounting Sub-applications](/guides/features/mounting-applications "Mounting Sub-applications") - [Delivering Mail](/guides/features/padrino-mailer "Delivering Mail") - [Admin and Authentication](/guides/features/padrino-admin "Admin and Authentication") - [Site Caching](/guides/features/padrino-cache "Site Caching") Note that as a user of Padrino, each of the major components can be used [standalone](/guides/advanced-usage/standalone-usage-in-sinatra "standalone") in an existing Sinatra application or used together for a full-stack Padrino project. --- # Introduction: Why Learn Padrino? # Why Learn Padrino? This guide will give an overview of the various other guides, resources and steps towards becoming a Sinatra + Padrino aficionado. -------------------------------------------------------------------------------- ## Advantages Let's take a second to briefly enumerate three major advantages of learning Padrino: ### Easy to Learn The most interesting aspect of the Padrino stack (Rack, Sinatra, et. al.) in comparison to other web development tools is how modular and standalone each individual piece of the stack is. This makes learning each part much easier, and allows people to be productive right away, organically building up their knowledge of different aspects as they become necessary within a system. This concept of genuine graduated complexity within a web development stack is relatively unique to Sinatra and Padrino in the Ruby web world. ### Fast Sinatra and Padrino are **very fast** relative to other full stack frameworks. The Padrino stack is lightweight and slim which can be demonstrated in our [performance benchmarks](https://github.com/DAddYE/web-frameworks-benchmark/wiki "performance benchmarks"). While all benchmarks should be taken with a grain of salt, over the course of developing hundreds of applications, we have found that the memory usage, stability and requests per second speak for themselves. ### Extensible The other benefit of Sinatra and Padrino is the rich ecosystem of extensions that can be applied at any level of the stack. For Rack, there is a [wealth of middlewares](https://github.com/rack/rack/wiki/List-of-Middleware "wealth of middlewares") that can help do almost anything. For Sinatra, there is also a [large base](http://www.sinatrarb.com/extensions-wild.html "large base") of extensions to add most any functionality you might need. Best of all, these are all 100% compatible with Padrino, and using our [Recipe Box](http://github.com/padrino/padrino-recipes "Recipe Box") and our use of [Bundler](http://bundler.io "Bundler"), you can enable nearly any library or functionality you will need with a single command. -------------------------------------------------------------------------------- ## Getting to Know Ruby A detailed overview of Ruby is beyond the scope of our documentation, but this guide is intended to point you in the right direction and get you familiarized with the important terms. The Padrino web framework is written in the [Ruby programming language](http://www.ruby-lang.org/en/ "Ruby programming language"). The Padrino codebase is a set of modular libraries for Ruby which are packaged using [RubyGems](https://rubygems.org/ "RubyGems"). In order to use Ruby and Padrino, you need to [install the Ruby interpreter](http://www.ruby-lang.org/en/downloads "install the Ruby interpreter") onto your local machine and setup [RubyGems](https://rubygems.org/pages/download "RubyGems") so you can install ruby packages. You should now be able to execute the following commands in the terminal: ```shell $ ruby -v $ gem -v ``` These should return with version numbers and no errors if everything is installed correctly. Once you have Ruby and RubyGems installed locally, you should become familiar with the Ruby syntax. We recommend a few resources below to get yourself familiar with Ruby: - [TryRuby](http://tryruby.org/levels/1/challenges/0 "TryRuby") – This is an interactive tutorial that takes you step by step through learning Ruby. This is highly recommended. Visit the site and type "help" to get started. - [Learn to Program](https://pine.fm/LearnToProgram "Learn to Program") by Chris Pines – Excellent first Ruby tutorial, straightforward and excellent overview of the language. - [Learn Ruby the Hard Way](http://learnrubythehardway.org/book/ "Learn Ruby the Hard Way") – Newest addition to the group, based off of Zed's excellent Python tutorial. Set of exercises that teaches Ruby to you in a rigorous but simple approach. - [Why's Poignant Guide](http://poignant.guide/book/chapter-1.html "Why's Poignant Guide") - Definitely the most unorthodox way to learn Ruby, but must be mentioned. Through these guides, learning Ruby the language should be fairly painless and hopefully you will come to appreciate the elegance and simplicity of the syntax. If you enjoy Ruby and want to continue, the next step is to get familiar with Sinatra, the Ruby DSL for the web. -------------------------------------------------------------------------------- ## Learning to Love Sinatra Padrino is a framework which builds on the existing functionality of the [Sinatra](http://sinatrarb.com "Sinatra") Ruby web DSL and provides a variety of additional tools and helpers to extend this foundation. To use Padrino, one should be familiar with the basic usage of Sinatra itself. First, let's install Sinatra through RubyGems: ```shell $ gem install sinatra ``` Thankfully, Sinatra is probably the easiest to learn tool for making web apps you have yet encountered. Here is an example of a Sinatra application: ```ruby # app.rb require 'sinatra' get '/hi' do "Hello World!" end ``` and then you can start the application with this in your terminal: ```shell $ ruby -rubygems app.rb == Sinatra has taken the stage ... >> Listening on 0.0.0.0:4567 ``` and then visit in your web browser. It really is that easy, but there's a lot more to learn! Resources for Sinatra are listed below: - [Sinatra Introduction](http://www.sinatrarb.com/intro.html "Sinatra Introduction") - [Sinatra Book](https://github.com/sinatra/sinatra-book "Sinatra Book") - [Sinatra Github Repo](https://github.com/sinatra/sinatra "Sinatra Github Repo") There are several good Sinatra tutorials as well: - [Just Do It, Learn Sinatra](http://www.sitepoint.com/just-do-it-learn-sinatra-i/ "Just Do It, Learn Sinatra") – Great step by step 3 part tutorial on Sinatra. - [Singing with Sinatra](http://code.tutsplus.com/tutorials/singing-with-sinatra--net-18965 "Singing with Sinatra") – Excellent beginners guide to learning Sinatra. - [Sinatra Usage Examples](http://blog.maxaller.name/2010/01/a-brief-introduction-to-ruby-sinatra-and-haml "Sinatra Usage Examples") – Great set of examples for how to use Sinatra in code snippets. Read through these tutorials to understand routes, helpers, and the request/response cycle that is exposed through Sinatra. The most comprehensive resource is probably the [Sinatra Book](https://github.com/sinatra/sinatra-book "Sinatra Book") so make sure to skim through that and familiarize yourself with the concepts before continuing to Padrino. -------------------------------------------------------------------------------- ## Scalable Sinatra using Padrino Once you have a solid understanding of Sinatra then you can also already understand the simplest functionality of Padrino. This is because Padrino acts as a super-set of Sinatra. First, check out the [Installation](/guides/getting-started/installation "Installation") guide to get Padrino setup on your computer through RubyGems. Padrino itself is a set of modular extensions for Sinatra. These extensions are actually fairly standalone and you can use many Padrino modules in your existing Sinatra apps through the [Standalone Usage](/guides/advanced-usage/standalone-usage-in-sinatra "Standalone Usage") guide. This usage is intended for applications that have already been built that would benefit from a particular aspect of Padrino such as a mailer or view helpers. Check out [our API docs](http://www.rubydoc.info/github/padrino/padrino-framework "our API docs") for more information about the individual modules. If you are able to convert your project to Padrino or start a new application from scratch, we recommend using the full Padrino stack which makes starting a new project much easier. The best way to become familiar with Padrino is to first check out the [Blog Tutorial](/guides/getting-started/blog-tutorial "Blog Tutorial") which takes you step by step through creating a blog in Padrino with an accompanying screencast. You may also want to checkout the [Why Padrino (broken)](http://www.padrinorb.com/pages/why "Why Padrino") guide to understand the benefits of using Padrino. You should also take time to familiarize yourself with the various "components" Padrino allows you to use for your application. To make things simple, if you are new to the Ruby community and want to create a Padrino application with a good default set of components, generate your project with this command: ```shell $ padrino g project my_project -d datamapper -t shoulda -s jquery -e haml -m mocha ``` And then read up on these components: - [DataMapper](http://datamapper.org/docs/ "DataMapper") – Great Object Relational Mapper for interacting with data - [Haml](http://haml.info/ "Haml") – Solid templating choice for views - [jQuery](http://jquery.com/ "jQuery") – Excellent javascript framework for frontend development - [Mocha](http://www.rubydoc.info/github/floehopper/mocha/Mocha/Mock "Mocha") – Popular mocking and stubbing for tests There are also a few important guides that cover the vast majority of Padrino's functionality. These are definitely recommended reading: - [Blog Tutorial](/guides/getting-started/blog-tutorial "Blog Tutorial") – Step by step blog tutorial using Padrino - [Generators](/guides/generators/overview "Generators") – A reference guide for the generator and the various components - [Project Types](/guides/getting-started/basic-projects "Project Types") – Overview of the various project types - [Application Helpers](/guides/application-helpers/overview "Application Helpers") – A reference guide for the view helpers available - [Controllers and Routing](/guides/controllers/overview "Controllers and Routing") – An overview of the enhanced routing system - [Delivering Mail](/guides/features/padrino-mailer "Delivering Mails") – Overview of how to deliver mail in Padrino applications - [Admin and Authentication](/guides/features/padrino-admin "Admin and Authentication") – Admin dashboard and authentication system - [Caching](/guides/features/padrino-cache) – Page and fragment caching system - [Mounting Sub-applications](/guides/features/mounting-applications "Mounting Sub-applications") – Explanation of the "application" mounting process - [Development and Terminal Commands](/guides/features/development-commands "Development and Terminal Commands") – Important notes about development These guides should shed light on the various aspects that make Padrino helpful while developing Sinatra-based applications. --- # Introduction: Examples # Examples Padrino is a relatively new framework, but many coders agree the best way to get familiar with a framework is to see examples of existing applications. Soon, we hope to compile a list of developed apps using Padrino. ## Open-Source Applications Below is the current list of known open-source Padrino applications. Name | Description | In Production | Author ---------------------------------------------- | ------------------- | ------------- | ------------ [padrino-web](https://github.com/padrino/padrino-web) | Open-sourced padrinorb.com source code. | Yes | [Padrino Team](https://github.com/orgs/padrino/teams/collaborators) [middleman](http://middlemanapp.com) | A Static Frontend Development Framework (use Padrino for their template helpers). | Yes | [middleman](https://github.com/middleman/middleman) [hasbeen.in](https://github.com/findoutwho/hasbeen.in) | Your geek-friendly travel site. | Yes | [findoutwho](https://github.com/findoutwho) [getVolunteers](https://github.com/RmMsr/getVolunteers) | Embed an image anywhere to let people know if your event needs more help. | Yes | [RmMsr](https://github.com/RmMsr) | [lumen](https://github.com/wordsandwriting/lumen) | Open-source group discussion platform. | Yes | [wordsandwriting](https://github.com/wordsandwriting) [iTunes Store Transporter: GUI](http://transportergui.com) | GUI for the iTunes Store’s Transporter (iTMSTransporter). | Yes | [sshaw](https://github.com/sshaw) [HOF Studios Website](http://www.hofstudios.com/) | HOF Studios specializes in game development for PC and mobile platforms. | Yes | [HOF Studios](https://github.com/hofstudios) [sochi2014 api](http://olympics.clearlytech.com/api-doc.html) | Unofficial olympics for sochi2014. | Yes, but API is no longer active | [clearytech](https://github.com/clearlytech) [dbd\_notebook](https://github.com/k2052/dbd_notebook) | Source code for notebook.designbreakdown.com (inactive since 2014). | No | [k2052](https://github.com/k2052) [manicminer-front](https://github.com/jorgefuertes/manicminer-front) | Manicminer Multicoin Mining Pool - Front App (inactive since 2014). | No [jorgefuertes](https://github.com/jorgefuertes) [fumblr](https://github.com/pengwynn/fumblr) | Stop fumbling with your Tumblr theme development (inactive since 2013). | No | [pengwynn](https://github.com/pengwynn) [machete](https://github.com/gtgames/machete) | Simple mongomapper+padrino driven web site engine (inactive since 2012). | No | [gtgames](https://github.com/gtgames) [shortener-demo]( https://github.com/padrino/shortener-demo) | Example Padrino url shortener app (inactive since 2011). | No | [achiu](https://github.com/achiu) [haircut](https://github.com/udzura/haircut) | The URL shortener with Padrino and MongoId (inactive since 2011). | No | [udzura](https://github.com/udzura) [moolah](https://github.com/mcmire/moolah) | A tiny money management app (inactive since 2011). | No | [mcmire](https://github.com/mcmire) [picciotto](https://github.com/apeacox/picciotto) | Minimalistic website framework (inactive since 2011). | No | [apeacox](https://github.com/apeacox) [fikas](https://github.com/bratta/fikus) | The Simple Ruby CMS (inactive since 2010). | No | [bratta](https://github.com/bratta) [pergola](https://github.com/ryanfitz/pergola) | Web frontend to mongoDB (inactive since 2011). | No | [ryanfitz](https://github.com/ryanfitz) [presto](https://github.com/pengwynn/presto) | Padrino + NestaCMS (inactive since 2010). | No | [pengwynn](http://wynnnetherland.com/) [shopping-cart](https://github.com/cored/shopping-cart) | The Rails shopping cart demo ported to the Padrino framework (inactive since 2010). | No | [cored](https://github.com/cored) [omerta](https://github.com/padrino/omerta) | Simple blog platform using Mongo Mapper, MongoDB and Padrino (inactive since 2010). | No | [zenom](https://github.com/zenom) [mashup](https://github.com/mwlang/mashup) | Displays various RSS feeds on one page (inactive since 2010). | No | [mwlang](https://github.com/mwlang) [padrino\_questionnaire](https://github.com/pepe/padrino_questionnaire) | Simple questionnaire application (inactive since 2011). | No | [pepe](https://github.com/pepe) [tokyo-project](https://github.com/CaDs/tokyo-project) | A photo gallery showcasing pictures of Tokyo. | Yes | [CaDs](https://github.com/CaDs) [Amethyst](https://github.com/AustinBlues/Amethyst) | Amethyst2 is an RSS/ATOM feed reader Web server inspired by Amphetadisk. | Yes | [AustinBlues](https://github.com/AustinBlues) ## Web Libraries Name | Description | Author --------------------------------------- | ----------------------- | ------------- [padrino-recipes](https://github.com/padrino/padrino-recipes) | Recipes for auto-installing various extra components into Padrino. | [Padrino](https://github.com/padrino) [padrino-contrib](https://github.com/padrino/padrino-contrib) | Extra libraries that are useful for use in Padrino. | [Padrino](https://github.com/padrino) [padrino-warden](https://github.com/jondot/padrino-warden) | Provides authentication for your Padrino application. | [jondot](https://github.com/jondot) [sinatra-simple-navigation](https://github.com/codeplant/sinatra-simple-navigation) | Creates a simple way to do navigation for Padrino and Sinatra. | [codeplant](https://github.com/codeplant) [Sinatra Wild Extensions](http://www.sinatrarb.com/extensions-wild.html) | Bunch of Sinatra which can be used alongside with Padrino. | - [padrino-pagination](https://github.com/sumskyi/padrino-pagination) | Pagination for Padrino (inactive since 2014). | [sumskyi](https://github.com/sumskyi) [rack-recaptcha](https://github.com/achiu/rack-recaptcha) | Rack Middleware for CAPTCHA verification via Recaptcha API | [achiu](https://github.com/achiu) [padrino-fields](https://github.com/activestylus/padrino-fields) | Simple, flexible form helpers for the Padrino Framework (inactive since 2013). | [activestylus](https://github.com/activestylus) [padrino-completion](https://github.com/bolshakov/padrino-completion) | Bash completion for Padrino (inactive since 2012). | [bolshakov](https://github.com/bolshakov) [vim-padrino](https://github.com/spllr/vim-padrino) | Vim support for Padrino (inactive since 2012). | [spllr](https://github.com/spllr) [ripl-padrino](https://github.com/achiu/ripl-padrino) | ripl console for Padrino applications (inactive since 2011). | [achiu](https://github.com/achiu) [padrino-responders](https://github.com/nu7hatch/padrino-responders) | Awesome way to remove redundancy in your Padrino controllers (inactive since 2011). | [nu7hatch](https://github.com/nu7hatch) [view\_models](https://github.com/floere/view_models) | A view model/representer solution for Padrino and Rails (inactive since 2012). | [floere](https://github.com/floere) [declarative\_authorization\_padrino](https://github.com/dariocravero/declarative_authorization_padrino) | Declarative Authorization for Padrino (inactive since 2011). | [dariocravero](https://github.com/dariocravero) [padrino-rpm](https://github.com/Asquera/padrino-rpm) | New Relic RPM for Padrino (inactive since 2011). | [Asquera](https://github.com/Asquera) [padrino-form-errors](https://github.com/nu7hatch/padrino-form-errors) | Simple way to handle errors more robustly (inactive since 2010). | [nu7hatch](https://github.com/nu7hatch) ## Closed-source Applications Name | Description | In Production | Author --------------------------------------- | ----------------------- | ------------- | ------------ [Maptia](http://maptia.com/) | A beautiful way to tell stories about places. | Yes | - [Brainfeed](http://brainfeed.org/) | Back-end for iPad app that presents educational videos for kids | Yes | - [Coca Cola Enterprises](http://www.cokecce.com) | Coca Cola's European bottling arm. Webby award. | Yes | - [jumpseller](http://jumpseller.com) | SaaS to create online stores | Yes | - [FreshBSD](http://freshbsd.org) | FreshBSD is a search engine cataloguing the development of major BSD-associated software | Yes | [Thomas Hurst](http://hur.st/) [Smartmedia](http://www.smartmedia.cz) | Create advanced mobile, facebook and web applications | Yes | - [Otticalisotti](http://www.otticalisotti.com/) | | Yes | - [martianoids.com](http://martianoids.com) | System administration company at Spain. Products and blog. | Yes | [jorgefuertes](https://github.com/jorgefuertes) [demo.biosig.xyz](http://demo.biosig.xyz) | Voice biometric software, where you can enroll and authenticate using voice. | Yes | [jorgefuertes](https://github.com/jorgefuertes) [api.biosig.xyz](http://api.biosig.xyz) | The api for demo.biosig.xyz | Yes | [jorgefuertes](https://github.com/jorgefuertes) [HRPartner](http://www.hrpartner.io/) | The go-to cloud HR software for small & medium-sized businesses. | Yes | [Devan Sabaratnam](https://github.com/CyberFerret) [Logbook HQ](http://www.logbookhq.com/) | An easy and beautiful way to track your mileage, fuel and other car expenses. | Yes | [Devan Sabaratnam](https://github.com/CyberFerret) [StaffStatus](http://www.staffstatus.io/) | Allows staff to maintain their in-out status. | Yes | [Devan Sabaratnam](https://github.com/CyberFerret) Unknown if they still use Padrino, but I asked them: - http://www.idyllic-software.com - http://edenspiekermann.com - http://salin.org - http://www.videofy.me - VideofyMe helps bloggers make money with a slick video platform. - http://www.headlondon.com - London web agency using Padrino as its main dev framework - http://www.clearhaus.com - Acquiring merchant services - https://nofity.com - When notes meets social - http://landmoda.com - Networks for models in the world! - http://martianoids.com - System administration company at Spain. Products and blog. - http://middlemanapp.com - Middleman: A Static Frontend Development Framework --- # Introduction: The Bleeding Edge # The Bleeding Edge You have three ways of using Padrino edge; the first one is using the git source code in a gem file, the second one is using a vendored version, and the third is to install edge into system gems from repository. **Git in Gemfile** is suitable for those that **only** want Padrino's latest bleeding edge code. **System Gems** is suitable for people that want to use the latest padrino and freely use `padrino g` and `padrino`. **Path in Gemfile** is recommended for developers because they can share their changes and merge repos between projects. ## Git in Gemfile ```ruby # Edit Gemfile gem 'padrino', github: 'padrino/padrino-framework' ``` and from console: ```shell $ bundle install ``` after that you need to run your app in the bundler environment because if you call directly: ```shell $ padrino g admin ``` you will use system wide gems. So for do that remember to run commands with `bundle exec` as a prefix like: ```shell $ bundle exec padrino start $ bundle exec padrino g controller foo $ bundle exec padrino g admin $ bundle exec padrino g model post ``` You can find more info about bundler usage on their [site](http://bundler.io/ "Link bundler site"). -------------------------------------------------------------------------------- ## System Gems If you want to install the padrino edge gems into your system rubygems, simply follow the following steps. First, clone the padrino repository: ```shell $ cd /tmp $ git clone git://github.com/padrino/padrino-framework.git ``` Next, we should mark the version as dev(elopment) to get a fresh set of gems: ```ruby # /tmp/padrino-framework/padrino-core/lib/padrino-core/version.rb module Padrino VERSION = '0.16.1' unless defined?(Padrino::VERSION) # Change to bump version #... end ``` Finally, run the `fresh` rake command to install the latest version: ```shell padrino-framework$ rake fresh ~/.rvm/rubies/ruby-3.4.7/bin/ruby -S rake install padrino-support 0.16.1 built to pkg/padrino-support-0.16.1.gem. padrino-support (0.16.1) installed. ~/.rvm/rubies/ruby-3.4.7/bin/ruby -S rake install padrino-core 0.16.1 built to pkg/padrino-core-0.16.1.gem. padrino-core (0.16.1) installed. ... ``` this will install the latest 'edge' gems into rubygems. Be sure to verify your project's Gemfile depends on the edge version you installed: ```ruby Gemfile # Padrino gem 'padrino', '~> 0.16.1' ``` or you can generate a new project easily and you can use padrino commands normally: ```shell $ padrino g project test-project ``` This should allow you to use the latest padrino code from your system. -------------------------------------------------------------------------------- ## Path in Gemfile ```shell $ mkdir /src # Remember to use your fork $ git clone git://github.com/padrino/padrino-framework.git # Edit your ~/.profile or ~/.bash_profile or some and add alias padrino-dev="/src/padrino-framework/padrino-core/bin/padrino" alias padrino-dev-gen="/src/padrino-framework/padrino-gen/bin/padrino-gen" # you can omit this ``` Reload source or open a new terminal window and check if you have _padrino-dev_ and _padrino-dev-gen_ commands correctly added to your path. Create a new padrino project: ```shell padrino-dev-gen project test-project --dev ``` or if you don't have `padrino-dev-gen` ```shell padrino-dev g project test-project --dev ``` This will append the following lines to your test-app/Gemfile: ```ruby # Vendored Padrino %w(core gen helpers mailer admin).each do |gem| gem 'padrino-' + gem, path: '/src/padrino-framework/padrino-' + gem end ``` Make changes accordingly to your _/src/padrino-framework_ to see them reflected through your padrino project now using your own vendored version of the framework. **REMEMBER** to add always `--dev` when generating a project because without that the generated project will use the gem instead the git checkout. --- # Introduction: Press # Press This is just a collection of links (news, blogs, etc) that mention Padrino and not directly authored by the core team: Name | Description | Author ---------------------------------------------- | ------------------- | ------------ [Cerner Tech Talk (Video)](https://www.youtube.com/watch?v=CH_a3yNbYDM) | This talk is part of Cerner's Tech Talk series (2013). | [@CernerEng](https://twitter.com/CernerEng) [O’Reilly Programming](http://radar.oreilly.com/2013/12/how-setting-aside-rails-and-picking-up-padrino-might-make-you-a-better-ruby-developer.html) | How Setting Aside Rails and Picking Up Padrino Might Make You a Better Ruby Developer (2013). | [Aaron Sumner](http://radar.oreilly.com/asumner) [RubyInside](http://www.rubyinside.com/padrino-sinatra-webapp-framework-3198.html) | Padrino: A Webapp Framework Wrapped Around Sinatra (2010). | [Peter Cooper](http://www.rubyinside.com/author/admin) [PuddingBowl](http://mph.puddingbowl.org/2011/04/padrino/) | padrino (2011). | [Michael Hall](http://mph.puddingbowl.org/about/) [JaxEnter](https://jaxenter.com/get-more-features-for-sinatra-100726.html) | Get More Features for Sinatra (2010)! | [Jessica Thornsby ](https://jaxenter.com/author/jessicathornsby) [HackerNews](https://news.ycombinator.com/item?id=1235078) | Padrino Ruby Web Framework (sits on top of Sinatra) - Release notes | [jmonegro](https://news.ycombinator.com/user?id=jmonegro) [Ruby5](https://ruby5.codeschool.com/episodes/64-episode-62-march-26-2010) | Padrino got mentioned in this Podcast (2010). | [Paul Elliott](http://uncle.ninja/) [coryodaniel](http://coryodaniel.com/index.php/tag/padrino/) | Some blog posts tackling problems with compass and sass (2010). | [Cory O'Daniel](http://coryodaniel.com/) [Ramaze vs. Padrino Benchmarks](http://codeconnoisseur.org/ramblings/ramaze-vs-padrino-benchmarks) | Ramaze vs. Padrino Benchmarks (2010). | [Michael Lang](http://codeconnoisseur.org/) [programmingzen](http://programmingzen.com/2010/06/11/padrino-a-ruby-framework-built-upon-sinatra/) | Padrino: a Ruby framework built upon Sinatra (2010). | [Antonio Cangiano](http://programmingzen.com/about/) [changelog podcast](https://changelog.com/?s=padrino) | Several podcasts mentioning Padrino. | [changelog](https://changelog.com/about/) [Tropical Software Observation](http://tech.favoritemedium.com/2010/08/initial-review-on-padrino-fast-ruby-web.html) | Initial Thoughts on Padrino, a Fast Ruby Web Framework Based On Sinatra (2010) | [ Isak Rickyanto]() [trevmex](http://trevmex.com/post/934878009/padrino-and-sequel-for-lightweight-web-apps) | Padrino and Sequel for lightweight web apps (2010). | [trevmex](http://trevmex.com/) [Fikus](https://blog.engineyard.com/2010/fikus-deploying-padrino-to-engine-yard-appcloud) | Fikus: Deploying Padrino to Engine Yard AppCloud (2010). | [Tim Gourley](https://blog.engineyard.com/authors/Tim%20Gourley) [halfdecent](http://halfdecent.net/2010/08/20/installing-padrino-on-ubuntu-debian/) | Installing Padrino on Ubuntu / Debian (2010). | [Matt South](http://halfdecent.net/about/) [ruby-ua](http://ruby-ua.blogspot.de/2010/04/meet-padrino-part-1.html) | Russian page talking about Padrino (2010). | - [habrahabr](https://habrahabr.ru/post/94911/) | Russian page talking about Padrino (2010). | [Konstantin Shabanov](https://habrahabr.ru/users/Aesthete/) [cyberwave](http://cyberwave.jp/nashiki/2010/06/rails-%EF%BC%8B-sinatra-%E2%89%92-padrino-%E3%81%A7%E9%81%8A%E3%81%BC%E3%81%86%EF%BC%81/) | Japanese site mentioning Padrino (2010). | [nashik](http://cyberwave.jp/nashiki/) --- # Getting Started: Overview # Overview This is a guide intended for a developer that is just getting started with the Padrino web framework (and perhaps Sinatra or Ruby). This guide will give an overview the various other guides, resources and steps towards becoming a Sinatra + Padrino aficionado. You may want to skim through sections you already are familiar with. If you are already familiar with Padrino and just want to access reference materials, you may want to jump to [our api docs](http://www.rubydoc.info/github/padrino/padrino-framework "our api docs") or the [guides homepage](/guides/ "guides homepage"). - [Why Learn Padrino?](/guides/introduction/why-learn-padrino "Why Learn Padrino?") - [Installation](/guides/getting-started/installation "Installation") - [Blog Tutorial](/guides/getting-started/blog-tutorial "Blog Tutorial") --- # Getting Started: Basic Projects # Basic Projects Be sure to read the [Installation](/guides/getting-started/installation "Installation") instructions first. You might also want to check out the [Why Learn Padrino?](/guides/introduction/why-learn-padrino "Why Learn Padrino?") guide for a better understanding of Sinatra and Padrino if you are new to the stack. -------------------------------------------------------------------------------- ## Generating a Project To generate a new Padrino project using its defaults (RSpec for testing and Haml for rendering) and no database adapter, simply invoke the following command: ```shell $ padrino g project my_project ``` Padrino has also built-in support for several different mocking, testing, rendering, ORM, and JavaScript components. ```shell $ padrino g project custom_project -t rspec -d activerecord -s jquery ``` For a breakdown of all the available components options please refer to the [Generators](/guides/generators/overview "Generators") page. ### Persistence Engine Whenever you are creating a new project, Padrino will assume by default that a database is not required for your project. To add support for a persistence engine, specify a supported ORM of your choice to use by flagging the `padrino g` command with the `-d` option followed by the name of your ORM: ```shell $ padrino g project your_project -d mongoid $ padrino g project your_project -d activerecord $ padrino g project your_project -d datamapper $ padrino g project your_project -d couchrest $ padrino g project your_project -d mongomatic $ padrino g project your_project -d ohm $ padrino g project your_project -d ripple $ padrino g project your_project -d sequel $ padrino g project your_project -d dynamoid ``` For the SQL-based persistence engines, you can even specify the RDBMS adapter to use with the `-a` option followed by the name of the adapter: ```shell $ padrino g project your_project -d datamapper -a mysql # Uses Datamapper and MySQL $ padrino g project your_project -d activerecord -a postgres # Uses ActiveRecord and Postgres $ padrino g project your_project -d sequel -a sqlite # Uses Sequel and Sqlite3 ``` The adapters currently supported are `sqlite`, `mysql`, and `postgres` for use with `datamapper`, `activerecord`, or `sequel`. -------------------------------------------------------------------------------- ## Generating Applications Padrino's main concept is to generate a default "project" or "core application": ```shell $ padrino g project my_project ``` You can then add, if needed, sub-applications to your existing Padrino "project": ```shell $ cd my_project $ padrino g app gallery ``` You can also generate your own controllers, mailers, models, etc. for your "gallery" app as well. ```shell $ padrino g controller sample get:index --app gallery ``` Whenever generating a "mounted" app, Padrino will mount that application automatically. As a reference, the above example "gallery" application will be mounted to: `/gallery`. You can easily change and configure your "mounted" application path and decide where your applications will be mounted, by editing your `config/apps.rb` file. -------------------------------------------------------------------------------- ## Generating the Admin Section Let's start by creating a new Padrino project using Active Record: ```shell $ padrino g project blog -d activerecord ``` Install all project dependencies: ```shell $ cd blog $ bundle # if you haven't that command, run 'gem install bundler' ``` Padrino ships with a beautiful Admin interface. Remember that Padrino has been principally structured and designed for mounting multiple applications at the same time. Under this perspective, our **admin** section is nothing but a new Padrino **application**: ```shell $ padrino g admin -e slim ``` Beside slim, you can also use `erb` or `haml`. You need to configure your database settings in `config/database.rb` and run your migrations to add tables and columns to your database: ```shell $ bundle exec rake db:create $ bundle exec rake db:migrate ``` Create your first admin account; this is easily achieved by seeding your database with default admin data, stored in your `seed.rb` file: ```shell $ bundle exec rake db:seed ``` You will see this in your terminal: ```shell Which email do you want use to log into admin? info@padrino.local Tell me the password to use: foobar ================================================================= Account has been successfully created, now you can login with: ================================================================= email: info@padrino.local password: ****** ================================================================= ``` You are now ready to start your webserver: ```shell $ padrino start ``` Point your browser to `http://localhost:3000/admin` and log in by using the email and password provided while seeding your database: -------------------------------------------------------------------------------- ## Adding a model Let's add a new `Post` model to our blog: ```shell $ padrino g model post name:string body:text ``` Run the migrations to add database table columns to our database for our newly created Post model: ```shell $ bundle exec rake db:migrate ``` Create a new admin section for managing (creating, updating, deleting) our blog posts: ```shell $ padrino g admin_page post ``` That's all! Start your webserver and begin adding some posts. --- # Getting Started: Installation # Installation In order to use Padrino, you need a few prerequisite libraries. -------------------------------------------------------------------------------- ## Ruby & RubyGems First, you need to have a ruby interpreter installed. You can verify if you have the interpreter installed by typing `which ruby` into the terminal and ensuring a result. If not, we recommend installing [RVM](https://rvm.io/ "RVM") on most platforms which manages your ruby interpreter installation and configuration. Once RVM is installed, be sure to install Ruby 3.2 or newer (recommended, minimum 2.7.8) or an alternative runtime such as [JRuby](http://jruby.org/ "JRuby") according to your needs. Once you have ruby, you need to make sure you have [RubyGems](https://rubygems.org "RubyGems") which is the standard Ruby package management method. You can ensure you have ruby gems by typing `gem -v` in the terminal and ensuring a version result. If not, then be sure to [download and install](https://rubygems.org/pages/download "download and install") before continuing. ### Windows A caveat: If you are on Windows, we recommend the [RubyInstaller](http://rubyinstaller.org "RubyInstaller") project which takes care of the above steps for you. -------------------------------------------------------------------------------- ## Padrino Once you have ruby and rubygems installed properly, you just need to install the Padrino framework with the `padrino` gem: ```shell $ gem install padrino ``` This will install all the necessary padrino dependencies to get you started creating applications. Now you are ready to use this gem to [enhance your Sinatra projects](/guides/advanced-usage/standalone-usage-in-sinatra "enhance your Sinatra projects") or to create new Padrino applications. If you are new to Ruby or Sinatra, be sure to check out the [Why Learn Padrino?](/guides/introduction/why-learn-padrino "Why Learn Padrino?") guide for more information. --- # Getting Started: Blog Tutorial # Blog Tutorial When reading about a new framework, I often find that the best way to get familiar is to read a brief tutorial on how to develop a simple application. This can quickly give new users a sense of the development flow and processes involved in using a framework. This guide will show new users how to develop a simple blog using the Padrino framework. Along the way, each step will be explained and links will be provided to further information on different relevant topics. -------------------------------------------------------------------------------- ## Screencast There is also a screencast available for this tutorial. You can check it out by:

Blog Tutorial from PadrinoCasts on Vimeo.

Please note that previous screencast, written for Padrino 0.12.2, is available under . -------------------------------------------------------------------------------- ## Study Guide To skip this tutorial or immediately see the complete blog tutorial project, you can checkout the [blog tutorial repository](https://github.com/padrino/blog-tutorial "blog tutorial repository") using git: ```shell $ git clone git@github.com:padrino/blog-tutorial.git ``` -------------------------------------------------------------------------------- ## Installation In order to develop a Padrino application, we have to do a few things. First, we must obviously have [ruby](http://www.ruby-lang.org/en/ "ruby") (at least version 2.7.8 or later) and [rubygems](https://rubygems.org/ "rubygems") installed. Next, we must install the padrino framework gems: ```shell $ gem install padrino ``` For more details on installation, check out the [installation guide](/guides/getting-started/installation "installation guide"). Now we can begin developing our sample blog. -------------------------------------------------------------------------------- ## Project Generation To create a Padrino application, the best place to start is using the convenient Padrino generator. Similar to Rails, Padrino has a project generator which will create a skeleton application with all the files you need to being development of your new idea. Padrino is an agnostic framework and supports using a variety of different template, testing, JavaScript and database components. You can learn more by reading the [generators guide](/guides/generators/overview "generators guide"). For this sample application, we will use the Sequel ORM, the Haml templating language, the RSpec testing framework and the jQuery JavaScript library. With that in mind, let us generate our new project: ```shell $ padrino g project blog-tutorial -t rspec -e haml -c scss -s jquery -d sequel -b ``` This command will generate our basic Padrino project and the print out a nice report of the files generated. The output of this generation command can be viewed in [this gist](https://gist.github.com/wikimatze/0f8b63d28bccac84014f8a592f79197d "gist for initial project generation") file. Notice the `-b` flag in the previous command which automatically instructs bundler to install all dependencies. All we need to do now is `cd` into our brand new application. ```shell $ cd blog-tutorial ``` Now, the terminal should be inside the root of our newly generated application with all necessary gem dependencies installed. Let us take a closer look at the particularly important generated files before we continue on with development. - `Gemfile` – Includes any necessary gem dependencies for your app. - `app/app.rb` – The primary configuration file for your app. - `config/apps.rb` – This defines which applications are mounted in your project. - `config/database.rb` – This defines the connection details for your chosen database adapter. The following important directories are also generated: - `app/controllers` – This is where the Padrino route definitions should be defined. - `app/helpers` – This is where helper methods should be defined for your application. - `app/views` – This should contain your template views to be rendered in a controller. - `lib` – This should contain any extensions, libraries or other code to be used in your project. - `public` – This is where images, style sheets and JavaScript files should be stored. - `spec` – This is where your model and controller tests are stored. For now, the defaults for the database connection settings (`config/database.rb`) are fine for this tutorial. This environment can be configured in `config/apps.rb` as: ```ruby Padrino.configure_apps do if RACK_ENV == 'production' disable :reload disable :reload_templates else enable :reload enable :reload_templates end end ``` or can be configured in `app/app.rb` as ```ruby if Padrino.env == :production # do production else # non production here end ``` Let us also setup a few simple routes in our application to demonstrate the Padrino routing system. Let's go into the `app/app.rb` file and enter the following routes: ```ruby # app/app.rb module BlogTutorial class App < Padrino::Application register ScssInitializer register Padrino::Mailer register Padrino::Helpers enable :sessions # Here are the defined routes get "/" do 'Hello World!' end get :about, map: '/about-us' do render :haml, '%p This is a sample blog created to demonstrate how Padrino works!' end end end ``` Note that the first route here sets up a simple string to be returned at the root URL of the application. The second route defines a one-line `about` page inline using Haml which is then explicitly mapped to the `/about-us` URL. The symbol `:about` is used to reference the route later. Be sure to check out the [controllers guide](/guides/controllers/overview "controllers guide") for a comprehensive overview of the routing system. -------------------------------------------------------------------------------- ## Admin Dashboard Setup Next, this is a good time to setup the Padrino admin panel which allows us to easily view, search and modify data for a project. Let's go back to the console and enter: ```shell $ padrino g admin ``` This will create the admin sub-application within your project and mount it within the `config/apps.rb` file. The output of this command can be viewed in [this gist](https://gist.github.com/wikimatze/2a325cb7d019371a5403d7420cdf2458 "gist for admin generation output") file. Now, you should follow the instructions of the output: ```sh 1) Run 'bundle' 2) Run 'bundle exec rake db:create' if you have not created a database yet 3) Run 'bundle exec rake db:migrate' 4) Run 'bundle exec rake db:seed' 5) Visit the admin panel in the browser at '/admin' ``` During this process, you will be prompted to enter an email and password to use for the admin dashboard. Be sure to remember this for use later in development. To read more about the features of the admin panel, check out the [Admin Panel Guide](/guides/features/padrino-admin "Admin Panel Guide"). -------------------------------------------------------------------------------- ## Booting Padrino Now the Padrino project has been generated, the database has been configured and created and the admin panel has been properly setup. We can now start up our Padrino application server. This is quite easy to do with the built-in Padrino tasks. Simply execute the following in the terminal: ```shell $ padrino s ``` You should see no errors, and the terminal should output: ```shell => Padrino/0.16.1 has taken the stage development at http://127.0.0.1:3000 [2025-11-21 01:02:13] INFO WEBrick 1.9.1 [2025-11-21 01:02:13] INFO ruby 3.4.6 (2025-09-16) [x86_64-linux] [2025-11-21 01:02:13] INFO WEBrick::HTTPServer#start: pid=3489 port=3000 ``` To read more about available terminal commands, checkout the [Development and Terminal Commands](/guides/features/development-commands "Development and Terminal Commands") guide. Your application now exists on . Visit this URL in the browser and you should see the `Hello World!`. We can also visit the admin panel by going to the URL: and then log in using the admin credentials specified during the `rake db:seed` command performed earlier. Feel free to explore this area and checkout the existing accounts. We will come back to this in more detail later. To read more about the features of the admin panel, check out the [Admin Panel Guide](/guides/features/padrino-admin "Admin Panel Guide"). Worth noting here is that Padrino has full support for code reloading in development mode. This means you can keep the Padrino server running and change your code source and when you refresh in the browser, the changes will be automatically displayed. -------------------------------------------------------------------------------- ## Creating Posts Now that the application is ready and the layouts have been defined, let's implement the functionality to view our blog posts and even add the ability to create new posts! Let's start off by generating the model into our app directory. The models will be generated at the top level `models` directory in a project. If you want to place your models to another location, you can append the `-a` option to the command - this is handy if you would like to have models which should be coped only to sub-apps. ```shell $ padrino g model post title:string body:text created_at:datetime apply orms/sequel apply tests/rspec create models/post.rb create spec/models/post_spec.rb create db/migrate/002_create_posts.rb ``` Go ahead and migrate the database now. ```shell $ padrino rake sq:migrate => Executing Rake sq:migrate ... INFO - (0.000163s) PRAGMA foreign_keys = 1 INFO - (0.000022s) PRAGMA case_sensitive_like = 1 INFO - (0.000107s) SELECT sqlite_version() INFO - (0.000061s) CREATE TABLE IF NOT EXISTS `schema_info` (`version` integer DEFAULT (0) NOT NULL) INFO - (0.000103s) SELECT * FROM `schema_info` LIMIT 1 INFO - (0.000068s) SELECT 1 AS 'one' FROM `schema_info` LIMIT 1 INFO - (0.000062s) SELECT count(*) AS 'count' FROM `schema_info` LIMIT 1 INFO - (0.000070s) SELECT `version` FROM `schema_info` LIMIT 1 INFO - Begin applying migration version 2, direction: up INFO - (0.011094s) CREATE TABLE `posts` (`id` integer NOT NULL PRIMARY KEY AUTOINCREMENT, `title` varchar(255), `body` Text, `created_at` timestamp) INFO - (0.014224s) UPDATE `schema_info` SET `version` = 2 INFO - Finished applying migration version 2, direction: up, took 0.025778 seconds <= sq:migrate:up executed ``` Next, let's create the controller to allow the basic viewing functionality. ```shell $ padrino g controller posts get:index get:show create app/controllers/posts.rb create app/views/posts apply tests/rspec create spec/app/controllers/posts_controller_spec.rb create app/helpers/posts_helper.rb apply tests/rspec create spec/app/helpers/posts_helper_spec.rb ``` We'll want to attached some of the standard routes (`:index` and `:show`) to the controller. ```ruby # app/controllers/posts.rb BlogTutorial::App.controllers :posts do get :index do @posts = Post.reverse_order(:created_at).all render 'posts/index' end get :show, with: :id do @post = Post[params[:id]] render 'posts/show' end end ``` This controller is defining routes that can be accessed via our application. The "http method" `get` starts off the declaration followed by a symbol representing the "action". Inside a block we store an instance variable fetching the necessary objects and then render a view template. This should look familiar to those coming from Rails or Sinatra. Next, we'll want to create the views for the two controller actions we defined: `index` and `show`. ```haml -# app/views/posts/index.haml - @title = "Welcome" #posts= partial 'posts/post', collection: @posts -# app/views/posts/_post.haml .column.is-7.is-offset-2 .card.article .card-content .media .media-content.has-text-centered %p.title.article-title %h3.has-text-centered = link_to post.title, url_for(:posts, :show, id: post.id) .has-addons %span.tag.is-rounded= time_ago_in_words(post.created_at || Time.now) + ' ago' .content.article-body = simple_format(post.body) -# app/views/posts/show.haml %section.articles .column.is-8.is-offset-2 .card.article .card-content .media .media-content.has-text-centered %h3.title.article-title= @post.title .tags.has-addons.level-item %span.tag.is-rounded= time_ago_in_words(@post.created_at || Time.now) + ' ago' .content.article-body = simple_format(@post.body) %p= link_to 'View all posts', url_for(:posts, :index) ``` Padrino Admin makes it easy to create, edit and delete records automatically. To manage posts using Padrino Admin, run this command. ```shell $ padrino g admin_page post create admin/controllers/posts.rb create admin/views/posts/_form.haml create admin/views/posts/edit.haml create admin/views/posts/index.haml create admin/views/posts/new.haml insert admin/app.rb ``` Let's make sure the server is running (`padrino s`) and give this admin interface a try. Visit and login using the credentials you had setup during the seed. There should now be two tabs, one for **Posts** and one for **Accounts**. Now click on 'Posts'. Padrino Admin allows you to easily create new records by clicking "New". It has a form all ready complete with the fields you had generated prior in the creation of the Post model. **Note:** make sure to use `padrino g admin_page post` **after** the creation of your model and their migration. Now that you have added a few posts through the admin interface, check out and notice that the posts you created now show up in the "index" action! You can see all the routes that we now have defined using the `padrino rake routes` command: ```shell $ padrino rake routes Application: BlogTutorial::Admin URL REQUEST PATH (:sessions, :new) GET /admin/sessions/new (:sessions, :create) POST /admin/sessions/create (:sessions, :destroy) DELETE /admin/sessions/destroy (:base, :index) GET /admin/ (:accounts, :index) GET /admin/accounts (:accounts, :new) GET /admin/accounts/new (:accounts, :create) POST /admin/accounts/create (:accounts, :edit) GET /admin/accounts/edit/:id (:accounts, :update) PUT /admin/accounts/update/:id (:accounts, :destroy) DELETE /admin/accounts/destroy/:id (:accounts, :destroy_many) DELETE /admin/accounts/destroy_many (:posts, :index) GET /admin/posts (:posts, :new) GET /admin/posts/new (:posts, :create) POST /admin/posts/create (:posts, :edit) GET /admin/posts/edit/:id (:posts, :update) PUT /admin/posts/update/:id (:posts, :destroy) DELETE /admin/posts/destroy/:id (:posts, :destroy_many) DELETE /admin/posts/destroy_many Application: BlogTutorial::App URL REQUEST PATH (:about) GET /about-us (:posts, :index) GET /posts (:posts, :show) GET /posts/show/:id ``` This can be helpful to understand the mapping between controllers and urls. -------------------------------------------------------------------------------- ## Attaching Accounts to Posts So far, a post does not have a user associated as the author. Suppose that now we want to let every post have an author. Let's revisit our post model. We'll start by adding a new migration to attach an Account to a Post. ```shell $ padrino g migration AddAccountToPost account_id:integer apply orms/activerecord create db/migrate/003_add_account_to_post.rb ``` This creates a new migration with the desired field attaching the `account_id` to the post. Now, we'll return to the post model to setup the `account` association and add a few validations. ```ruby # models/post.rb class Post < Sequel::Model many_to_one :account plugin :validation_helpers def validate super validates_presence [:title, :body] end end ``` And add the association to the Account model: ```ruby # models/account.rb class Account < Sequel::Model one_to_many :posts ... end ``` Now we are ready to run the migration: `$ padrino rake sq:migrate` Let's create another migration to assign the first user to all existing posts: ```shell $ padrino g migration MigrateExistingPostsToFirstAccount apply orms/activerecord create db/migrate/004_migrate_existing_posts_to_first_account.rb ``` And change the content of the migration: ```ruby # db/migrate/004_migrate_existing_posts_to_first_account.rb Sequel.migration do up do first_account_id = from(:accounts).get(:id) if first_account_id from(:posts).update(account_id: first_account_id) end end down do from(:posts).update(account_id: nil) end end ``` And run the migrations again: `$ padrino rake sq:migrate` We'll need to go inside the generated Padrino Admin and make some changes to include the account with the post. Head on over to `admin/controllers/posts.rb`. We're going to include the [current_account](https://github.com/padrino/padrino-framework/blob/master/padrino-admin/lib/padrino-admin/helpers/authentication_helpers.rb#L18 "current_account") to the creation of a new Post. ```ruby # admin/controllers/posts.rb Admin.controllers :posts do ... post :create do @post = Post.new(params[:post]) @post.account = current_account ... end ... end ``` We'll also update the post view to show the changes that we made and display the author: ```haml -# app/views/posts/show.haml %section.articles .column.is-8.is-offset-2 .card.article .card-content .media .media-content.has-text-centered %h3.title.article-title= @post.title .tags.has-addons.level-item %span.tag.is-rounded.is-info= @post.account.email %span.tag.is-rounded= time_ago_in_words(@post.created_at || Time.now) + ' ago' .content.article-body = simple_format(@post.body) %p= link_to 'View all posts', url_for(:posts, :index) -# app/views/posts/_post.haml .column.is-7.is-offset-2 .card.article .card-content .media .media-content.has-text-centered %p.title.article-title %h3.has-text-centered = link_to post.title, url_for(:posts, :show, id: post.id) .has-addons %span.tag.is-rounded.is-info= post.account.email %span.tag.is-rounded= time_ago_in_words(post.created_at || Time.now) + ' ago' .content.article-body = simple_format(post.body) ``` Now, lets add another user. Revisit and click on the Account tab. Now create a new Account record (don't forget to give the new account the admin role). Once you have a new account, try logging into it and then adding one more post in the admin interface. There you have it, multiple users and posts! See the effects of our changes by visiting to see our newly created posts linked to the author that wrote them. -------------------------------------------------------------------------------- ## Site Layout Now that the application has been properly configured and the server has been started, let's create a few basic styles and define a layout to prepare the application for continued development. We will take the [bulma css framework](https://bulma.io/ "bulma") for our application. Let's install the plugin with `padrino g plugin bulma`. You can find more plugins under https://github.com/padrino/padrino-recipes. Next, let us create a layout for our application to use. A layout is a file that acts as a container for the content templates yielded by each route. The layout should be used to create a consistent structure between each page of the application. To create a layout, simply add a file to the `app/views/layouts` directory: ```haml -# app/views/layouts/application.haml !!! Strict %html %head %title= 'Padrino Sample Blog' = stylesheet_link_tag 'bulma', 'application' = javascript_include_tag 'jquery', 'application' %body %nav.navbar %div.container .navbar-brand %a.navbar-item{href: '/'} %img{alt: 'Logo of Padrino blog', src: 'http://padrinorb.com/images/logo-6475397a.svg'}/ %span.navbar-burger.burger{'data-targe' => 'navbarMenu'} %span %span %span #navbarMenu.navbar-menu .navbar-end = link_to 'Home', '/', {class: 'navbar-item'} = link_to 'Blog', url_for(:posts, :index), {class: 'navbar-item'} = link_to 'About us', url_for(:about), {class: 'navbar-item'} %section.hero.is-info.is-medium.is-bold .hero-body .container.has-text-centered %h1.title An example blog created with Padrino %div.container #main= yield ``` This layout creates a basic structure for the blog and requires the necessary stylesheets and javascript files for controlling the behavior and presentation of our site. Next, we adjust some styling for our blog in the `/public/stylesheets/application.css`: ```css .hero-body { background-image: url(https://farm3.staticflickr.com/2840/33942486610_e0c80a7999_o_d.jpg); background-position: center; background-size: cover; background-repeat: no-repeat; height: 700px; background-color: black; } h1.title { margin-top: 145px; } h3.has-text-centered { color: #363636; font-size: 2rem; font-weight: 600; line-height: 1.125; margin-bottom: 1.5rem; } .articles { margin: 5rem 0; margin-top: 5rem; margin-top: -200px; } ``` And to have a proper mobile burger navigation we need JavaScript in `public/javascripts/application.js`: ```js document.addEventListener('DOMContentLoaded', function () { // Get all "navbar-burger" elements var $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0); // Check if there are any navbar burgers if ($navbarBurgers.length > 0) { // Add a click event on each of them $navbarBurgers.forEach(function ($el) { $el.addEventListener('click', function () { // Get the target from the "data-target" attribute var target = $el.dataset.target; var $target = document.getElementById(target); // Toggle the class on both the "navbar-burger" and the "navbar-menu" $el.classList.toggle('is-active'); $target.classList.toggle('is-active'); }); }); } }); ``` The blog has now a much improved look and feel! See the new style by visiting . If you want to setup a RSS feed for your page please follow [the instructions on our wiki](https://github.com/padrino/padrino-docs/wiki/Blog-Tutorial-generating-RSS-feed "the instructions on the wiki"). --- # Features: Overview # Overview This is a guide intended to show you more features of Padrino. - [Development Commands](/guides/features/development-commands/ "Development Commands") - [Mounting Applications](/guides/features/mounting-applications/ "Mounting Applications") - [Padrino Admin](/guides/features/padrino-admin/ "Padrino Admin") - [Padrino Mailer](/guides/features/padrino-mailer/ "Padrino Mailer") - [Padrino Cache](/guides/features/padrino-cache/ "Padrino Cache") - [Localization](/guides/features/localization/ "Localization") - [Extensions](/guides/features/extensions/ "Extensions") - [Rake Tasks](/guides/features/rake-tasks/ "Rake Tasks") --- # Features: Padrino Admin # Padrino Admin Padrino comes shipped with a slick and beautiful Admin Interface, with the following features: Feature | Description --------------------- | ------------------------------------------------------------------------------ **Orm Agnostic** | Adapters for datamapper, sequel, activerecord, mongomapper, mongoid, couchrest **Authentication** | User Authentication Support, User Authorization Management **Template Agnostic** | Erb and Haml Rendering Support **Scaffold** | You can create a new "admin interface" by providing a single Model **MultiLanguage** | English, German, Russian, Danish, French, Brazilian and Italian localizations -------------------------------------------------------------------------------- ## Admin Usage Create a new project: ```shell $ padrino g project admin-test-sample -d datamapper $ cd admin-test-sample && bundle ``` Create the admin application: ```shell $ padrino g admin -e erb ``` Follow the instructions in your terminal and provide a valid email and password for your newly created admin account: - edit your config/database.rb - create the database: `$ bundle exec rake db:create` - migrate your database: `$ bundle exec rake db:migrate` - seed your database with some data: `$ bundle exec rake db:seed` Your admin section is now "setup": you can start padrino `padrino s` and point your web browser to and log in with your admin account credentials. If you need to create a "scaffold", (basic CRUD actions) create a _model_, migrate your database, generate your scaffolding folder structure and views and add those to your admin section by running the following commands: ```shell $ padrino g model post title:string body:text $ padrino rake db:migrate $ padrino g admin_page post $ padrino s ``` That's it! Browse to and access your model by clicking on the newly created tab on your admin navbar: there you can create, edit, destroy and display your objects. You can find the sample app on [github](https://github.com/padrino/admin-test-sample "github"). -------------------------------------------------------------------------------- ## Admin Authentication Padrino Admin uses a single model Account for managing roles, memberships and permissions (User Authentication and Authorization). -------------------------------------------------------------------------------- ## Scenario E-commerce (User Authentication) To use a practical example, let's examine a common e-commerce application scenario, where we need to limit access to some of our controllers actions; we can easily accomplish this by editing `app.rb` accordingly: ```ruby class MyEcommerce < Padrino::Application register Padrino::Admin::AccessControl enable :authentication enable :store_location set :login_page, '/login' access_control.roles_for :any do |role| role.protect '/customer/orders' role.protect '/cart/checkout' end end ``` In the above example we protect paths starting with `/customer/orders` and `/cart/checkout`. The result will be that an unauthenticated user will not be able to access those actions, and they will be asked to authenticate; first by visiting our `:login_page` defined as `/login` and by providing their login credentials (default authentication behavior will use email and password). When successfully logged in, they will be granted access to the two protected pages. -------------------------------------------------------------------------------- ## Admin Scenario (User Authorization) Another common scenario is needing multiple roles with various level of access, instead of providing all management functionality to all logged in users. Consider a site where you want to allow unauthenticated users to login, an **editor** to manage posts and categories, and an **admin** role to manage settings. The Padrino admin generator will by default create an `Account` model with a `role` attribute which you can combine with the `project_module` method to easily manage which functionality is available to your users. ```ruby class Admin < Padrino::Application register Padrino::Admin::AccessControl enable :authentication disable :store_location set :login_page, '/admin/sessions/new' access_control.roles_for :any do |role| role.protect '/' role.allow '/sessions' end access_control.roles_for :admin do |role| role.project_module :settings, '/settings' end access_control.roles_for :editor do |role| role.project_module :posts, '/posts' role.project_module :categories, '/categories' end end ``` In the above example, we _protect_ the entire admin section (all paths starting with "/") with the only exception for all those paths starting with `/sessions` giving our `unauthenticated` users the possibility to log in by redirecting them to our login page and asking them to provide their email and password. If we are logged in as an **admin** (`account.role == 'admin'`) we will **only** have access to the `/settings` path. If we are logged in as an **editor** (`account.role == 'editor'`) we will **only** have access to the `/posts` and `/categories` paths. -------------------------------------------------------------------------------- ## Sharing Sessions Between Mounted Applications Sessions can be shared between mounted applications by setting a `:session_id` with the line `set :session_id, "your_session_id"` in each apps `app.rb`. -------------------------------------------------------------------------------- ## Contributing Persistence Adapters If you are planning to use padrino with other adapters rather than the currently supported ones, and you want to contribute to the project by extending its support with additional adapters like [ohm](https://github.com/soveran/ohm "ohm"), [ruby-driver](https://github.com/datastax/ruby-driver "ruby-driver") and so on, be sure to check out the [adding components](/guides/adding-components/overview "adding components") guide. --- # Features: Padrino Mailer # Padrino Mailer This component creates an easy and intuitive interface for delivering email within a Sinatra application. The [mail](http://github.com/mikel/mail "mail") library is utilized to do the bulk of the work. There is full support for rendering email templates, using a html content type and for file attachments. The Padrino Mailer uses a familiar Sinatra syntax similar to that of defining routes for a controller. -------------------------------------------------------------------------------- ## Configuration Let's take a look at using the Mailer in an application. By default, the mailer uses the [built-in sendmail](https://en.wikipedia.org/wiki/Sendmail "built-in sendmail") binary on the server. However, [other methods are supported](http://www.rubydoc.info/gems/mail/file/README.md#Sending_an_email_ "mail config"). For example, to use SMTP, add the following declaration to your application: ```ruby # app/app.rb set :delivery_method, smtp: { address: 'smtp.gmail.com', port: 587, user_name: '@gmail.com', password: '', authentication: :plain, enable_starttls_auto: true } ``` Once those have been defined, the default will become SMTP delivery unless overwritten in an individual mail definition. You can also configure the mailer to not send emails during development or testing. This can be done with: ```ruby # app/app.rb set :delivery_method, :test ``` When set, messages are added to the test mailer and can be retrieved with: ```ruby Mail::TestMailer.deliveries ``` -------------------------------------------------------------------------------- ## Quick Usage Padrino supports sending any arbitrary email (using either sendmail or SMTP) right from your controllers. This is ideal for 'one-off' emails where the 'full' mailer object is simply unnecessary or too heavy for your simple task. Delivering an email within your controller is simple: ```ruby # app/controllers/session.rb post :create do email( from: 'tony@reyes.com', to: 'john@smith.com', subject: 'Welcome!', body: 'Body' ) end ``` This simple helper will accept any of the standard email attributes and deliver your email in a single command. You can also use a block, render a template for the body and specify a delivery method: ```ruby # app/controllers/session.rb post :create do email do from 'tony@reyes.com' to 'john@smith.com' subject 'Welcome!' body render('email/registered') via :sendmail end end ``` This is all you need to send simple emails. However, Padrino also supports a more 'structured' mailer system as well. -------------------------------------------------------------------------------- ## Mailer Usage To use the structured mailer syntax, we should define a custom mailer using the `mailer` block: ```ruby # app/mailers/sample_mailer.rb MyAppName.mailer :sample do email :registration_email do |name, email| from 'admin@site.com' to email subject 'Welcome to the site!' locals :name => name, :email => email render 'sample/registration_email' content_type :html # optional, defaults to :plain via :sendmail, location: "/usr/bin/sendmail" # optional, to smtp if defined otherwise sendmail end end ``` Note that this can be created much easier by using the padrino-mailer generator in the terminal: ```shell $ padrino g mailer Sample registration_email ``` This mailer defines a mail route called `registration_mail` within the `sample` mailer. The `registration_email` route accepts the name and email arguments. Arguments are passed to the email body template via the `locals` method. The render command renders the email body template with the local variables, which should be defined in `[views_path]/mailers/sample/registration_email.erb` as shown below: ```erb # ./views/mailers/sample/registration_email.erb This is the body of the email and can access the <%= name %> variable. That‘s all there is to defining the body of the email which can be in plain text or html. ``` Note that the mailer has full support for content type resolution and the email could also be in the path `./views/mailers/sample/registration_email.html.erb` or `./views/mailers/sample/registration_email.plain.erb` specifying the mime type in the file name as well. Once the mailer has been defined and the template written, the email route can be invoked by the `deliver` method: ```ruby deliver(:sample, :registration_email, 'Bob', 'bob@bobby.com') ``` And that will then deliver the email according the configured options. -------------------------------------------------------------------------------- ## Multipart Emails The mailer supports [multipart emails](https://en.wikipedia.org/wiki/MIME "multipart emails") quite easily: ```ruby # app/mailers/sample_mailer.rb mailer :sample do email :email_with_parts do from 'admin@site.com' # ... text_part { render('path/to/basic.text') } html_part render('path/to/basic.html') # shorter part syntax end end ``` You can even specify multiple part types using the `provides` declaration: ```ruby # app/mailers/sample_mailer.rb mailer :sample do email :email_with_parts do from 'admin@site.com' # ... # renders path/to/basic.html.erb and path/to/basic.plain.erb provides :plain, :html render 'path/to/basic' end end ``` These will deliver a multipart/alternative email with the appropriate plain text and html sections. -------------------------------------------------------------------------------- ## File Attachments Using the mailer attaching files to a message is easy: ```ruby # app/mailers/sample_mailer.rb mailer :sample do email :email_with_files do from 'admin@site.com' # ... body "Here are your files!" add_file :filename => 'somefile.png', content: File.read('/somefile.png') add_file '/full/path/to/some-other-file.png' end end ``` This will deliver your email with the appropriate body and the specified files attached. -------------------------------------------------------------------------------- ## Defaults To define mailer defaults for a message, we can do so app-wide or within a `mailer` block. ```ruby # app/app.rb # Application-wide mailer defaults set :mailer_defaults, from: 'admin@padrinorb.com' # app/mailers/sample_mailer.rb MyAppName.mailers :sample do defaults content_type: 'html' email :registration do |name, age| # Uses default 'content_type' and 'from' values but can also overwrite them to 'user@domain.com' subject 'Welcome to the site!' locals name: name render 'sample/registration' end end ``` Using defaults makes sending email even easier when certain attributes are repeated between messages. -------------------------------------------------------------------------------- ## Rendering Variations To render a short body inline: ```ruby # app/mailers/sample_mailer.rb mailer :sample do email :short_email do |name, user| # ... body 'This is a short body defined right in the mailer itself' end end ``` To render a different template: ```ruby # app/mailers/sample_mailer.rb mailer :sample do email :custom_email do |name, user| # ... render('path/to/template') # relative to views_path/mailers end end ``` --- # Features: Padrino Cache # Padrino Cache This component enables caching of an application's response contents on both page- and fragment-levels. Output cached in this manner is persisted, until it expires or is actively expired, in a configurable store of your choosing. Most popular key/value stores work out of the box. Take a look at the [Moneta documentation](http://rubydoc.info/gems/moneta) for a list of all supported stores. ## Caching Quickstart Padrino-cache can reduce the processing load on your site very effectively with minimal configuration. By default, the component caches pages in a file store at `tmp/cache` within your project root. Entries in this store correspond directly to the request issued to your server. In other words, responses are cached based on request URL, with one cache entry per URL. This behavior is referred to as "page-level caching." If this strategy meets your needs, you can enable it very easily: ```ruby # Page-level caching class SimpleApp < Padrino::Application register Padrino::Cache enable :caching get '/foo', cache: true do expires 30 # expire cached version at least every 30 seconds 'Hello world' end end ``` You can also cache on a controller-wide basis: ```ruby # Controller-wide caching example class SimpleApp < Padrino::Application register Padrino::Cache enable :caching get '/' do 'Hello world' end # Requests to routes within '/admin' controller '/admin', :cache => true do expires 60 get '/foo' do 'Url is /admin/foo' end get '/bar' do 'Url is /admin/bar' end post '/baz' do # We cache only GET and HEAD request 'This will not be cached' end end end ``` You can also provide a custom `cache_key` in any route: ```ruby class SimpleApp < Padrino::Application register Padrino::Cache enable :caching get '/post/:id', :cache => true do @post = Post.find(params[:id]) cache_key :my_name end end ``` In this way you can manually expire cache with CachedApp.cache.delete(:my_name) for example from the Post model after an update. If you specify `:cache => true` but do not invoke `expires`, the response will be cached indefinitely. Most of the time, you will want to specify the expiry of a cache entry by `expires`. Even a relatively low value--1 or 2 seconds--can greatly increase application efficiency, especially when enabled on a very active part of your domain. ## Helpers When an application registers padrino-cache, it gains access to several helper methods. These methods are used according to your caching strategy, so they are explained here likewise--by functionality. As with all code optimization, you may want to start simply (at "page level"), and continue if necessary into sub-page (or "fragment level" ) caching. There is no one way to approach caching, but it's always good to avoid complexity until you need it. Start at the page level and see if it works for you. The padrino-cache helpers are made available to your application thusly: ```ruby # Enable caching class CachedApp < Padrino::Application register Padrino::Cache # includes helpers enable :caching # turns on caching # ... controllers/routes ... end ``` ### Page Caching As described above in the "Caching Quickstart" section, page caching is very easy to integrate into your application. To turn it on, simply provide the `:cache => true` option on either a controller or one of its routes. By default, cached content is persisted with a "file store"--that is, in a subdirectory of your application root. #### `expires( seconds )` This helper is used within a controller or route to indicate how often cached *page-level* content should persist in the cache. After `seconds` seconds have passed, content previously cached will be discarded and re-rendered. Code associated with that route will *not* be executed; rather, its previous output will be sent to the client with a 200 OK status code. ```ruby # Setting content expiry time class CachedApp < Padrino::Application register Padrino::Cache # includes helpers enable :caching # turns on caching controller '/blog', :cache => true do expires 15 get '/entries' do 'just broke up eating twinkies lol' end end end ``` Note that the "latest" method call to `expires` determines its value: if called within a route, as opposed to a controller definition, the route's value will be assumed. ### Fragment Caching Whereas page-level caching, described in the first section of this document, works by grabbing the entire output of a route, fragment caching gives the developer fine-grained control of what gets cached. This type of caching occurs at whatever level you choose. Possible uses for fragment caching might include: * a 'feed' of some items on a page * output fetched (by proxy) from an API on a third-party site * parts of your page which are largely static/do not need re-rendering every request * any output which is expensive to render #### `cache( key, opts, &block )` This helper is used anywhere in your application you would like to associate a fragment to be cached. It can be used in within a route: ```ruby # Caching a fragment class MyTweets < Padrino::Application register Padrino::Cache # includes helpers enable :caching # turns on caching controller '/tweets' do get :feed, map: '/:username' do username = params[:username] @feed = cache("feed_for_#{username}", expires: 3) do @tweets = Tweet.all(username: username) render 'partials/feedcontent' end # Below outputs @feed somewhere in its markup render 'feeds/show' end end end ``` This example adds a key to the cache of format `feed_for_#{username}` which contains the contents of that user's feed. Any subsequent action within the next 3 seconds will fetch the pre-rendered version of `feed_for_#{username}` from the cache instead of re-rendering it. The rest of the page code will, however, be re-executed. Note that any other action will reference the same content if it uses the same key: ```ruby # Multiple routes sharing the same cached fragment class MyTweets < Padrino::Application register Padrino::Cache # includes helpers enable :caching # turns on caching controller :tweets do get :feed, map: '/:username' do username = params[:username] @feed = cache("feed_for_#{username}", expires: 3) do @tweets = Tweet.all(username: username) render 'partials/feedcontent' end # Below outputs @feed somewhere in its markup render 'feeds/show' end get :mobile_feed, map: '/:username.iphone' do username = params[:username] @feed = cache("feed_for_#{username}", expires: 3) do @tweets = Tweet.all(username: username) render 'partials/feedcontent' end render 'feeds/show.iphone' end end end ``` The `opts` argument is actually passed to the underlying store. The stores support the `:expires` option out of the box or are enhanced by Moneta to support it. Finally, to DRY up things a bit, we might do: ```ruby # Multiple routes sharing the same cached fragment class MyTweets < Padrino::Application register Padrino::Cache # includes helpers enable :caching # turns on caching controller :tweets do # This works because all routes in this controller specify :username before do @feed = cache("feed_for_#{params[:username]}", expires: 3) do @tweets = Tweet.all(username: params[:username]) render 'partials/feedcontent' end end get :feed, map: '/:username' do render 'feeds/show' end get :mobile_feed, map: '/:username.iphone' do render 'feeds/show.iphone' end end end ``` Of course, this example assumes the markup generated by rendering `partials/feedcontent` would be suitable for both feed formats. This may or may not be the case in your application, but the principle applies: fragments are shared between all code which accesses the cache using the same key. ### Caching URLs with query strings Another use of cache_key is when you’d like to include query string parameters as part of the key; for example when using `will_paginate`, you might want to cache `url?page=1` separately from `url?page=2`: ```ruby cache_key { request.path_info + (params[:page].present? ? "?page=#{params[:page]}" : '') } ``` If you're using `ActiveSupport`, you can make this a bit more robust: ```ruby cache_key { request.path_info + '?' + params.slice('page').to_param } ``` ## Caching Store You can set a global caching option or a per app caching options. ### Global Caching Options ```ruby Padrino.cache = Padrino::Cache.new(:LRUHash) # in-memory, the default choice Padrino.cache = Padrino::Cache.new(:File, dir: Padrino.root('tmp', app_name.to_s, 'cache')) # Keeps cached values in file Padrino.cache = Padrino::Cache.new(:Memcached) # Uses default server at localhost Padrino.cache = Padrino::Cache.new(:Memcached, server: '127.0.0.1:11211', exception_retry_limit: 1) Padrino.cache = Padrino::Cache.new(:Memcached, backend: memcached_or_dalli_instance) Padrino.cache = Padrino::Cache.new(:Redis) # Uses default server at localhost Padrino.cache = Padrino::Cache.new(:Redis, host: '127.0.0.1', port: 6379, db: 0) Padrino.cache = Padrino::Cache.new(:Redis, backend: redis_instance) Padrino.cache = Padrino::Cache.new(:Mongo) # Uses default server at localhost Padrino.cache = Padrino::Cache.new(:Mongo, backend: mongo_client_instance) ``` You can manage your cache from anywhere in your app: ```ruby Padrino.cache['val'] = 'test' Padrino.cache['val'] # => 'test' Padrino.cache.delete('val') Padrino.cache.clear ``` The Padrino cache constructor `Padrino::Cache.new` calls `Moneta.new` to create a cache instance. Please refer to the [Moneta documentation](http://rubydoc.info/gems/moneta) if you have special requirements, for example if you want to configure the marshalling mechanism or use a more exotic backend. ### Application Caching Options ```ruby set :cache, Padrino::Cache.new(:LRUHash) # in-memory set :cache, Padrino::Cache.new(:Memcached) set :cache, Padrino::Cache.new(:Redis) set :cache, Padrino::Cache.new(:File, dir: Padrino.root('tmp', app_name.to_s, 'cache')) # default choice ``` You can manage your cache from anywhere in your app: ```ruby MyApp.cache['val'] = 'test' MyApp.cache['val'] # => 'test' MyApp.cache.delete('val') MyApp.cache.clear ``` ## Expiring Cached Content In certain circumstances, cached content becomes stale. The `expire` helper removes content associated with a key or keys, which your app is then free to re-generate. ### `expire( *key )` #### Fragment-level expiration Using the example above of a tweet server, let's suppose our users have a tendency to post things they quickly regret. When we query our database for new tweets, let's check to see if any have been deleted. If so, we'll do our user a favor and instantly re-render the feed. ```ruby # Expiring fragment-level cached content class MyTweets < Padrino::Application register Padrino::Cache # includes helpers enable :caching # turns on caching enable :session # we'll use this to store last time visited COMPANY_FOUNDING = Time.utc(2010, 'April') controller :tweets do get :feed, map: '/:username' do last_visit = session[:last_visit] || params[:since] || COMPANY_FOUNDING username = params[:username] @tweets = Tweet.since(last_visit, username: username).limit(100) expire("feed since #{last_visit}") if @tweets.any? { |t| t.deleted_since?(last_visit) } session[:last_visit] = Time.now @feed = cache("feed since #{last_visit}", expires: 60) do @tweets = @tweets.find_all { |t| !t.deleted? } render 'partials/feedcontent' end render 'feeds/show' end end end ``` Normally, this example will only re-cache feed content every 60 seconds, but it will do so immediately if any tweets have been deleted. #### Page-level expiration Page-level expiration works exactly like the example above--by using `expire` in your controller. The key is typically `env['PATH_INFO']`. --- # Features: Mounting Applications # Mounting Applications Padrino applications are all automatically mountable into other Padrino projects. This means that a given Padrino project directory can easily mount multiple applications. This allows for better organization of complex applications, re-usable applications that can be applied (i.e admin, auth, blog) and even more flexibility. You can think of mountable applications as a "full-featured" [Merb](https://github.com/merb/merb "Merb") slice or Rails engine. Instead of a separate construct, any application can simply be packaged and mounted into another project. -------------------------------------------------------------------------------- ## Mounting Syntax Padrino stores application mounting information by default within `config/apps.rb`. This file is intended to keep all information regarding what applications are mounted to which URI's. An `apps.rb` file has the following structure: ```ruby Padrino.mount('blog').to('/blog') Padrino.mount('website').to('/website') Padrino.mount('app').to('/') ``` This would mount three applications onto the Padrino project, one served from the '/blog' uri namespace one with '/website' uri namespace and the other served from the '/' uri namespace. -------------------------------------------------------------------------------- ## Advanced Mounting Support In addition to the basic mounting capabilities afforded by Padrino for each application within a project, the [Padrino Router](http://github.com/padrino/padrino-framework/blob/master/padrino-core/lib/padrino-core/router.rb) also allows for more advanced mounting conditions. The `Padrino::Router` is an enhanced version of [Rack UrlMap](http://github.com/rack/rack/blob/master/lib/rack/urlmap.rb) which extends the ability to mount applications to a specified path, or specify host and subdomains to match to an application. For example, you could put the following in your `config/apps.rb` file: ```ruby # Adds support for matching an app to a host string or pattern Padrino.mount('Blog').to('/').host('blog.example.org') Padrino.mount('Admin').host('admin.example.org') Padrino.mount('WebSite').host(/.*\.?example.org/) Padrino.mount('Foo').to('/foo').host('bar.example.org') ``` This will configure each application to match to the given host pattern simplifying routing considerably. --- # Features: Development Commands # Development Commands Padrino also supports robust logging capabilities. By default, logging information will go to the `STDOUT` in development (for use in a console) and in an environment-specific log file `log/development.log` in test and production environments. You can modify the logging behavior or disable logging altogether (more docs [here](http://www.rubydoc.info/github/padrino/padrino-framework/Padrino/Logger "logger")): ```ruby # boot.rb Padrino::Logger::Config[:development][:stream] = :to_file Padrino.load! ``` To use the logger within a Padrino application, simply refer to the `logger` method accessible within your app and any controller or views: ```ruby # controllers/example.rb SimpleApp.controllers do get('/test') { logger.info 'This is a test' } end ``` The logger automatically supports severity through the use of `logger.info`, `logger.warn`, `logger.error`, et al. For more information about the logger, check out our [Logger RDoc](http://www.rubydoc.info/github/padrino/padrino-framework/Padrino/Logger "Logger RDoc"). -------------------------------------------------------------------------------- ## Development Reloader Padrino applications also have the enabled ability to automatically reload all changing application files without the need to restart the server. Through the use of a customized Rack middleware, all files on the 'load path' are monitored and reloaded whenever changes are applied. This makes rapid development much easier and provides a better alternative to 'shotgun' or 'rerun' which require the application server to be restarted which makes requests take much longer to complete. An application can explicitly enable / disable reloading through the use of options: ```ruby # app.rb class SimpleApp < Padrino::Application disable :reload # reload is disabled in all environments enable :reload # enabled in all environments end ``` -------------------------------------------------------------------------------- ## Gemfile Dependency Resolution Padrino has native support for `bundler` and the Gemfile system. If your Padrino application was generated with `padrino g`, a Gemfile has already been created for your application. This file will contain a list of all the dependencies for our application. ```ruby # Gemfile source 'https://rubygems.org' gem 'rake' gem 'padrino', '0.16.1' ``` This manifest file uses the standard `bundler` gem syntax of which details can be found in the [Bundler Website](http://bundler.io/ "Bundle Website"). This gem allows us to place all our dependencies into a single file. Padrino will then automatically require all necessary files (if they exist on the system). If the dependencies are not on the system, you can automatically vendor all necessary gems using the `bundle install --path ./vendor` command within the application root or (only with bundler 1.0) run `bundle install` to install system wide. Note that this is all possible without any further effort than adding the Gemfile (or having this generated automatically with generators explained later). -------------------------------------------------------------------------------- ## Auto Load Paths Padrino also intelligently supports requiring useful files within your application automatically and provides functionality for easily splitting up your application into separate files. Padrino automatically requires `config/database.rb` as a convention for establishing database connection. Also, any files within the `lib` folder will be required automatically by Padrino. This is powered by the fact that Padrino will automatically load (and reload) any directory patterns within the 'prerequisite paths'. Additional directory patterns can be added to the set of reloaded files as needed by simply appending to the `prerequisites` within your application: ```ruby # config/boot.rb Padrino.after_load do SimpleApp.prerequisites << Padrino.root('my_app', 'custom_model.rb') SimpleApp.prerequisites << Padrino.root('custom_folder/*.rb') end Padrino.load! ``` This will instruct Padrino to autoload these files (and reload them when changes are detected). By default, the load path contains certain paths known to contain important files such as controllers, mailers, models, urls, and helpers. -------------------------------------------------------------------------------- ## Terminal Commands Padrino also comes equipped with multiple useful terminal commands which can be activated to perform common tasks such as starting / stopping the application, executing the unit tests or activating an irb session. The following commands are available: ```shell # starts the app server (non-daemonized) $ padrino start # starts the app server (daemonized) with given port, environment and adapter $ padrino start -d -p 3000 -e development -a thin # Stops a daemonized app server $ padrino stop # Bootup the Padrino console (irb) $ padrino console # Run/List tasks $ padrino rake -T # Run piece of code in the context of Padrino (with given environment) $ padrino runner 'puts Padrino.env' -e development # Run Ruby file in the context of Padrino $ padrino r script/my_script.rb ``` The last command "padrino rake" look for rake files in: - `lib/tasks/**/*.rake` - `tasks/**/*.rake` - `test/test.rake` - `spec/spec.rake` In this way you can customize project tasks. Using these commands can simplify common tasks making development that much smoother. -------------------------------------------------------------------------------- ## Special Folders Padrino load these paths: ```shell # special folders project/lib project/models project/shared/lib project/shared/models project/each_app/models ``` This mean that you are free to store for example `models` where you prefer, if you have two or more apps with same models you can use `project/shared/models` or `root/models`. If you have only one app you still use `project/app/models`(this is the default `padrino g` choice) --- # Features: Extensions # Extensions Extensions provide helper or class methods for Sinatra and Padrino applications. See [the Sinatra extensions page](http://www.sinatrarb.com/extensions-wild.html) for more information about this topic. We also have some 3rd party extensions (ex: for ActiveRecord/MongoMapper/DataMapper, etc ...) that are useful for web developers. -------------------------------------------------------------------------------- ## Usage If the extension is a gem put it in Gemfile, otherwise create a file under your lib directory. -------------------------------------------------------------------------------- ## Extension List Name | Description | Gist ------------------ | ------------------------------------------------- | ---------------------------------------------------------------- Exception Notifier | Sends an email when an exception is raised | [gist](http://gist.github.com/308913#file_exception_notifier.rb) Auto Locale | Sets for you I18n.locale parsing path\_info | [gist](http://gist.github.com/308919#file_auto_locale.rb) Locale | Translates ActiveRecord attributes | [gist](http://gist.github.com/308915#file_locale.rb) Permalink | Generates ActiveRecord permalinks for your fields | [gist](http://gist.github.com/308928#file_permalink.rb) Flash | Helps setup cookie sessions with swfupload | [gist](http://gist.github.com/313322#file_flashmiddleware.rb) --- # Features: Localization # Localization Padrino supports full localization in: - padrino-core (date formats, time formats etc ...) - padrino-admin (admin language, orm fields, orm errors, etc ...) - padrino-helpers (currency, percentage, precision, duration etc ...) At the moment we support the following list of languages: - Czech - Danish - German - English - Spanish - French - Italian - Dutch - Norwegian - Russian - Polish - Brazilian Portuguese - Turkish - Ukrainian - Traditional Chinese - Simplified Chinese - Japanese -------------------------------------------------------------------------------- ## Provide your translations Download and translate these files: - [padrino-core.yml](https://raw.github.com/padrino/padrino-framework/master/padrino-support/lib/padrino-support/locale/en.yml) - [padrino-admin.yml](http://raw.github.com/padrino/padrino-framework/master/padrino-admin/lib/padrino-admin/locale/admin/en.yml) - [padrino-admin-orm.yml](http://raw.github.com/padrino/padrino-framework/master/padrino-admin/lib/padrino-admin/locale/orm/en.yml) - [padrino-helper.yml](http://raw.github.com/padrino/padrino-framework/master/padrino-helpers/lib/padrino-helpers/locale/en.yml) zip your files and send it to [padrinorb@gmail.com](mailto:padrinorb@gmail.org) -------------------------------------------------------------------------------- ## How to localize your app The request's locale can be set in a [route filter](http://padrinorb.com/guides/controllers/route-filters/): ```ruby # Route filter before do I18n.locale = :de end ``` Or within a route: ```ruby get "/" do I18n.locale = :de end ``` By default Padrino will search for all `.yml` or `.rb` files located in `app/locale`; as an example try to add the following to your `app/locale/de.yml`: ```yml de: foo: Bar ``` in your view or controller or wherever you prefer add: ```ruby I18n.t('foo') ``` you will get: ``` => "Bar" ``` For more ways to configure the locale see [Sinatra's I18n recipe](http://recipes.sinatrarb.com/p/development/i18n). -------------------------------------------------------------------------------- ## Translate Models (ActiveRecord) Translating models via Padrino requires few seconds thanks to a built-in rake task! Assuming the following Account model: ```ruby create_table :accounts do |t| t.string :surname t.string :name t.string :email t.string :salt t.string :crypted_password t.string :role end ``` add this to your `boot.rb` (or anywhere else): ```ruby Padrino.before_load do I18n.locale = :it end ``` run padrino rake task for localizing your model: ```shell $ padrino rake ar:translate ``` A new `it.yml` file will be created into `/app/locale/models/account/it.yml` with the following: ```yml it: models: account: name: Account attributes: id: Id name: Name surname: Surname email: Email salt: Salt crypted_password: Crypted password role: Role ``` You can now edit your generated `it.yml` file to reflect your current locale (Italian): ```yml it: models: account: name: Account attributes: id: Id name: Nome surname: Cognome email: Email salt: Salt crypted_password: Crypted password role: Role ``` padrino-admin will now use your newly created yml file for translating the column names of grids, forms, error_messages etc ... -------------------------------------------------------------------------------- ## Form Builders [Form builder](http://padrinorb.com/guides/application-helpers/form-builders/) labels are automatically translated: ```haml -form_for :account, url(:accounts_create, :format => :js), :remote => true do |f| %table %tr %td=f.label :name %td=f.text_field :name %tr %td=f.label :surname %td=f.text_field :surname %tr %td=f.label :role %td=f.select :role, :options => access_control.roles ``` It looks for translations with a key of `MODEL.attributes.ATTRIBUTE` where `MODEL` is the name of the model passed to `form_for` and `ATTRIBUTE` is the given attribute name. --- # Features: Rake Tasks # Rake Tasks After generating a new padrino project, you will not find any Rakefile in your generated project folder structure; in fact it's not strictly needed to build a new one because we can already use padrino rake: ```shell # for a list of tasks $ padrino rake -T ``` If you need custom tasks you can add those to: - `your_project_/**lib/tasks**` - `your_project_/**tasks**` - `your_project_/**test**` - `your_project_/**spec**` Padrino will look recursively for any `*.rake` file in any of these directories. Padrino by default has some useful tasks. -------------------------------------------------------------------------------- ## Basic Like other frameworks we have an `:environment` task that loads our `environment` and `apps`. Example: ```ruby # This is a custom task # task/version.rake task version: :environment do puts Padrino.version end ``` -------------------------------------------------------------------------------- ## Routes We have support for retrieving a list of named routes within your application for easy access. ```shell $ padrino rake routes ``` which will return all the named routes for your project: ```shell Application: SampleBlogUpdated::Admin URL REQUEST PATH (:sessions, :new) GET /admin/sessions/new (:sessions, :create) POST /admin/sessions/create (:sessions, :destroy) DELETE /admin/sessions/destroy (:base, :index) GET /admin/ (:accounts, :index) GET /admin/accounts (:accounts, :new) GET /admin/accounts/new (:accounts, :create) POST /admin/accounts/create (:accounts, :edit) GET /admin/accounts/edit/:id (:accounts, :update) PUT /admin/accounts/update/:id (:accounts, :destroy) DELETE /admin/accounts/destroy/:id (:accounts, :destroy_many) DELETE /admin/accounts/destroy_many (:posts, :index) GET /admin/posts (:posts, :new) GET /admin/posts/new (:posts, :create) POST /admin/posts/create (:posts, :edit) GET /admin/posts/edit/:id (:posts, :update) PUT /admin/posts/update/:id (:posts, :destroy) DELETE /admin/posts/destroy/:id (:posts, :destroy_many) DELETE /admin/posts/destroy_many Application: SampleBlogUpdated::App URL REQUEST PATH (:about) GET /about_us (:posts, :index) GET /posts(.:format)? (:posts, :show) GET /posts/show/:id ``` -------------------------------------------------------------------------------- ## Testing When testing with Padrino you have a built-in `padrino rake test` or for rspec `padrino rake spec`. ```shell $ padrino rake test # => for bacon, shoulda $ padrino rake spec # => for rspec ``` you can customize `test/test.rake` or `spec/spec.rake` -------------------------------------------------------------------------------- ## I18n You can auto generate a _yml_ file for localizing your models using this command: ```shell $ padrino rake locale:models ``` See [Localization](/guides/features/localization "Localization") for detailed instructions. -------------------------------------------------------------------------------- ## ORM Padrino has rake tasks for _DataMapper_ , _ActiveRecord_, _Sequel_, _Mongomapper_,and _Mongoid_ with some **bonuses**. **NOTE**: we have a **namespace** for each orm, because of this, Padrino can mount several applications and each of them can use different orms without conflict, so that you can have multiple applications living together and one of them can use DataMapper, while another `ActiveRecord/MongoMapper/Couch/Sequel` instead. In this way we prevent collisions. -------------------------------------------------------------------------------- ## ActiveRecord Tasks ```shell rake ar:abort_if_pending_migrations # Raises an error if there are pending migrations. rake ar:auto:upgrade # Uses schema.rb to auto-upgrade. rake ar:charset # Retrieves database charset. rake ar:collation # Retrieves database collation. rake ar:create # Creates the database as defined in config/database.yml rake ar:create:all # Creates local databases as defined in config/database.yml rake ar:drop # Drops the database for the current Padrino.env rake ar:drop:all # Drops local databases defined in config/database.yml rake ar:forward # Pushes the schema to the next version. rake ar:migrate # Migrates the database through scripts in db/migrate. rake ar:migrate:down # Runs the "down" for a given migration VERSION. rake ar:migrate:redo # Rollbacks current migration and migrates up to version rake ar:migrate:reset # Resets your database using your migrations. rake ar:migrate:up # Runs the "up" for a given migration VERSION NUMBER rake ar:reset # Drops and recreates the database using db/schema.rb. rake ar:rollback # Rolls back the schema to previous schema version. rake ar:schema:dump # Creates a portable db/schema.rb file. rake ar:schema:load # Loads a schema.rb file into the database. rake ar:schema:to_migration # Creates a migration from schema.rb rake ar:schema:to_migration_with_reset # Creates a migration and resets the migrations log. rake ar:setup # Creates the database, loads the schema, and seeds data. rake ar:structure:dump # Dumps the database structure to a SQL file. rake ar:version # Retrieves the current schema version number. ``` **rake ar:auto:upgrade** This is some sort of super cool and useful task for people like me who don't love migrations (especially for small apps). It's a forked version of [auto_migrations](http://github.com/pjhyett/auto_migrations). Basically, instead of writing migrations you can directly edit your **schema.rb** and perform _a non destructive_ migration with `padrino rake ar:auto:upgrade`. -------------------------------------------------------------------------------- ## DataMapper Tasks ```shell rake dm:auto:migrate # Performs an automigration (resets your db data) rake dm:auto:upgrade # Performs a non destructive automigration rake dm:create # Creates the database rake dm:drop # Drops the database (postgres and mysql only) rake dm:migrate # Migrates the database to the latest version rake dm:migrate:down[version] # Migrates down using migrations rake dm:migrate:up[version] # Migrates up using migrations rake dm:reset # Drops the database, and migrates from scratch rake dm:setup # Create the database migrate and initialize with the seed data ``` -------------------------------------------------------------------------------- ## Sequel Tasks ```shell rake sq:migrate:auto # Perform automigration (reset your db data) rake sq:migrate:to[version] # Perform migration up/down to VERSION rake sq:migrate:up # Perform migration up to latest migration available rake sq:migrate:down # Perform migration down (erase all data) rake sq:reset # Drops the database, and migrates from scratch ``` -------------------------------------------------------------------------------- ## Mongomapper Tasks ```shell rake mm:translate # Generates .yml files for I18n translations ``` -------------------------------------------------------------------------------- ## Mongoid Tasks ```shell rake mi:drop # Drops all the collections for the database for the current environment rake mi:create_indexes # Create the indexes defined on your mongoid models rake mi:objectid_convert # Convert string objectids in mongo database to ObjectID type rake mi:cleanup_old_collections # Clean up old collections backed up by objectid_convert ``` -------------------------------------------------------------------------------- ## Seed Like in Rails we can populate our db using `db/seeds.rb` here's an example (from our [padrino-admin](/guides/padrino-admin/ "padrino-admin)): ```ruby email = shell.ask 'Which email do you want use for logging into admin?' password = shell.ask 'Tell me the password to use:' shell.say '' account = Account.create(email: email, password: password, password_confirmation: password, role: 'admin') if account.valid? shell.say 'Perfect! Your account was created.' shell.say '' shell.say 'Now you can start your server with padrino start and then login into /admin with:' shell.say " email: #{email}" shell.say " password: #{password}" shell.say '' shell.say "That's all!" else shell.say 'Sorry but some thing went wrong!' shell.say '' account.errors.full_messages.each { |m| shell.say " - #{m}" } end ``` --- # Generators: Overview # Overview Padrino provides generator support for quickly creating new Padrino applications. This provides many benefits such as constructing the recommended Padrino application structure, auto-generating a Gemfile listing all starting dependencies and guidelines provided within the generated files to help orient a new user to using Padrino. One important feature of the generators is that they were built from the ground up to support a wide variety of tools, libraries and gems for use within your Padrino application. This means that Padrino generators do **not** lock you into using any particular database, ORM, testing framework, templating engine or JavaScript library. In fact, when generating an application you can actually tell Padrino which components you would like to use! - [Projects](/guides/generators/projects "Projects") - [Plugins](/guides/generators/plugins "Plugins") - [Controllers](/guides/generators/controllers "Controllers") - [Models](/guides/generators/models "Model") - [Migrations](/guides/generators/migrations "Migrations") - [Mailers](/guides/generators/mailers "Mailers") - [Sub-Applications](/guides/generators/sub-applications "Sub-Applications") - [Tiny Skeleton](/guides/generators/tiny-skeleton "Tiny Skeleton") - [Admin](/guides/generators/admin "Admin") - [Components](/guides/generators/components "Components") - [Tasks](/guides/generators/tasks "Tasks") --- # Generators: Projects # Projects The usage for the project generator is quite simple: ```shell $ padrino g project --root -- ``` The simplest possible command to generate a base application would be: ```shell $ padrino g project demo_project ``` This would construct a Padrino application DemoProject (which extends from `Padrino::Application`) inside the folder `demo_project` at our current path. Inside the application there would be configuration and setup performed for the default components. You can also define specific components to be used: ```shell $ padrino g project demo_project -t rspec -e haml -m rr -s jquery -d datamapper -c sass ``` You can also instruct the generator to skip a certain component to avoid using one at all (or to use your own): ```shell $ padrino g project demo_project --test none --renderer none ``` You can also specify an alternate name for your core application using the `--app` option: ```shell $ padrino g project demo_project --app alternate_app_name # alias -n ``` The generator uses the `bundler` gem to resolve any application dependencies when the application is newly created. The necessary bundler command can be executed automatically through the generator with: ```shell $ padrino g project demo_project --run_bundler # alias -b ``` This can also be done manually by executing the command `bundle install` in the terminal at the root of the generated application. For more examples of using the project generator in common cases, check out the [Basic Projects](/guides/getting-started/basic-projects "Basic Projects") guide. The generator framework within Padrino is extensible and additional components and tools can be added easily. This would be achieved through forking our project and reading through the code in `lib/generators/project.rb` and the setup instructions inside the relevant files within `lib/generators/components/`. We are happy to accept pull requests for additional component types not originally included (although helping us maintain them would also be appreciated). ## Options The project generator has several available configuration options: Options | Default | Aliases | Description ------------------ | ------- | ------- | ------------------------------------------------ bundle | false | -b | execute bundler dependencies installation root | . | -r | the root destination path for the project dev | false | none | use edge version from local git checkout app | nil | -n | specify app name different from the project name tiny | false | -i | generate tiny project skeleton adapter | sqlite | -a | specify orm db adapter (mysql, sqlite, postgres) --migration_format | number | | format for migrations (number, timestamp) The available components and their default options are listed below: Component | Default | Aliases | Options ---------- | ------- | ------- | --------------------------------------------------------------------------------------- server | webrick | -s | thin, **puma** (recommended), spider-gazelle (unmaintained), mongrel (unmaintained), trinidad, webrick orm | none | -d | mongoid, activerecord, datamapper, couchrest, mongomatic, ohm, ripple, sequel, dynamoid test | none | -t | bacon, shoulda, cucumber, testunit, rspec, minitest script | none | -s | prototype, jquery, mootools, extcore, dojo renderer | none | -e | erb, haml, slim, liquid stylesheet | none | -c | sass, less, scss, compass mock | none | -m | rr, mocha Note: Be careful with your naming when using generators and do not have your project name, or any models or controllers overlap. Avoid naming your app "Posts" and then your controller or subapp with the same name. ## Examples **Generate a project with a different application name from the project path** ```shell $ padrino g my_project -n blog ``` This will generate the project at path `my_project/` but the applications name will be **Blog**. **Generate a project with mongoid and run bundler after** ```shell $ padrino g project your_project -d mongoid -b ``` **Generate a project with shoulda test and rr mocking** ```shell $ padrino g project your_project -t shoulda -m rr ``` **Generate a project with sequel with mysql** ```shell $ padrino g project your_project -d sequel -a mysql ``` **Generate a tiny project skeleton** ```shell $ padrino g project your_project --tiny ``` **Choose a root for your project** ```shell $ padrino g project your_project -r /usr/local/padrino ``` This will create a new padrino project in `/usr/local/padrino/your_project/` **Use Padrino from a git cloned repository** ```shell padrino g project your_project [--dev] # Use padrino from a git checkout ``` Visit [The Bleeding Edge](/guides/introduction/the-bleeding-edge "The Bleeding Edge") for more info how to setup a **dev** environment. --- # Generators: Plugins # Plugins The Plugin Generator allows you to create Padrino projects based on a template file that contains all the necessary actions needed to create the project. Plugins can also be executed within an existing Padrino application. The plugin generator provides a simple DSL in addition with leveraging Thor to make generating projects a breeze! ```shell $ padrino g project my_project --template path/to/my_template.rb ``` This will generate a project based on the template file provided. You can also generate a project based on a remote url such as a [gist](https://gist.github.com/ "gist") for an additional level of convenience: ```shell $ padrino g project my_project --template https://gist.github.com/356156 ``` You can also execute template files directly from [the official templates repo](http://github.com/padrino/padrino-recipes/tree/master/templates "the official templates repo"): ```shell $ padrino g project my_project --template sampleblog ``` You can also apply templates as plugins to existing Padrino applications: ```shell $ cd path/to/existing/padrino/app $ padrino g plugin path/to/my_plugin.rb ``` You can also execute plugin files directly from [the official plugins repo](https://github.com/padrino/padrino-recipes/tree/master/plugins/ "the official plugins repo"): ```shell $ cd path/to/existing/padrino/app $ padrino g plugin hoptoad ``` You can even get a list of available plugins with the following command: ```shell $ padrino g plugin --list ``` A simple template (plugin) file might look like this: ```ruby # my_template.rb project :test => :rspec, :orm => :activerecord generate 'model', 'account username:string password:string' generate 'model', 'post title:string body:text' generate 'controller', 'posts get:index get:new post:new' generate 'controller', 'users get:index' generate 'migration', 'AddEmailToAccount email:string' require_dependencies 'nokogiri' git :init git :add, "." git :commit, "-m 'initial commit'" inject_into_file 'app/models/post.rb','#Hello', :after => "end\n" rake 'ar:create ar:migrate' initializer :test, '# Example' git :add, '.' git :commit, "- m 'second commit'" ``` Keep in mind that the template file is pure Ruby and has full access to [all available thor actions](https://github.com/erikhuda/thor/blob/master/lib/thor/actions.rb "thor actions"). --- # Generators: Controllers # Controllers Padrino provides generator support for quickly creating new controllers within your Padrino application. Note that the controller tests are generated specifically tailored towards the testing framework chosen during application generation. Options | Default | Aliases | Description --------- | ------- | ------- | ---------------------------------------------- app | /app | -a | specify the application root | . | -r | specify the root destination namespace | | -n | specify the name space of your padrino project layout | | -l | specify the layout parent | | -p | specify the parent provides | | -f | specify the formats for this controller destroy | false | -d | removes all generated files Very important to note that controller generators are intended primarily to work within applications created through the Padrino application generator and that follow Padrino conventions. Using the controller generator is as simple as: ```shell $ padrino g controller Admin ``` If you want create a controller for a specified sub app you can: ```shell $ padrino g controller Admin -a my_sub_app ``` You can also specify desired actions to be added to your controller: ```shell $ padrino g controller Admin get:index get:new post:create ``` The controller generator will then construct the controller file within `app/controllers/admin.rb` and also a controller test file at `test/controllers/admin_controller_test.rb` according to the test framework chosen during app generation. A default route will also be generated mapping to name of the controller and the route name. For example: ```shell $ padrino g controller User get:index ``` will create a URL route for `:index` mapping to `/user`. You may also specify layout, parent and provides respectively: ```shell $ padrino g controller User -l global $ padrino g controller User -p users $ padrino g controller User -f :html,:json ``` You can destroy controllers that you created via the destroy option and setting it to true. Default is false. ```shell $ padrino g controller User -d ``` This removes all created controller files. --- # Generators: Models # Models Padrino provides generator support for quickly creating new models within your Padrino application. Note that the models (and migrations) generated are specifically tailored towards the ORM component and testing framework chosen during application generation. Options | Default | Aliases | Description --------------- | ------- | ------- | ---------------------------------------- root | . | -r | specify the root destination path app | . | -a | specify the application destination path skip\_migration | false | -s | skip migration generation destroy | false | -d | removes all generated files Very important to note that model generators are intended primarily to work within applications created through the Padrino application generator and that follow Padrino conventions. Using model generators within an existing application not generated by Padrino will likely not work as expected. Using the model generator is as simple as: ```shell $ padrino g model User ``` You can also specify desired fields to be contained within your `User` model: ```shell $ padrino g model User name:string age:integer email:string ``` The model generator will create multiple files within your application and based on your ORM component. Usually the model file will generate files similar to the following: - Model definition file (`models/user.rb`) - Migration declaration (`db/migrate/xxx_create_users.rb`) - Model unit test file (`test/models/user_test.rb`) You can define as many models as you would like in a Padrino application using this generator. You can destroy models that you created via the destroy option and setting it to true. Default is false. ```shell $ padrino g model User -d ``` This remove all created model files. --- # Generators: Migrations # Migrations Padrino provides generator for quickly generating new migrations to change or manipulate the database schema. These migrations generated will be tailored towards the ORM chosen when generating the application. Options | Default | Aliases | Description ------- | ------- | ------- | --------------------------------- root | . | -r | specify the root destination path destroy | false | -d | removes all generated files Very important to note that migration generators are intended primarily to work within applications created through the Padrino application generator and that follow Padrino conventions. Using migration generators within an existing application not generated by Padrino will likely not work as expected. Using the migration generator is as simple as: ```shell $ padrino g migration AddFieldsToUsers $ padrino g migration RemoveFieldsFromUsers ``` You can also specify desired columns to be added to the migration file: ```shell $ padrino g migration AddFieldsToUsers last_login:datetime crypted_password:string $ padrino g migration RemoveFieldsFromUsers password:string ip_address:string ``` The migration generator will then construct the migration file according to your ORM component chosen within `db/migrate/xxx_add_fields_to_users.rb` including the columns specified in the command. You can destroy migrations that you created via the destroy option and setting it to true. Default is false. ```shell $ padrino g migration AddFieldsToUsers -d ``` --- # Generators: Mailers # Mailers Padrino provides generator support for quickly creating new mailers within your Padrino application. Options | Default | Aliases | Description --------- | ------- | ------- | ---------------------------------------------- app | nil | -n | specify the application root | . | -r | specify the root destination path namespace | | -n | specify the name space of your padrino project destroy | false | -d | removes all generated files Very important to note that mailer generators are intended primarily to work within applications created through the Padrino application generator and that follow Padrino conventions. Using the mailer generator is as simple as: ```shell $ padrino g mailer UserNotifier ``` If you want create a mailer for a specified sub app you can: ```shell $ padrino g mailer UserNotifier -a my_sub_app ``` You can also specify desired delivery actions to be added to the mailer: ```shell $ padrino g mailer UserNotifier confirm_account welcome inactive_account ``` The mailer generator will then construct the mailer file within `app/mailers/user_notifier.rb` You can destroy mailer that you created via the destroy option and setting it to true. Default is false. ```shell $ padrino g mailer UserNotifier -d ``` This remove all created mailer files. --- # Generators: Sub-Applications # Sub-Applications Unlike other Ruby frameworks, Padrino is principally designed for mounting multiple apps at the same time. Options | Default | Aliases | Description ------- | ------- | ------- | --------------------------------- tiny | false | -i | generate tiny app skeleton root | . | -r | specify the root destination path destroy | false | -d | removes all generated files First you need to create a project: ```shell $ padrino g project demo_project $ cd demo_project ``` Now you are in `demo_project` and you can create your apps: ```shell $ padrino g app one $ padrino g app two ``` By default these apps are mounted under: - `/one` - `/two` fee free to change the routing in `config/apps.rb`. You can create controllers: ```shell $ padrino g controller base --app one # create controller for app one $ padrino g controller base # create controller for main app $ padrino g controller base --app two # create controller for app two ``` Or mailers: ```shell $ padrino g mailer registration --app one # create mailer for app one $ padrino g mailer registration # create mailer for main app $ padrino g mailer registration --app two # create mailer for app one ``` --- # Generators: Tiny Skeleton # Tiny Skeleton Both the Project Generator and Sub App Generator allow you to create an even smaller project skeleton. Instead of the default skeleton, the tiny option removes the need for a controllers, helpers, and mailers folder and instead generates `controllers.rb`, `helpers.rb`, and `mailers.rb` in its place. To use the tiny skeleton generator for project run: ```shell $ padrino g project tiny_app -d mongoid --tiny ``` To use the tiny skeleton generator for app run in your project: ```shell $ padrino g app tiny_app --tiny ``` --- # Generators: Admin # Admin Padrino also comes with a built-in admin dashboard. To generate the admin application in your project: Options | Default | Aliases | Description --------------- | --------- | ------- | ---------------------------------------------------- admin_name | admin | -a | allows you to specify the admin app’s name admin_model | "Account" | -m | specify the name of model for access controlling root | . | -r | specify the root destination path skip\_migration | false | -s | skip migration generation renderer | | -e | the default value is a renderer used in the main app destroy | false | -d | removes all generated files ```shell $ padrino g admin ``` This will generate the admin application and mount it at `/admin`. For more information, check out the [Admin Guide](/guides/features/padrino-admin "Admin Guide"). --- # Generators: Components # Components The available components and their default options are same as the Project Generator. Options | Default | Aliases | Description ------- | ------- | ------- | ------------------------------------------------ root | . | -r | the root destination path for the project adapter | sqlite | -a | specify orm db adapter (mysql, sqlite, postgres) ## Examples Show help and selected components: ```shell $ padrino g component ``` Add to `minirecord` with `mysql` and `rspec` in your project: ```shell $ padrino g component -d minirecord -a mysql2 -t rspec ``` --- # Generators: Tasks # Tasks Padrino provides generator for quickly generating new task for your app. Options | Default | Aliases | Description ----------- | ------- | ------- | ------------------------------------------------ root | . | -r | the root destination path for the project description | nil | -d | specify the description of your application task namespace | nil | -n | specify the namespace of your application task ## Examples Show help: ```shell $ padrino g task ``` Using the task generator is as simple as: ```shell $ padrino g task foo ``` Generate the task file with namespace and description options: ```shell $ padrino g task bar --namespace=sample --description="This\ is\ a\ sample" ``` --- # Controllers: Overview # Overview Suppose we wanted to add routes to our Padrino application, and we want to organize a set of related routes within a more structured grouping. Padrino has the notion of a 'controller' block which can group related routes and make URL generation much easier. Simply add a `controllers.rb` file or `app/controllers` folder and create a file as such: ```ruby # app/controllers/main.rb or controllers.rb SimpleApp.controller do get '/test' do 'Text to return' end get '/sample' do 'Sample Route' end end ``` In this case, the controller merely acts as a structured grouping mechanism to allow better organization of routes. Controllers actually have other benefits as well when used in conjunction with the enhanced Padrino routing system. - [Routing](/guides/controllers/routing "Routing") - [Layouts](/guides/controllers/layouts "Layouts") - [Provides Formats](/guides/controllers/provides-formats "Provides Formats") - [Route Filters](/guides/controllers/route-filters "Route Filters") - [Prioritized Routes](/guides/controllers/prioritized-routes "Prioritized Routes") - [Custom Conditions](/guides/controllers/custom-conditions "Custom Conditions") - [Parsing Params](/guides/controllers/parsing-params "Parsing Params") - [Rendering](/guides/controllers/rendering "Rendering") - [Sessions](/guides/controllers/sessions "Sessions") - [Params Whitelisting](/guides/controllers/params-whitelisting "Params Whitelisting") --- # Controllers: Routing # Routing Padrino provides advanced routing definition support to make routes and URL generation much easier. This routing system supports named route aliases and easy access to url paths. The benefits of this is that instead of having to hard-code route URLs into every area of your application, now we can just define the URLs in a single spot and then attach an alias which can be used to refer to the URL throughout the application. ## Basic Routing Aliases The routing system supports named aliases by using symbols instead of strings for your routes: ```ruby Demo::App.controllers :page do get :index do # url is generated as '/' # url_for(:index) => "/" end get :account, :with => :id do # url is generated as '/account/:id' # url_for(:account, id: 5) => "/account/5" # access params[:id] end end ``` These routes can then be referenced anywhere in the application: ```haml = link_to 'Index', url_for(:index) = link_to 'Account', url_for(:account, id: 1) ``` ## Inline Route Alias Definitions The routing plugin also supports inline route definitions in which the explicit URL and the named alias are both defined: ```ruby Demo::App.controllers :account do get :index, map: '/index/example' do # url_for(:index) => "/index/example" end get :account, map: '/the/accounts/:name/and/:id' do # url_for(:account, name: 'John', id: 5) => "/the/accounts/John/and/5" # access params[:name] and params[:id] end end ``` Routes defined inline this way can be accessed and treated the same way as traditional named aliases: ```haml = link_to 'Index Page', url_for(:index) = link_to 'Account Page', url_for(:account, id: 1) ``` ## Namespaced Route Aliases There is also support for namespaced routes which are organized into a named controller group: ```ruby Demo::App.controllers :admin do get :index do # url is generated as '/admin/' # url_for(:admin, :index) => "/admin" end get :show, map: "/admin/:id" do # url is generated as "/admin/#{params[:id]}" # url_for(:admin, :show, id: 5) => "/admin/5" end end ``` You can then reference these routes using the same `url_for` method: ```haml = link_to 'admin show page', url_for(:admin, :index) = link_to 'admin index page', url_for(:admin, :show, id: 25) ``` If you prefer explicit URLs to named aliases, that is also supported within a specified controller group: ```ruby Demo::App.controllers '/admin' do get '/show', name: :show do # url is generated as "/admin/show" end get '/other/:id', name: :other do # url is generated as "/admin/#{params[:id]}" end end ``` You can then reference these routes using the same `url_for` method: ```haml = link_to 'admin show page', url_for(:admin, :show) = link_to 'admin index page', url_for(:admin, :other, id: 25) ``` ## Named Parameters With Padrino you can also specify named parameters within your route definition: ```ruby Demo::App.controllers :admin do get :show, with: :id do # url is generated as "/admin/show/#{params[:id]}" # url_for(:admin, :show, id: 5) => "/admin/show/5" end get :other, with: [:id, :name] do # url is generated as "/admin/other/#{params[:id]}/#{params[:name]}" # url_for(:admin, :other, id: 5, name: "hey") => "/admin/other/5/hey" end end ``` You can then reference the URLs using the same `url_for` method: ```haml = link_to 'admin show page', url_for(:admin_show, id: 25) = link_to 'admin other page', url_for(:admin_other, id: 25, name: :foo) ``` ### Nested Routes You can specify parent resources in padrino with the `:parent` option on the controller: ```ruby Demo::App.controllers :product, parent: :user do get :index do # url is generated as "/user/#{params[:user_id]}/product" # url_for(:product, :index, user_id: 5) => "/user/5/product" end get :show, with: :id do # url is generated as "/user/#{params[:user_id]}/product/show/#{params[:id]}" # url_for(:product, :show, user_id: 5, id: 10) => "/user/5/product/show/10" end end ``` If need be the parent resource can also be specified on inline routes in addition: ```ruby Demo::App.controllers :product, parent: :user do get :index, parent: :project do # url is generated as "/user/#{params[:user_id]}/project/#{params[:project_id]}/product" # url(:product, :index, user_id: 5, project_id: 8) => "/user/5/project/8/product" end end ``` --- # Controllers: Layouts # Layouts With Padrino, a custom layout can be specified or the layout can be disabled altogether: ```ruby class SimpleApp < Padrino::Application # Disable layouts disable :layout # Use the layout located in views/layouts/custom.haml layout :custom end ``` Note that layouts are *scoped by controller*, so you can apply different layouts to different controllers: ```ruby SimpleApp.controllers :posts do # Apply a layout for routes in this controller # Layout file would be in 'app/views/layouts/posts.haml' layout :posts get('/posts') { render :haml, 'Uses posts layout' } end SimpleApp.controllers :accounts do # Padrino allows you to apply a different layout for this controller # Layout file would be in 'app/views/layouts/accounts.haml' layout :accounts get('/accounts') { render :haml, 'Uses accounts layout' } end ``` If necessary, you also can overwrite the layout for a given route: ```ruby SimpleApp.controllers :admin do get :index do render 'admin/index', layout: :admin end get :show, with: :id do render 'admin/show', layout: false end end ``` --- # Controllers: Provides Format # Provides Formats With Padrino you can simply declare which formats a request will respond to by using the `provides` route configuration: ```ruby Demo::App.controllers :admin do get :show, with: :id, provides: :js do # url is generated as "/admin/show/#{params[:id]}.#{params[:format]}" # url_for(:admin, :show, id: 5, format: :js) => "/admin/show/5.js" end get :other, with: [:id, :name], provides: [:html, :json] do case content_type when :js then ... when :json then ... end end end ``` These formatted route paths can be accessed easily using `url_for` and then `format` option: ```haml = link_to 'admin show page', url_for(:admin, show, id: 25, format: :js) = link_to 'admin other page', url_for(:admin, index, id: 25, name: :foo) = link_to 'other json', url(:admin, index, id: 25, name: :foo, format: :json) ``` --- # Controllers: Params Whitelisting # Params Whitelisting We can provide a set of whitelisted params for a given request with: ```ruby Demo::App.controllers :admin do get :show, map: "show", params: [:foo, :bar] do # Only accepts params "foo" and "bar". All other params are removed. end end ``` --- # Controllers: Route Filters # Route Filters Before filters are evaluated before each request within the context of the request and can modify the request and response. Instance variables set in filters are accessible by routes and templates: ```ruby before do @note = 'Hi!' end ``` After filters are evaluated after each request within the context of the request and can also modify the request and response. Instance variables set in before filters and routes are accessible by after filters: ```ruby after do puts @note end ``` This is now standard in Sinatra, but Padrino adds support for filters being _scoped by controller_ which means that unlike Sinatra in which a filter is global, in Padrino you can run different filters for each controller: ```ruby Demo::App.controllers :posts do before { @foo = 'bar' } get('/posts') { render :haml, 'Has access to @foo variable' } end Demo::App.controllers :accounts do before { @bar = 'foo' } get('/accounts') { render :haml, 'Has access to @bar variable' } end ``` This allows for more fine-grained filters and prevents the need to have unnecessary filters running on every route. As of Padrino 0.10.0, there is also a much more powerful route selection system that has been setup: ```ruby Demo::App.controllers :example do # Based on a symbol before :index do # Code here to be executed end # Based on a symbol, regexp and string all in one before :index, /main/, '/example' do # Code here to be executed end # Also filter by excluding an action before :except => :index do # Code here to be executed end get :index do # ... end end ``` This gives developers a lot more flexibility when running filters and enables much more selective execution in a convenient way. --- # Controllers: Prioritized Routes # Prioritized Routes Padrino (0.10.0+) has added support for respecting route order in controllers and also allows the developer to specify certain routes as less or more "important" than others in the route recognition order. Consider two controllers, the first with a "catch-all" route: ```ruby # app/controllers/pages.rb Demo::App.controllers :pages do get :show, map: '/*page' do 'Catchall route' end end # app/controllers/projects.rb Demo::App.controllers :projects do get :index do 'Index' end end ``` This wouldn't work by default because the second "/projects" endpoint would be eclipsed by the "/*page" catch-all route and as such `projects` would not be accessible. To solve this, you can do the following: ```ruby # app/controllers/pages.rb Demo::App.controllers :pages do # NOTE that this route is now marked as low priority get :show, map: '/*page', priority: :low do 'Catchall route' end end # app/controllers/projects.rb Demo::App.controllers :projects do get :index do 'Index' end end ``` When setting a routes priority to `:low`, this route is then recognized lower than all "high" and "normal" priority routes. You are encouraged in cases where there is ambiguity, to mark key routes as `priority: :high` or catch-all routes as `priority: :low` in order to guarantee expected behavior. --- # Controllers: Custom Conditions # Custom Conditions Padrino has support for Sinatra's custom route conditions as well. This allows you to apply custom condition checks to evaluate before a route is executed for an incoming request: ```ruby Demo::App.controllers :projects do def protect(*args) condition do unless username == 'foo' && password == 'bar' halt 403, 'Not Authorized' end end end get '/', protect: true do 'Only foo can see this' end end ``` Conditions can also be specified at the controller and route levels: ```ruby # You can specify conditions to run for all routes: Demo::App.controllers :projects, conditions: { protect: true } do def self.protect(protected) condition do halt 403, 'No secrets for you!' unless params[:key] == 's3cr3t' end if protected end # This route will only return "secret stuff" if the user goes to # `/private?key=s3cr3t`. get('/private') { 'secret stuff' } # And this one, too! get('/also-private') { 'secret stuff' } # But you can override the conditions for each route as needed. # This route will be publicly accessible without providing the # secret key. get :index, protect: false do 'Welcome!' end end ``` This gives the developer considerable power to construct arbitrarily complex route conditions and apply them to any route within their application. --- # Controllers: Parsing Params # Parsing Params Padrino is often used for web service applications. One common need these applications have is to parse incoming messages, typically JSON or XML. This data will come as part of the request's body instead of the typical form data approach, i.e. url parameters or multipart form data. Here's when [Rack::Parser](https://github.com/achiu/rack-parser "Rack::Parser") comes in handy since it will do just that. In `app/app.rb` or in `config.ru` just add: ```ruby use Rack::Parser, content_types: { 'application/json' => lambda { |body| ::MultiJson.decode body } } ``` Now all of your controllers will have the request's body JSON object parse as inside `params` and you would be able to do something along the lines of: ```ruby post '/people' order = Person.new(name: params['name'] ) # ... end ``` Have a look at [this great Sinatra recipe](http://recipes.sinatrarb.com/p/middleware/rack_parser?#article "this great Sinatra recipe") for a more detailed guide of how this middleware works. --- # Controllers: Rendering # Rendering Unlike Sinatra, Padrino supports automatic template engine lookups with: ```ruby # searches for 'account/index.{erb,haml,...} render 'account/index' ``` It will choose the first one that is discovered, without regards to the type of rendering (erb, haml, slim). Otherwise you can explicitly specify the type of rendering of your choice (erb, haml, slim). ```ruby # will use example.haml render :haml, 'account/index' ``` Padrino also automatically considers your current locale and/or content_type. ```ruby Demo::App.controllers :admin do get :show, with: :id, provides: [:html, :js] do render "admin/show" end end ``` When you visit the `:show` route with `I18n.locale == :ru` enabled, Padrino will first try to look for "admin/show.ru.js.*" if nothing matches that criteria, it will try "admin/show.ru.*" then "admin/show.js.*". As a last resort, if he finds nothing matching your criteria, it will return "admin/show.erb" (or admin/show.haml) --- # Controllers: Sessions # Sessions _Kindly borrowed from Sinatra's docs :)_ A session is used to keep state during requests. If activated, you have one session hash per user session: ```ruby enable :sessions get '/' do "value = #{session[:value].inspect}" end get '/:value' do session[:value] = params[:value] end ``` Note that `enable :sessions` actually stores all data in a cookie. This might not always be what you want (storing lots of data will increase your traffic, for instance). You can use any Rack session middleware: in order to do so, do **not** call `enable :sessions`, but instead pull in your middleware of choice as you would any other middleware: ```ruby use Rack::Session::Pool, expire_after: 2592000 get '/' do "value = #{session[:value].inspect}" end get '/:value' do session[:value] = params[:value] end ``` To improve security, the session data in the cookie is signed with a session secret. A random secret is generated for you by Sinatra. However, since this secret will change with every start of your application, you might want to set the secret yourself, so all your application instances share it: ```ruby set :session_secret, 'super secret' ``` If you want to configure it further, you may also store a hash with options in the `sessions` setting: ```ruby set :sessions, domain: 'foo.com' ``` To share your session across other apps on subdomains of foo.com, prefix the domain with a `.` like this instead: ```ruby set :sessions, domain: '.foo.com' ``` --- # Application Helpers: Overview # Overview This component provides a great deal of view helpers related to HTML markup generation. There are helpers for generating tags, forms, links, images, and more. Most of the basic methods should be very familiar to anyone who has used Rails view helpers. - [Output Helpers](/guides/application-helpers/output-helpers "Output Helpers") - [Tag Helpers](/guides/application-helpers/tag-helpers "Tag Helpers") - [Asset Helpers](/guides/application-helpers/asset-helpers "Asset Helpers") - [Form Helpers](/guides/application-helpers/form-helpers "Form Helpers") - [Form Builders](/guides/application-helpers/form-builders "Form Builders") - [Standard Form Builder](/guides/application-helpers/standard-form-builder "Standard Form Builder") - [Custom Form Builders](/guides/application-helpers/custom-form-builders "Custom Form Builders") - [Nested Object Form Support](/guides/application-helpers/nested-object-form-support "Nested Object Form Support") - [Format Helpers](/guides/application-helpers/format-helpers "Format Helpers") - [Render Helpers](/guides/application-helpers/render-helpers "Render Helpers") - [Custom Helpers](/guides/application-helpers/custom-helpers "Custom Helpers") - [Unobtrusive Javascript Helpers](/guides/application-helpers/ujs-helpers "Unobtrusive Javascript Helpers") --- # Application Helpers: Output Helpers # Output Helpers Output helpers are a collection of important methods for managing, capturing and displaying output in various ways and is used frequently to support higher-level helper functions. There are three output helpers worth mentioning: `content_for`, `capture_html`, and `concat_content` The `content_for` functionality supports capturing content and then rendering this into a different place such as within a layout. One such popular example is including assets onto the layout from a template: ```erb # app/views/site/index.erb # ... <% content_for :assets do %> <%= stylesheet_link_tag 'index', 'custom' %> <% end %> # ... ``` Added to a template, this will capture the includes from the block and allow them to be yielded into the layout: ```erb # app/views/layout.erb Example <%= stylesheet_link_tag 'style' %> <%= yield_content :assets %> ``` This will automatically insert the contents of the block (in this case a stylesheet include) into the location the content is yielded within the layout. You can also check if a `content_for` block exists for a given key using `content_for?`: ```erb # app/views/layout.erb <% if content_for?(:assets) %>
<%= yield_content :assets %>
<% end %> ``` The `capture_html` and the `concat_content` methods allow content to be manipulated and stored for use in building additional helpers accepting blocks or displaying information in a template. One example is the use of these in constructing a simplified `form_tag` helper which accepts a block. ```ruby # form_tag '/register' do ... end def form_tag(url, options = {}, &block) # ... truncated ... inner_form_html = capture_html(&block) concat_content '
' + inner_form_html + '
' end ``` This will capture the template body passed into the `form_tag` block and then append the content to the template through the use of `concat_content`. Note have been built to work for both haml and erb templates using the same syntax. ## List of Output Helpers - `content_for(key, &block)` - Capture a block of content to be rendered at a later time. - Existence can be checked using the `content_for?(key)` method. - `content_for(:head) { ...content... }` - Also supports arguments passed to the content block - `content_for(:head) { |param1, param2| ...content... }` - `yield_content(key, *args)` - Render the captured content blocks for a given key. - `yield_content :head` - Also supports arguments yielded to the content block - `yield_content :head, param1, param2` - `capture_html(*args, &block)` - Captures the html from a block of template code for erb or haml - `capture_html(&block)` => "...html..." - `concat_content(text = '')` - Outputs the given text to the templates buffer directly in erb or haml - `concat_content("This will be output to the template buffer in erb or haml")` --- # Application Helpers: Tag Helpers # Tag Helpers Tag helpers are the basic building blocks used to construct html 'tags' within a view template. There are three major functions for this category: `tag`, `content_tag` and `input_tag`. The `tag` and `content_tag` are for building arbitrary html tags with a name and specified options. If the tag contains 'content' within then `content_tag` is used. For example: ```erb tag(:br, style: 'clear:both') # =>
content_tag(:p, 'demo', class: 'light') # =>

demo

``` The `input_tag` is used to build tags that are related to accepting input from the user: ```erb input_tag :text, class: 'demo' # => input_tag :password, value: 'secret', class: 'demo' ``` Note that all of these accept html options and result in returning a string containing html tags. ## List of Tag Helpers - `tag(name, options = nil, open = false)` - Creates an html tag with the given name and options - `tag(:br, style: 'clear:both', open: true)` => `
` - `content_tag(name, content, options = nil, &block)` - Creates an html tag with given name, content and options - `content_tag(:p, 'demo', class: 'light')` => `

demo

` - `content_tag(:p, class: 'dark') { ...content... }` => `

...content...

` - `input_tag(type, options = {})` - Creates an html input field with given type and options - `input_tag :text, class: 'demo'` - `input_tag :password, value: 'secret', class: 'demo'` --- # Application Helpers: Asset Helpers # Asset Helpers Asset helpers are intended to help insert useful html onto a view template such as 'flash' notices, hyperlinks, mail\_to links, images, stylesheets and javascript. An example of their uses would be on a simple view template: ```haml # app/views/example.haml ... %head = stylesheet_link_tag 'layout' = javascript_include_tag 'application' = favicon_tag 'images/favicon.png' %body ... = flash_tag :notice %p= link_to 'Blog', '/blog', class: 'example' %p Mail me at #{mail_to 'fake@faker.com', 'Fake Email Link', cc: "test@demo.com"} %p= image_tag 'padrino.png', width: '35', class: 'logo' ``` By default, all 'assets' including images, scripts, and stylesheets have a timestamp appended at the end to clear the stale cache for the item when modified. To disable this, simply put the setting `disable :asset_stamp` in your application configuration within `app/app.rb`. ## List of Asset Helpers - `flash_tag(kind, options = {})` - Creates a div to display the flash of given type if it exists - `flash_tag(:notice, class: 'flash', id: 'flash-notice')` - `link_to(*args, &block)` - Creates a link element with given name, url and options - `link_to 'click me', '/dashboard', class: 'linky'` - `link_to 'click me', '/dashboard', class: 'linky', if: @foo.present?` - `link_to 'click me', '/dashboard', class: 'linky', unless: @foo.blank?` - `link_to 'click me', '/dashboard', class: 'linky', unless: :current` - `link_to('/dashboard', class: 'blocky') { ...content... }` - `mail_to(email, caption = nil, mail_options = {})` - Creates a mailto link tag to the specified email_address - `mail_to 'me@demo.com'` - `mail_to 'me@demo.com', 'My Email', subject: 'Feedback', cc: 'test@demo.com'` - `image_tag(url, options = {})` - Creates an image element with given url and options - `image_tag('icons/avatar.png')` - `stylesheet_link_tag(*sources)` - Returns a stylesheet link tag for the sources specified as arguments - `stylesheet_link_tag 'style', 'application', 'layout'` - `javascript_include_tag(*sources)` - Returns an html script tag for each of the sources provided. - `javascript_include_tag 'application', 'special'` - `favicon_tag(source, options = {})` - Returns a favicon tag for the header for the source specified. - `favicon_tag 'images/favicon.ico', type: 'image/ico'` - `feed_tag(mime, source, options = {})` - Returns a feed tag for the mime and source specified - `feed_tag :atom, url(:blog, :posts, format: :atom), title: 'ATOM'` --- # Application Helpers: Form Helpers # Form Helpers Form helpers are the 'standard' form tag helpers you would come to expect when building forms. A simple example of constructing a non-object form would be: ```haml # app/views/example.haml = form_tag '/destroy', class: 'destroy-form', method: 'delete' do = flash_tag(:notice) = field_set_tag do %p = label_tag :username, class: 'first' = text_field_tag :username, value: params[:username] %p = label_tag :password, class: 'first' = password_field_tag :password, value: params[:password] %p = label_tag :strategy = select_tag :strategy, options: ['delete', 'destroy'], selected: 'delete' %p = check_box_tag :confirm_delete = field_set_tag(class: 'buttons') do = submit_tag 'Remove' ```
## List of Form Helpers - `form_tag(url, options = {}, &block)` - Constructs a form without object based on options - Supports form methods 'put' and 'delete' through hidden field - `form_tag('/register', class: 'example') { ... }` - `field_set_tag(*args, &block)` - Constructs a field_set to group fields with given options - `field_set_tag(class: 'office-set') { }` - `field_set_tag('Office', class: 'office-set') { }` - `error_messages_for(:record, options = {})` - Constructs list html for the errors for a given object - `error_messages_for :user` - `label_tag(name, options = {}, &block)` - Constructs a label tag from the given options - `label_tag :username, class: 'long-label'` - `label_tag(:username, class: 'blocked-label') { ... }` - `hidden_field_tag(name, options = {})` - Constructs a hidden field input from the given options - `hidden_field_tag :session_key, value: 'secret'` - `text_field_tag(name, options = {})` - Constructs a text field input from the given options - `text_field_tag :username, class: 'long'` - `text_area_tag(name, options = {})` - Constructs a text area input from the given options - `text_area_tag :username, class: 'long'` - `password_field_tag(name, options = {})` - Constructs a password field input from the given options - `password_field_tag :password, class: 'long'` - `number_field_tag(name, options = {})` - Constructs a number field input from the given options - `number_field_tag :age, class: 'long'` - `telephone_field_tag(name, options = {})` - Constructs a phone field input from the given options - `telephone_field_tag :mobile, class: 'long'` - `email_field_tag(name, options = {})` - Constructs a email field input from the given options - `email_field_tag :email, class: 'long'` - `search_field_tag(name, options = {})` - Constructs a search field input from the given options - `search_field_tag :query, class: 'long'` - `url_field_tag(name, options = {})` - Constructs a url field input from the given options - `url_field_tag :image_source_url, class: 'long'` - `check_box_tag(name, options = {})` - Constructs a checkbox input from the given options - `check_box_tag :remember_me, checked: true` - `radio_button_tag(name, options = {})` - Constructs a radio button input from the given options - `radio_button_tag :gender, value: 'male'` - `select_tag(name, settings={})` - Constructs a select tag with options from the given settings - `select_tag(:favorite_color, options: ['1', '2', '3'], selected: '1')` - `select_tag(:more_color, options: [['label', '1'], ['label2', '2']])` - `select_tag(:multiple_color, options: ['1', '2', '3'], multiple: true, selected: ['1', '3'])` - `file_field_tag(name, options = {})` - Constructs a file field input from the given options - `file_field_tag :photo, class: 'long'` - `submit_tag(caption, options = {})` - Constructs a submit button from the given options - `submit_tag 'Create', class: 'success'` - `button_tag(caption, options = {})` - Constructs an input (type => 'button') from the given options - `button_tag 'Cancel', class: 'clear'` - `image_submit_tag(source, options = {})` - Constructs an image submit button from the given options - `image_submit_tag 'submit.png', class: 'success'` --- # Application Helpers: Form Builders # Form Builders Form builders are full-featured objects allowing the construction of complex object-based forms using a simple, intuitive syntax. A `form_for` using these basic fields might look like: ```haml = form_for @user, '/register', id: 'register' do |f| = f.error_messages %p = f.label :username, caption: "Nickname" = f.text_field :username %p = f.label :email = f.text_field :email %p = f.label :password = f.password_field :password %p = f.label :is_admin, caption: "Admin User?" = f.check_box :is_admin %p = f.label :color, caption: "Favorite Color?" = f.select :color, options: ['red', 'black'] %p = fields_for @user.location do |location| = location.text_field :street = location.text_field :city %p = f.submit 'Create', class: 'button' ``` ## Form Builder Helpers - `form_for(object, url, settings = {}, &block)` - Constructs a form using given or default `form_builder` - Supports form methods 'put' and 'delete' through hidden field - Defaults to `StandardFormBuilder` but you can [easily create your own](/guides/application-helpers/custom-form-builders "Custom Form Builders")! - `form_for(@user, '/register', id: 'register') { |f| ...field-elements... }` - `form_for(:user, '/register', id: 'register') { |f| ...field-elements... }` - `fields_for(object, settings = {}, &block)` - Constructs fields for a given object for use in an existing form - Defaults to StandardFormBuilder but you can easily create your own! - `fields_for @user.assignment do |assignment| ... end` - `fields_for :assignment do |assignment| ... end` Some of the methods provided by `AbstractFormBuilder` that can be used within `form_for` or `fields_for` are: - `error_messages(options = {})` - Displays list html for the errors on form object - `f.error_messages` - `label(field, options = {})` - `f.label :name, class: 'long'` - `text_field(field, options = {})` - `f.text_field :username, class: 'long'` - `check_box(field, options = {})` - Uses hidden field to provide a 'unchecked' value for field - `f.check_box :remember_me, uncheck_value: 'false'` - `radio_button(field, options = {})` - `f.radio_button :gender, value: 'male'` - `hidden_field(field, options = {})` - `f.hidden_field :session_id, class: 'hidden'` - `text_area(field, options = {})` - `f.text_area :summary, class: 'long'` - `password_field(field, options = {})` - `f.password_field :secret, class: 'long'` - `number_field(field, options = {})` - `f.number_field :age, class: 'long'` - `telephone_field(field, options = {})` - `f.telephone_field :mobile, class: 'long'` - `email_field(field, options = {})` - `f.email_field :email, class: 'long'` - `search_field(field, options = {})` - `f.search_field :query, class: 'long'` - `url_field(field, options = {})` - `f.url_field :image_source, class: 'long'` - `file_field(field, options = {})` - `f.file_field :photo, class: 'long'` - `select(field, options = {})` - `f.select(:state, options: ['California', 'Texas', 'Wyoming'])` - `f.select(:state, collection: @states, fields: [:name, :id])` - `f.select(:state, options: [...], include_blank: true)` - `submit(caption, options = {})` - `f.submit 'Update', class: 'long'` - `image_submit(source, options = {})` - `f.image_submit 'submit.png', class: 'long'` - `date_field(field, options = {})` - `f.date_field :time_start, class: 'input'` For a complete list checkout [the form helper docs](http://www.rubydoc.info/gems/padrino-helpers/Padrino/Helpers/FormHelpers "Form Helper Docs"). ## Localization See [localization](/guides/features/localization/). --- # Application Helpers: Standard Form Builder # Standard Form Builder There is also an additional StandardFormBuilder which builds on the abstract fields that can be used within a `form_for`. A `form_for` using these standard fields might be: ```haml = form_for @user, '/register', id: 'register' do |f| = f.error_messages = f.text_field_block :name, caption: 'Full name' = f.text_field_block :email = f.check_box_block :remember_me = f.select_block :fav_color, options: ['red', 'blue'] = f.password_field_block :password = f.submit_block 'Create', class: 'button' ``` and would generate this html: ```html
...omitted...
``` ## List of Standard Form Builder Helpers The following are fields provided by StandardFormBuilder that can be used within a `form_for` or `fields_for`: - `text_field_block(field, options = {}, label_options = {})` - `text_field_block(:nickname, class: 'big', caption: "Username")` - `text_area_block(field, options = {}, label_options = {})` - `text_area_block(:about, class: 'big')` - `password_field_block(field, options = {}, label_options = {})` - `password_field_block(:code, class: 'big')` - `file_field_block(field, options = {}, label_options = {})` - `file_field_block(:photo, class: 'big')` - `check_box_block(field, options = {}, label_options = {})` - `check_box_block(:remember_me, class: 'big')` - `select_block(field, options = {}, label_options = {})` - `select_block(:country, option: ['USA', 'Canada'])` - `submit_block(caption, options = {})` - `submit_block(:username, class: 'big')` - `image_submit_block(source, options = {})` - `image_submit_block('submit.png', class: 'big')` --- # Application Helpers: Custom Form Builders # Custom Form Builders You can also easily build your own FormBuilder which allows for customized fields and behavior: ```ruby class MyCustomFormBuilder < AbstractFormBuilder # Here we have access to a number of useful variables # # ** template (use this to invoke any helpers)(ex. template.hidden_field_tag(...)) # ** object (the record for this form) (ex. object.valid?) # ** object_name (object's underscored type) (ex. object_name => 'admin_user') # # We also have access to self.field_types => [:text_field, :text_area, ...] # In addition, we have access to all the existing field tag # helpers (text_field, hidden_field, file_field, ...) end ``` Once a custom builder is defined, any call to `form_for` can use the new builder: ```haml = form_for @user, '/register', builder: 'MyCustomFormBuilder', id: 'register' do |f| ...fields here... ``` The form builder can even be made into the default builder when `form_for` is invoked: ```ruby # anywhere in the Padrino or Sinatra application set :default_builder, 'MyCustomFormBuilder' ``` And there you have it, a fairly complete form builder solution for Padrino (and Sinatra). --- # Application Helpers: Nested Object Form Support # Nested Object Form Support Nested This allows forms to have arbitrarily complex nested forms that can build multiple related objects together. Let's take a simple example of a person with an address. Here are the related pseudo models: ```ruby class Person < ORM::Base has_many :addresses, class_name: 'Address' accepts_nested_attributes_for :addresses, allow_destroy: true end class Address < ORM::Base belongs_to :person end ``` The model declarations are dependent on your chosen ORM. Check the documentation to understand how to declare nested attributes in your given ORM component. Given those models and enabling nested attributes for the association, the following view will allow nested form creation: ```haml = form_for @person, '/person/create' do |f| = f.text_field :name = f.text_field :favorite_color = f.fields_for :addresses do |address_form| = address_form.label :street = address_form.text_field :street = address_form.label :city = address_form.text_field :city - unless address_form.object.new_record? = address_form.check_box '_destroy' = address_form.label '_destroy', caption: 'Remove' = submit_tag 'Save' ``` This will present a form that allows the person's name and color to be set along with their first address. Using this functionality, the controller does not need to change whatsoever as the nested data will be passed in and instantiated as part of the parent model. --- # Application Helpers: Format Helpers # Format Helpers Format helpers are several useful utilities for manipulating the format of text to achieve a goal. The four format helpers are `escape_html`, `distance_of_time_in_words`, `time_ago_in_words`, and `js_escape_html`. The `escape_html` and `js_escape_html` function are for taking an html string and escaping certain characters. `escape_html` will escape ampersands, brackets and quotes to their HTML/XML entities. This is useful to sanitize user content before displaying this on a template. `js_escape_html` is used for passing javascript information from a js template to a javascript function. ```ruby escape_html('&') # => <hello>&<goodbye> ``` There is also an alias for `escape_html` called `h` for even easier usage within templates. Format helpers also includes a number of useful text manipulation functions such as `simple_format`, `pluralize`, `word_wrap`, and `truncate`. ```ruby simple_format("hello\nworld") # => "

hello
world

" pluralize(2, 'person') => '2 people' word_wrap('Once upon a time', line_width: 8) => "Once upon\na time" truncate("Once upon a time in a world far far away", length: 8) => "Once upon..." truncate_words("Once upon a time in a world far far away", length: 4) => "Once upon a time..." highlight('Lorem dolor sit', 'dolor') => "Lorem dolor sit" ``` These helpers can be invoked from any route or view within your application. ## List of Format Helpers - `simple_format(text, html_options)` - Returns text transformed into HTML using simple formatting rules. - `simple_format("hello\nworld")` => `"

hello
world

"` - `pluralize(count, singular, plural = nil)` - Attempts to pluralize the singular word unless count is 1. - `pluralize(2, 'person')` => '2 people' - `word_wrap(text, *args)` - Wraps the text into lines no longer than line_width width. - `word_wrap('Once upon a time', line_width: 8)` => "Once upon\na time" - `truncate(text, *args)` - Truncates a given text after a given `:length` if text is longer than `:length` (defaults to 30). - `truncate("Once upon a time in a world far far away", length: 8)` => "Once upon..." - `truncate_words(text, *args)` - Truncates a given text after a given :length of total words (defaults to 30). - `truncate_words("Once upon a time in a world far far away", length: 4) => "Once upon a time..."` - `highlight(text, words, *args)` - Highlights one or more words everywhere in text by inserting it into a `:highlighter` string. - `highlight('Lorem ipsum dolor sit amet', 'dolor')` - `escape_html` (alias `h` and `h!`) - (from RackUtils) Escape ampersands, brackets and quotes to their HTML/XML entities. - `strip_tags(html)` - Remove all html tags and return only a clean text. - `distance_of_time_in_words(from_time, to_time = 0)` - Returns relative time in words referencing the given date - `distance_of_time_in_words(2.days.ago)` => "2 days" - `distance_of_time_in_words(5.minutes.ago)` => "5 minutes" - `distance_of_time_in_words(2800.days.ago)` => "over 7 years" - `time_ago_in_words(from_time)` - Returns relative time in words from the current date - `time_ago_in_words(2.days.ago)` => "2 days" - `time_ago_in_words(1.day.from_now)` => "tomorrow" - `js_escape_html(html_content)` - Escapes html to allow passing information to javascript. Used for passing data inside an ajax .js.erb template. - `js_escape_html('

Hey

')` --- # Application Helpers: Render Helpers # Render Helpers This component provides a number of rendering helpers making the process of displaying templates a bit easier. This plugin also has support for useful additions such as partials (with support for `:collection`) for the templating system. Using render plugin helpers is extremely simple. If you want to render an erb template in your view path: ```ruby render :erb, 'path/to/erb/template' ``` or using haml templates works just as well: ```ruby render :haml, 'path/to/haml/template' ``` There is also a method which renders the first view matching the path and removes the need to define an engine: ```ruby render 'path/to/any/template' ``` It is worth noting these are mostly for convenience. With nested view file paths in Sinatra, this becomes tiresome: ```ruby haml :"the/path/to/file" erb '/path/to/file'.to_sym ``` Finally, we have the all-important partials support for rendering mini-templates onto a page: ```ruby partial 'photo/item', object: @photo, locals: { foo: 'bar' } partial 'photo/item', collection: @photos ``` ## List of Render Helpers - `render(engine, data, options, locals)` - Renders the specified template with the given options - `render 'user/new'` - `render :erb, 'users/new', layout: false` - `partial(template, *args)` - Renders the html related to the partial template for object or collection - `partial 'photo/item', object: @photo, locals: { foo: 'bar' }` - `partial 'photo/item', collection: @photos` --- # Application Helpers: Custom Helpers # Custom Helpers In addition to the helpers provided by Padrino out of the box, you can also add your own helper methods and classes that will be accessible within any controller or view automatically. To define a helper method, simply use an existing helper file (created when generating a controller) or define your own file in `app/helpers` within your application. Methods can be made available within you controller by simply wrapping the methods in the `helpers` block: ```ruby MyAppName.helpers do def some_method # ...do something here... end end ``` You can also define entire classes for use as helpers just as easily: ```ruby class SomeHelper def self.do_something # ...do something here... end end ``` These helpers can then easily be invoked in any controllers or templates within your application: ```ruby MyAppName.controllers :posts do get :index do some_method # helper method SomeHelper.do_something # helper class end end ``` Use these in situations where you wish to cleanup your controller or your view code. Helpers are particularly useful for DRY'ing up repeated use of the same markup or behavior. **Note** that helper methods and objects should be reloaded automatically for you in development. --- # Application Helpers: Unobtrusive JavaScript Helpers # Unobtrusive JavaScript Helpers Certain helpers have certain unobtrusive JavaScript options that are available to be used with any of the javascript adapters packaged with padrino. Once your app has been [generated](/guides/generators/overview "generated") with a particular javascript adapter, you can utilize the baked in support with the `link_to` and `form_for` tags. ## Remote Forms To generate a 'remote' form in a view: ```haml = form_for :user, url(:create, format: :js), remote: true do |f| .content=partial '/users/form' ``` which will generate the following unobtrusive markup: ```html
``` ```ruby # /app/controllers/users.rb post :create, provides: :js do @user = User.new(params[:user]) if @user.save "$('form.content').html('#{partial("/users/form")}');" else "alert('User is not valid');" end end ``` A remote form, when submitted by the user, invokes an xhr request to the specified url (with the appropriate form parameters) and then evaluates the response as javascript. ## Remote Links To generate a 'remote' link in a view: ```ruby link_to "add item", url(:items, :new, format: :js), remote: true ``` which will generate the following unobtrusive markup: ```html add item ``` A remote link, when clicked by the user, invokes an xhr request to the specified url and then evaluates the response as javascript. ## Link Confirmations To generate a 'confirmation' link in a view: ```ruby link_to "delete item", url(:items, :destroy, format: :js), confirm: 'Are You Sure?' ``` which will generate the following unobtrusive markup: ```html [destroy] ``` A link with confirmation, when clicked by the user, displays an alert box confirming the action before invoking the link. ## Custom Method Links To generate a 'method' link in a view: ```ruby link_to "logout", url(:session, :destroy, format: :js), method: :delete ``` which will generate the following unobtrusive markup: ```html [destroy] ``` A link with a custom method, when clicked by the user, visits the link using the http method specified rather than via the 'GET' method. ## Enabling UJS Adapter **Note**: In order for the unobtrusive javascript to work, you must be sure to include the chosen javascript framework and ujs adapter in your views (or layout). For instance, if I selected jquery for my project: ```haml -# /apps/views/layouts/application.haml = javascript_include_tag 'jquery', 'jquery-ujs', 'application' ``` This will ensure jquery and the jquery ujs adapter are properly loaded to work with the helpers listed above. --- # Adding Components: Overview # Overview Padrino is an agnostic web framework. This means that the framework has been built from the ground up to easily allow support for any arbitrary number of different developer choices with respect to object permanence, stylesheet templates, JavaScript libraries, testing libraries, mocking libraries and rendering engines. For a detailed overview of the available components, check out the [Generators guide](/guides/generators/overview "generators guide"). Although Padrino is fundamentally agnostic, in practice only a very limited set of available components have actually been integrated into the Padrino generator and admin dashboard. The set of available components is determined by libraries actually used or noted by the core developers and the existing community. However, adding additional components to Padrino is not only possible but highly recommended. In fact, this is possibly _the best_ way for a developer to get started [contributing to Padrino](/contribute "contributing to Padrino"). The following guides will outline in detail how to properly contribute new components to Padrino and get them included into the next Padrino generator as quickly and efficiently as possible. - [Persistence Engine](/guides/adding-components/persistence-engine "Persistence Engine") - [Javascript Library](/guides/adding-components/javascript-engine "Javascript Library") - [Testing Library](/guides/adding-components/testing-library "Testing Library") - [Rendering Engine](/guides/adding-components/rendering-engine "Rendering Engine") - [Mocking Library](/guides/adding-components/mocking-library "Mocking Library") - [Stylesheet Engine](/guides/adding-components/stylesheet-engine "Stylesheet Engine") - [Locale Translations](/guides/adding-components/locale-translations "Locale Translations") --- # Adding Components: Persistence Engine # Persistence Engine Contributing an object persistence library is probably the most involved component to integrate with Padrino. For this guide, let us pretend that we would like to integrate [Datamapper](http://datamapper.org) into Padrino. ## Generators First, let's add Datamapper to the project generator's available components in [padrino-gen/generators/project.rb](https://github.com/padrino/padrino-framework/blob/master/padrino-gen/lib/padrino-gen/generators/project.rb): ```ruby # padrino-gen/lib/padrino-gen/generators/project.rb component_option :orm, "database engine", choices: [:activerecord, :datamapper] ``` Here, we needed to append `:datamapper` as an option for the `:orm` `component_option` in the project generator. Once we have defined Datamapper as an option for the ORM component, let's actually define the specific integration tasks for the generator in [padrino-gen/generators/components/orms/datamapper.rb](https://github.com/padrino/padrino-framework/blob/master/padrino-gen/lib/padrino-gen/generators/components/orms/datamapper.rb): ```ruby # padrino-gen/lib/padrino-gen/generators/components/orms/datamapper.rb # These are the steps to setup the persistence layer in the initial project such # as requiring certain gems, constructing the database.rb configuration file and # creating the models folder for the application def setup_orm require_dependencies 'data_objects', 'do_sqlite3', 'datamapper' create_file('config/database.rb', DM) empty_directory('app/models') end # These are the steps to generate the actual model file # when the model generator is executed. # # e.g. create_model_file("account", ["username:string", "password:string"]) def create_model_file(name, fields) # ...truncated... create_file(model_path, model_contents) end # These are the steps to generate the model migration file # when the model generator is executed. # # e.g. create_model_migration("create_accounts", "account", ["username:string"]) def create_model_migration(migration_name, name, columns) # ...truncated... end # These are the steps to generate the db migration file # when the migration generator is executed. # # e.g. create_migration_file("AddEmailToAccount", "AddEmailToAccount", ["email:string"]) def create_migration_file(migration_name, name, columns) # ...truncated... end ``` ## Rake Tasks Next, if the persistence engine needs to include useful rake tasks (to migrate or modify the database for instance), you can add these to the `padrino-tasks` folder in the `padrino-gen` gem. For Datamapper, there are a number of tasks that should be available in [padrino-tasks/datamapper.rb](https://github.com/padrino/padrino-framework/blob/master/padrino-gen/lib/padrino-gen/padrino-tasks/datamapper.rb): ```ruby # padrino-gen/lib/padrino-gen/padrino-tasks/datamapper.rb if defined?(DataMapper) namespace :dm do namespace :migrate do task load: :environment do # ...truncated... end desc 'Migrate up using migrations' task :up, :version, needs: :load do |t, args| # ...truncated... end end end end ``` ## Unit Tests Next, let's add the appropriate unit tests to ensure our new component works as intended in [padrino-gen/test/test_project_generator.rb](https://github.com/padrino/padrino-framework/blob/master/padrino-gen/test/test_project_generator.rb#L359): ```ruby # padrino-gen/test/test_project_generator.rb ... it 'should properly generate default' do out, err = capture_io { generate(:project, 'project.com', "--root=#{@apptmp}", '--orm=datamapper', '--script=none') } assert_match(/applying.*?datamapper.*?orm/, out) assert_match_in_file(/gem 'dm-core'/, "#{@apptmp}/project.com/Gemfile") assert_match_in_file(/gem 'dm-sqlite-adapter'/, "#{@apptmp}/project.com/Gemfile") assert_match_in_file(/DataMapper.setup/, "#{@apptmp}/project.com/config/database.rb") assert_match_in_file(/project_com/, "#{@apptmp}/project.com/config/database.rb") end ... ``` ## README Finally for the generator integration, we should add the available option to the [generator README file](https://github.com/padrino/padrino-framework/blob/master/padrino-gen/README.rdoc): ```ruby # padrino-gen/README.rdoc orm:: none (default), mongomapper, mongoid, activerecord, sequel, couchrest, datamapper ``` With that update to the README, persistence support for the generator is complete. However, to be fully compliant, support for Padrino Admin should also be added. This will allow the admin dashboard to work properly with your persistence engine of choice and is **highly** recommended. ## Padrino Admin Support Adding `padrino-admin` support for your persistence engine is actually fairly straightforward. First, let's add Datamapper to the set of supported admin ORM engines in [padrino-admin/generators/actions.rb](https://github.com/padrino/padrino-framework/blob/master/padrino-admin/lib/padrino-admin/generators/actions.rb#L29): ```ruby # padrino-admin/lib/padrino-admin/generators/actions.rb def supported_orm [:activerecord, :mongomapper, :mongoid, :couchrest, :datamapper] end ``` Next, we need to define the interaction methods available by our persistence engine on our models in [padrino-admin/generators/orm.rb](https://github.com/padrino/padrino-framework/blob/master/padrino-admin/lib/padrino-admin/generators/orm.rb): ```ruby # padrino-admin/lib/padrino-admin/generators/orm.rb module Padrino module Admin module Generators class OrmError < StandardError; end class Orm attr_reader :klass_name, :klass, :name_plural, :name_singular, :orm def initialize(name, orm, columns = nil, column_fields = nil) # ...truncated... end # Defines access to a model's columns def columns @columns ||= case orm when :activerecord then @klass.columns when :datamapper then @klass.properties else raise OrmError, "Adapter #{orm} is not yet supported!" end end # Defines access to retrieving all existing records for a model. def all "#{klass_name}.all" end # Defines access for querying records for a model. def find(params = nil) case orm when :activerecord then "#{klass_name}.find(#{params})" when :datamapper then "#{klass_name}.get(#{params})" else raise OrmError, "Adapter #{orm} is not yet supported!" end end # Defines how to build a new record for a model. def build(params = nil) if params "#{klass_name}.new(#{params})" else "#{klass_name}.new" end end # Defines how to save a new record for a model. def save "#{name_singular}.save" end # Defines how to update attributes of a record for a model. def update_attributes(params = nil) case orm when :activerecord then "#{name_singular}.update_attributes(#{params})" when :datamapper then "#{name_singular}.update(#{params})" else raise OrmError, "Adapter #{orm} is not yet supported!" end end # Defines how to destroy a record for a model. def destroy "#{name_singular}.destroy" end end # Orm end # Generators end # Admin end # Padrino ``` Next, we need to describe how the `Account` model should be defined for our persistence engine within [padrino-admin/generators/templates/account/datamapper.rb.tt](https://github.com/padrino/padrino-framework/blob/master/padrino-admin/lib/padrino-admin/generators/templates/account/datamapper.rb.tt): ```ruby # padrino-admin/lib/padrino-admin/generators/templates/account/datamapper.rb.tt class Account include DataMapper::Resource include DataMapper::Validate attr_accessor :password, :password_confirmation # Define Properties property :id, Serial property :name, String # ...truncated... # Define Validations validates_present :email, :role # ...truncated... # Callbacks before :save, :generate_password # # This method is for authentication purpose # def self.authenticate(email, password) account = first(conditions: { email: email }) if email.present? account && account.password_clean == password ? account : nil end # # This method is used from AuthenticationHelper # def self.find_by_id(id) get(id) rescue nil end # # This method is used for retrieve the original password. # def password_clean crypted_password.decrypt(salt) end private def generate_password return if password.blank? self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{email}--") if new? self.crypted_password = password.encrypt(self.salt) end def password_required crypted_password.blank? || !password.blank? end end ``` Finally, let's update the `padrino-admin` README file at [padrino-admin/README.rdoc](https://github.com/padrino/padrino-framework/blob/master/padrino-admin/README.rdoc) to reflect our newly support component: ```ruby # padrino-admin/README.rdoc Orm Agnostic:: Data Adapters for Datamapper, Activerecord, Mongomapper, Mongoid, Couchrest, Dynamoid ``` ## Contribute to Padrino This completes the full integration of a persistence engine into Padrino. Once all of this has been finished in your GitHub fork, send us a pull request and assuming you followed these instructions properly and the engine actually works when generated, we will include the component into the next Padrino version crediting you for the contribution! --- # Adding Components: JavaScript Engine # JavaScript Engine Contributing an additional JavaScript library to Padrino is actually quite straightforward. For this guide, let's assume we want to add `extcore` as a JavaScript component integrated into Padrino. ## Generators First, let's define the actual integration of the javascript into the generator in [padrino-gen/generators/components/scripts/extcore.rb](https://github.com/padrino/padrino-framework/blob/master/padrino-gen/lib/padrino-gen/generators/components/scripts/extcore.rb): ```ruby # padrino-gen/lib/padrino-gen/generators/components/scripts/extcore.rb def setup_script begin get('https://raw.github.com/padrino/padrino-static/master/js/ext.js', destination_root('/public/javascripts/ext.js')) rescue copy_file('templates/static/js/ext.js', destination_root('/public/javascripts/ext.js')) end create_file(destination_root('/public/javascripts/application.js'), '// Put your application scripts here') end ``` This will copy the script into the `public/javascripts` folder of a newly generated project and construct the `application.js` file. Next, let's copy the latest version of the javascript library to the templates folder: ```javascript // padrino-gen/lib/padrino-gen/generators/templates/scripts/ext-core.js // ...truncated javascript library code here... ``` ## Tests Let's also add a test to ensure the new JavaScript component generates as expected in [padrino-gen/test/test\_project\_generator.rb](https://github.com/padrino/padrino-framework/blob/master/padrino-gen/test/test_project_generator.rb#L517): ```ruby # padrino-gen/test/test_project_generator.rb it 'should properly generate for ext-core' do out, err = capture_io { generate(:project, 'sample_project', "--root=#{@apptmp}", '--script=extcore') } assert_match(/applying.*?extcore.*?script/, out) assert_file_exists("#{@apptmp}/sample_project/public/javascripts/ext.js") assert_file_exists("#{@apptmp}/sample_project/public/javascripts/ext-ujs.js") assert_file_exists("#{@apptmp}/sample_project/public/javascripts/application.js") end ``` ## README Finally, let's update the README for `padrino-gen` to reflect the new component in [padrino-gen/README.rdoc](https://github.com/padrino/padrino-framework/blob/master/padrino-gen/README.rdoc): ```ruby # padrino-gen/README.rdoc script:: none (default), jquery, prototype, mootools, extcore, dojo ``` ## Unobtrusive JavaScript Adapter Although optional, you can also provide a unobtrusive JavaScript (UJS) adapter which provides 'remote' and 'method' support to a project using a particular JavaScript framework. For more information about UJS, check out the [UJS Helpers](/guides/application-helpers/ujs-helpers/) guide. To support UJS in a given JavaScript framework, simply create a new file such as 'jquery-ujs' in your [padrino-static](https://github.com/padrino/padrino-static) fork and then follow the UJS [adapter template](https://github.com/padrino/padrino-static/blob/master/ujs/jquery.js) used by the existing implementation. ```javascript // ujs/jquery-ujs.js /* Remote Form Support * form_for @user, '/user', :remote => true **/ $("form[data-remote=true]").live('submit', function(e) { // ... }); /* Confirmation Support * link_to 'sign out', '/logout', :confirm => "Log out?" * Link Remote Support * link_to 'add item', '/create', :remote => true * Link Method Support * link_to 'delete item', '/destroy', :method => :delete **/ /* JSAdapter */ var JSAdapter = { // Sends an xhr request to the specified url with given verb and params // JSAdapter.sendRequest(element, { verb: 'put', url : '...', params: {} }); sendRequest : function(element, options) { // ... }, // Triggers a particular method verb to be triggered in a form posting to the url // JSAdapter.sendMethod(element); sendMethod : function(element) { // ... } }; ``` Generally the only changes need to be made in the `JSAdapter` JavaScript module specifically to implement the `sendRequest` and `sendMethod` functions that are used by all the events to power the UJS functionality. Once that unobtrusive adapter has been implemented, you can finish by adding the UJS file to the generator in Padrino: ```ruby # padrino-gen/lib/padrino-gen/generators/components/scripts/extcore.rb def setup_script begin get('https://raw.github.com/padrino/padrino-static/master/ujs/ext.js', destination_root('/public/javascripts/ext-ujs.js')) rescue copy_file('templates/static/ujs/ext.js', destination_root('/public/javascripts/ext-ujs.js')) end create_file(destination_root('/public/javascripts/application.js'), '// Put your application scripts here') end ``` and update the [tests](https://github.com/padrino/padrino-framework/blob/master/padrino-gen/test/test_project_generator.rb#L483): ```ruby # padrino-gen/test/test_project_generator.rb describe "the generator for script component" do it 'should properly generate for jquery' do out, err = capture_io { generate(:project, 'sample_project', "--root=#{@apptmp}", '--script=jquery') } assert_match(/applying.*?jquery.*?script/, out) assert_file_exists("#{@apptmp}/sample_project/public/javascripts/jquery.js") assert_file_exists("#{@apptmp}/sample_project/public/javascripts/jquery-ujs.js") assert_file_exists("#{@apptmp}/sample_project/public/javascripts/application.js") end ... end ``` ## Contribute to Padrino This completes the full integration of a JavaScript library into Padrino. Once all of this has been finished in your GitHub fork, send us a pull request and assuming you followed these instructions properly and the library actually works when generated, we will include the component into the next Padrino version crediting you for the contribution! An example of the [actual commit](https://github.com/padrino/padrino-framework/commit/43fb57dd39fa9d860873c14840e68281e314abb8) of the `extcore` JavaScript library is a great example of how to contribute to Padrino. --- # Adding Components: Testing Library # Testing Library Contributing an additional testing library to Padrino is actually quite straightforward. For this guide, let's assume we want to add `shoulda` as a testing component integrated into Padrino. ## Generators First, let's define the actual integration of the testing library into the generator in [padrino-gen/generators/components/tests/shoulda.rb](https://github.com/padrino/padrino-framework/blob/master/padrino-gen/lib/padrino-gen/generators/components/tests/shoulda.rb): ```ruby # padrino-gen/lib/padrino-gen/generators/components/tests/shoulda.rb SHOULDA_SETUP = (<<-TEST).gsub(/^ {10}/, '') unless defined?(SHOULDA_SETUP) RACK_ENV = 'test' unless defined?(RACK_ENV) require File.expand_path(File.dirname(__FILE__) + "/../config/boot") Dir[File.expand_path(File.dirname(__FILE__) + "/../app/helpers/**/*.rb")].each(&method(:require)) require 'test/unit' class Test::Unit::TestCase include Rack::Test::Methods # You can use this method to custom specify a Rack app # you want rack-test to invoke: # # app CLASS_NAME # app CLASS_NAME.tap { |a| } # app(CLASS_NAME) do # set :foo, :bar # end # def app(app = nil, &blk) @app ||= block_given? ? app.instance_eval(&blk) : app @app ||= Padrino.application end end TEST SHOULDA_RAKE = (<<-TEST).gsub(/^ {10}/, '') unless defined?(SHOULDA_RAKE) require 'rake/testtask' test_tasks = Dir['test/*/'].map { |d| File.basename(d) } test_tasks.each do |folder| Rake::TestTask.new("test:\#{folder}") do |test| test.pattern = "test/\#{folder}/**/*_test.rb" test.verbose = true end end desc "Run application test suite" task 'test' => test_tasks.map { |f| "test:\#{f}" } TEST def setup_test require_dependencies 'rack-test', require: 'rack/test', group: 'test' require_dependencies 'shoulda', group: 'test' insert_test_suite_setup SHOULDA_SETUP create_file destination_root('test/test.rake'), SHOULDA_RAKE end # Generates a controller test given the controllers name def generate_controller_test(name) # ...truncated... end def generate_model_test(name) # ...truncated... end ``` ## Tests Let's also add a test to ensure the new testing component generates as expected in [padrino-gen/test/test\_project\_generator.rb](https://github.com/padrino/padrino-framework/blob/master/padrino-gen/test/test_project_generator.rb#L580): ```ruby # padrino-gen/test/test_project_generator.rb it 'should properly generate for shoulda' do out, err = capture_io { generate(:project, 'sample_project', "--root=#{@apptmp}", '--test=shoulda', '--script=none') } assert_match(/applying.*?shoulda.*?test/, out) assert_match_in_file(/gem 'rack-test'/, "#{@apptmp}/sample_project/Gemfile") assert_match_in_file(/:require => 'rack\/test'/, "#{@apptmp}/sample_project/Gemfile") assert_match_in_file(/:group => 'test'/, "#{@apptmp}/sample_project/Gemfile") assert_match_in_file(/gem 'shoulda'/, "#{@apptmp}/sample_project/Gemfile") assert_match_in_file(/RACK_ENV = 'test' unless defined\?\(RACK_ENV\)/, "#{@apptmp}/sample_project/test/test_config.rb") assert_match_in_file(/Test::Unit::TestCase/, "#{@apptmp}/sample_project/test/test_config.rb") assert_file_exists("#{@apptmp}/sample_project/test/test.rake") assert_match_in_file(/Rake::TestTask\.new\("test:\#/,"#{@apptmp}/sample_project/test/test.rake") assert_match_in_file(/task 'test' => test_tasks/,"#{@apptmp}/sample_project/test/test.rake") end ``` ## README Finally, let's update the README for `padrino-gen` to reflect the new component in [padrino-gen/README.rdoc](https://github.com/padrino/padrino-framework/blob/master/padrino-gen/README.rdoc): ```ruby # padrino-gen/README.rdoc test:: none (default), bacon, shoulda, cucumber, rspec, minitest ``` ## Contribute to Padrino This completes the full integration of a testing library into Padrino. Once all of this has been finished in your GitHub fork, send us a pull request and assuming you followed these instructions properly and the library actually works when generated, we will include the component into the next Padrino version crediting you for the contribution! --- # Adding Components: Rendering Engine # Rendering Engine Contributing a rendering engine to Padrino is actually quite straightforward. For this guide, let's assume we want to add `haml` as a rendering engine integrated into Padrino. First let's add rendering engine into the generator in [padrino-gen/generators/components/renderers/haml.rb](http://github.com/padrino/padrino-framework/blob/master/padrino-gen/lib/padrino-gen/generators/components/renderers/haml.rb): ```ruby # padrino-gen/lib/padrino-gen/generators/components/renderers/haml.rb def setup_renderer require_dependencies 'haml' end ``` Let's also add a test to ensure the new rendering component generates as expected in [padrino-gen/test/test\_project\_generator.rb](http://github.com/padrino/padrino-framework/blob/master/padrino-gen/test/test_project_generator.rb#L450): ```ruby # padrino-gen/test/test_project_generator.rb should "properly generate for haml" do buffer = silence_logger { @project.start(['sample_project', '--root=/tmp', '--renderer=haml','--script=none']) } assert_match /Applying.*?haml.*?renderer/, buffer assert_match_in_file(/gem 'haml'/, '/tmp/sample_project/Gemfile') end ``` and finally let's update the README for `padrino-gen` to reflect the new component in [padrino-gen/README.rdoc](http://github.com/padrino/padrino-framework/blob/master/padrino-gen/README.rdoc): ```ruby # padrino-gen/README.rdoc renderer:: erb (default), haml ``` When adding support for a new rendering engine, you are highly encouraged to also include support for this engine within the `padrino-admin` gem. This admin gem constructs views and forms based on templates provided for each supported renderer. When adding a new renderer, be sure to add templates for each of the necessary admin views. The necessary templates and structure can be found in the [padrino-admin/generators/templates/haml](http://github.com/padrino/padrino-framework/tree/master/padrino-admin/lib/padrino-admin/generators/templates/haml/) views folder. Be sure to implement all of these if you want the integrated rendering engine to work with the admin dashboard. Finally, let's update the `padrino-admin` README file at [padrino-admin/README.rdoc](http://github.com/padrino/padrino-framework/blob/master/padrino-admin/README.rdoc) to reflect our newly support component: ```ruby # padrino-admin/README.rdoc Template Agnostic:: Erb and Haml Renderer ``` This completes the full integration of a rendering engine into Padrino. Once all of this has been finished in your github fork, send us a pull request and assuming you followed these instructions properly and the engine actually works when generated, we will include the component into the next Padrino version crediting you for the contribution! --- # Adding Components: Mocking Library # Mocking Library Contributing an additional mocking library to Padrino is actually quite straightforward. For this guide, let's assume we want to add [Mocha](https://github.com/freerange/mocha) as a mocking component integrated into Padrino. ## Generators First, let's define the actual integration of the mocking library into the generator in [padrino-gen/generators/components/mocks/mocha.rb](https://github.com/padrino/padrino-framework/blob/master/padrino-gen/lib/padrino-gen/generators/components/mocks/mocha.rb): ```ruby # padrino-gen/lib/padrino-gen/generators/components/mocks/mocha.rb def setup_mock require_dependencies 'mocha', :group => 'test', :require => false case options[:test].to_s when 'rspec' inject_into_file 'spec/spec_helper.rb', " conf.mock_with :mocha\n", :after => "RSpec.configure do |conf|\n" else inject_into_file 'test/test_config.rb', "require 'mocha/api'\n", :after => "require File.expand_path(File.dirname(__FILE__) + \"/../config/boot\")\n" insert_mocking_include 'Mocha::API' end end ``` ## Tests Let's also add a test to ensure the new mocking component generates as expected in [padrino-gen/test/test\_project\_generator.rb](https://github.com/padrino/padrino-framework/blob/master/padrino-gen/test/test_project_generator.rb#L248): ```ruby # padrino-gen/test/test_project_generator.rb it 'should properly generate for mocha and rspec' do out, err = capture_io { generate(:project, 'sample_project', "--root=#{@apptmp}", '--test=rspec', '--mock=mocha', '--script=none') } assert_match(/applying.*?mocha.*?mock/, out) assert_match_in_file(/gem 'mocha'/, "#{@apptmp}/sample_project/Gemfile") assert_match_in_file(/conf.mock_with :mocha/m, "#{@apptmp}/sample_project/spec/spec_helper.rb") end ``` ## README Finally, let's update the README for `padrino-gen` to reflect the new component in [padrino-gen/README.rdoc](https://github.com/padrino/padrino-framework/blob/master/padrino-gen/README.rdoc): ```ruby # padrino-gen/README.rdoc mock:: none (default), mocha, rr ``` ## Contribute to Padrino This completes the full integration of a mocking library into Padrino. Once all of this has been finished in your GitHub fork, send us a pull request and assuming you followed these instructions properly and the library actually works when generated, we will include the component into the next Padrino version crediting you for the contribution! --- # Adding Components: Stylesheet Engine # Stylesheet Engine Contributing an additional stylesheet engine to Padrino is actually quite straightforward. For this guide, let's assume we want to add [Less](http://lesscss.org) as a stylesheet engine component integrated into Padrino. ## Generators First, let's define the actual integration of the stylesheet engine into the generator in [padrino-gen/generators/components/stylesheets/less.rb](https://github.com/padrino/padrino-framework/blob/master/padrino-gen/lib/padrino-gen/generators/components/stylesheets/less.rb): ```ruby # padrino-gen/lib/padrino-gen/generators/components/stylesheets/less.rb LESS_INIT = <<-LESS unless defined?(LESS_INIT) # Enables support for Less template reloading for rack. # Store Less files by default within 'app/stylesheets/'. # See http://github.com/kelredd/rack-less for more details. require 'rack/less' # optional - use as necessary Rack::Less.configure do |config| config.compress = true # config.cache = true # other configs ... end app.use Rack::Less, :root => Padrino.root, :source => 'app/stylesheets', :public => 'public', :hosted_at => 'stylesheets' LESS def setup_stylesheet require_dependencies 'less' require_dependencies 'rack-less' require_dependencies 'therubyracer' initializer :less, LESS_INIT empty_directory destination_root('/app/stylesheets') end ``` ## Tests Let's also add a test to ensure the new stylesheet engine component generates as expected in [padrino-gen/test/test\_project\_generator.rb](https://github.com/padrino/padrino-framework/blob/master/padrino-gen/test/test_project_generator.rb#L656): ```ruby # padrino-gen/test/test_project_generator.rb it 'should properly generate for less' do capture_io { generate(:project, 'sample_project', "--root=#{@apptmp}", '--renderer=haml','--script=none','--stylesheet=less') } assert_match_in_file(/gem 'rack-less'/, "#{@apptmp}/sample_project/Gemfile") assert_match_in_file(/module LessInitializer.*Rack::Less/m, "#{@apptmp}/sample_project/lib/less_initializer.rb") assert_match_in_file(/register LessInitializer/m, "#{@apptmp}/sample_project/app/app.rb") assert_dir_exists("#{@apptmp}/sample_project/app/stylesheets") end ``` ## README Finally, let's update the README for `padrino-gen` to reflect the new component in [padrino-gen/README.rdoc](https://github.com/padrino/padrino-framework/blob/master/padrino-gen/README.rdoc): ```ruby # padrino-gen/README.rdoc stylesheet:: none (default), less, compass, sass, scss ``` ## Contribute to Padrino This completes the full integration of a stylesheet engine into Padrino. Once all of this has been finished in your GitHub fork, send us a pull request and assuming you followed these instructions properly and the engine actually works when generated, we will include the component into the next Padrino version crediting you for the contribution! --- # Adding Components: Locale Translations # Locale Translations In addition to components, we also encourage developers to send us their locale translations allowing Padrino to support a wide variety of different languages. In order to add locale translations, simply port the following YAML files to your favorite language. For this example, let's port over Padrino to Russian. The following YAML files must be translated: - [padrino-helpers/locale/ru.yml](https://github.com/padrino/padrino-framework/blob/master/padrino-helpers/lib/padrino-helpers/locale/ru.yml) - [padrino-admin/locale/admin/ru.yml](https://github.com/padrino/padrino-framework/blob/master/padrino-admin/lib/padrino-admin/locale/admin/ru.yml) - [padrino-admin/locale/orm/ru.yml](https://github.com/padrino/padrino-framework/blob/master/padrino-admin/lib/padrino-admin/locale/orm/ru.yml) ## Contribute to Padrino This completes the full integration of a new locale into Padrino. Once all of this has been finished in your GitHub fork, send us a pull request and assuming you followed these instructions properly and the language has proper translations, we will include the locale into the next Padrino version crediting you for the contribution! An example of the [actual commit](https://github.com/padrino/padrino-framework/commit/64465d1835cf32996bc36bb14ed9fd1c21e3cd76) of the Russian locale translations are a great example of how to contribute to Padrino. --- # Advanced Usage: Overview # Overview Sometimes plain Padrino is not enough. Learn how to extend Padrino, doing things async, and how to create an API. - [Extending Padrino Projects](/guides/advanced-usage/extending-padrino-projects "Extending Padrino Projects") - [Asynchronous Concurrency](/guides/advanced-usage/asynchronous-concurrency-with-padrino "Asynchronous Concurrency") - [Grape with Padrino](/guides/advanced-usage/grape-with-padrino "Grape with Padrino") - [Standalone Usage in Sinatra](/guides/advanced-usage/standalone-usage-in-sinatra "Standalone Usage in Sinatra") - [3rd Party Plugins](/guides/advanced-usage/3rd-party-plugins "3rd Party Plugins") - [Running Padrino on JRuby](/guides/advanced-usage/running-padrino-on-jruby "Running Padrino on JRuby") --- # Advanced Usage: Extending Padrino Projects # Extending Padrino Projects As you being building real projects with Padrino, you will quickly require additional functionality not built into Padrino itself. Thankfully finding and using modular libraries that add additional functionality to your Sinatra and Padrino applications is quite painless. ## Managing Dependencies with Bundler In order to manage dependencies for a project, Padrino relies on another gem called [Bundler](http://bundler.io/ "Bundler"). A default Padrino `Gemfile` looks akin to this: ```ruby # Gemfile source 'https://rubygems.org' # Project requirements gem 'rake' # ...more gems... # Padrino Stable Gem gem 'padrino', '~> 0.16.1' ``` All dependencies and libraries required for your project should be declared in the Bundler `Gemfile` which is generated automatically with every Padrino application. Once the dependencies have been declared, simply run Bundler's install command: ```shell $ bundle ``` This will resolve and install all the required libraries. Check out the [Bundler documentation](http://bundler.io/v1.11/man/bundle.1.html "Bundler documentation") for more details about how this works. ## Padrino Recipes The best extensions to look for are those that have already been included as "recipes" in our [padrino-recipes](https://github.com/padrino/padrino-recipes "padrino-recipes") repository. Any recipe included there represents a single command installation of the specified functionality. For instance, suppose you want to setup pagination in your application for a resource. Installing the [will_paginate](https://github.com/mislav/will_paginate "will paginate") gem is as simple as applying the recipe: ```shell $ padrino g plugin will_paginate ``` This will install that gem into your project (and Gemfile) automatically. ## Discovering Libraries If you can't find a recipe in the [padrino-recipes](http://github.com/padrino/padrino-recipes "padrino-recipes") repository, then be sure to search for Rack middlewares or Sinatra compatible libraries to use in your app. - [Rack Middlewares](https://github.com/rack/rack/wiki/List-of-Middleware "Rack Middlewares") - [Sinatra Extensions](http://www.sinatrarb.com/extensions-wild.html "Sinatra Extensions") - [Padrino Extensions](https://github.com/padrino/padrino-framework/wiki/Extensions "Padrino Extensions") - [Padrino Integrations](https://github.com/padrino/padrino-framework/wiki/Integrations "Padrino Integrations") And, of course, never underestimate searching [GitHub](https://github.com "GitHub") to find Rack or Sinatra compatible repositories. Any Rack/Sinatra/Padrino library will generally work seamlessly in Padrino projects. Consider [adding a recipe](http://github.com/padrino/padrino-recipes "adding a recipe") for your favorite extensions! --- # Advanced Usage: 3rd Party Plugins # 3rd Party Plugins Padrino is a modular framework. As such, you can leverage other libraries such as Sinatra libraries which complement Padrino quite well. ## Rendering JSON with sinatra-contrib First you must reference [sinatra-contrib](https://github.com/sinatra/sinatra-contrib) in your Gemfile: ```ruby # Gemfile source 'https://rubygems.org' # Project requirements gem 'rake' # Component requirements gem 'haml' # Test requirements # Padrino Stable Gem gem 'padrino', '~> 0.16.1' # Or Padrino Edge # gem 'padrino', github: 'padrino/padrino-framework' # Or Individual Gems # %w(core support gen helpers cache mailer admin).each do |g| # gem 'padrino-' + g, '~> 0.16.1' # ends gem 'sinatra-contrib' ``` You may use the #json method after you have registered the sinatra helper: ```ruby module MyJsonApp class App < Padrino::Application register Padrino::Mailer register Padrino::Helpers helpers Sinatra::JSON enable :sessions get '/' do hash = { foo: 'bar' } json hash end end end ``` --- # Advanced Usage: Asyncronous Concurrency # Asynchronous Concurrency > **⚠ Outdated Guide:** The libraries recommended below (Goliath, sinatra-synchrony) > are abandoned and incompatible with modern Padrino (Sinatra 4 / Rack 3). For > concurrent request handling, use [Puma](https://puma.io/) (the recommended > default server) or [Falcon](https://github.com/socketry/falcon) instead. > This guide is preserved for historical reference only. Lately, the Ruby community has become fascinated by asynchronous and concurrent web servers, the newest of which is called [Goliath](http://www.igvita.com/2011/03/08/goliath-non-blocking-ruby-19-web-server "Goliath"). This can be advantageous for your application especially if you have a lot of traffic and slow IO or Database calls (like HTTP calls to external APIs) since this substantially increases the number of clients your application can serve per process. This guide is dedicated to documenting how to achieve non-blocking, asynchronous requests while still using Sinatra and Padrino. For a more detailed guide be sure to checkout the [Sinatra Synchrony](https://github.com/kyledrake/sinatra-synchrony "Sinatra Synchrony") docs. ## Setup Add the gem to you Gemfile: ```ruby # Gemfile gem "sinatra-synchrony" ``` And then add the synchrony library to your Padrino application: ```ruby # app/app.rb require 'sinatra/synchrony' class DemoApp < Padrino::Application register Sinatra::Synchrony end ``` And that is really all you need for the basics. Also, you may want to take a look at [JRuby](http://jruby.org "JRuby") as an alternative ruby runtime. (Note: Rubinius is no longer maintained.) ## Benchmarks Added to Gemfile: ```ruby # Gemfile gem 'rest-client' gem 'sinatra-synchrony' gem 'faraday' ``` And the benchmark app: ```ruby # app/app.rb require 'sinatra/synchrony' require 'rest-client' require 'faraday' Faraday.default_adapter = :em_synchrony class DemoApp < Padrino::Application register Sinatra::Synchrony get '/' do Faraday.get 'http://google.com' end end ``` And results with `ab`: ``` $ ab -c 100 -n 100 http://127.0.0.1:9292/ ... Time taken for tests: 0.256 seconds ``` For a perspective, this operation took 33 seconds without this extension in thin. --- # Advanced Usage: Grape with Padrino # Grape with Padrino Create your new project. It can be a regular one: ```shell $ padrino g project grappe ``` Or a lean one if you don't want any Sinatra apps in it: ```shell $ padrino g project grappe --lean ``` Go to the new `grappe` folder and add `gem 'grape'` to `Gemfile`. Then install the required gems: ```shell $ bundle ``` Create file `api/api.rb` and put your Grape code there: ```ruby module Grappe class API < Grape::API get :hello do { hello: 'grape' } end get 'status' do cookies[:status_count] ||= 0 cookies[:status_count] = cookies[:status_count].to_i + 1 { status_count: cookies[:status_count] } end end end ``` Go to file `config/apps.rb` and add to the end of it: ```ruby Padrino.mount('API', app_file: Padrino.root('api/api.rb'), app_class: 'Grappe::API').to('/master') ``` Now you can run `rackup` from your `gapp` folder and visit or ```html grape 1 ``` You can find the code on [Github](https://github.com/padrino/grape-example "Github"). --- # Advanced Usage: Running Padrino on JRuby # Running Padrino on JRuby You can run Padrino (0.9.29 ... 0.16.1 is tested) on JRuby -------------------------------------------------------------------------------- ## Install on JRuby You can easily install Padrino on jruby when you use [RVM](https://rvm.io/rvm/install "RVM"): ```shell $ rvm install jruby-latest $ rvm use --create jruby-latest@padrino $ gem install bundler $ gem install padrino -v=0.16.1 ``` Create Padrino project just as you do when using MRI or REE: ```shell $ padrino g project jrack-test -e erb ``` `cd ./jrack-test` and you should edit `Gemfile`: ```ruby # JRuby deployment requirements # please add these lines... gem 'jruby-openssl' gem 'jruby-rack' gem 'warbler' ``` Now you can go: ```shell $ bundle ``` Then, create the test controller: ```shell $ padrino gen controller index get:index get:hello get:show_path ``` A controller sample is here: ```ruby # app/controllers/index.rb JrackTest::App.controllers :index do get :index do "Hello, JPadrino!" end get :hello, map: '/:id' do "Hello, #{params[:id]}!" end get :show_path, map: '/show-path/*urls' do "You accessed: #{params[:urls].inspect}" end end ``` Then run: ```shell $ padrino s ``` To run JRuby on 1.9 compat mode: > **Note:** Modern JRuby (9.x+) defaults to Ruby 2.6+ compatibility and no > longer requires the `--1.9` flag. The example below applies only to legacy > JRuby 1.7.x. ```shell alias padrino='jruby --1.9 -S padrino' padrino start ``` You can access as you run padrino on MRI... -------------------------------------------------------------------------------- ## How to create WAR Now you should install the `warbler` gem with `$ gem install warbler`, so you can: ```shell $ warble config ``` Edit `config/warble.rb` if you want to apply some customizations. You can access [JRuby-Rack official README](https://github.com/jruby/jruby-rack) and [Warbler rdoc](http://www.rubydoc.info/github/jruby/warbler). For example, if you want to deploy the app to server root directory, just add to `config/warble.rb`: ```ruby config.jar_name = "jrack-test" ``` Deploying with JRuby on 1.9 compat mode (obsolete for modern JRuby 9.x+, which defaults to Ruby 2.6+ compatibility): ```ruby config.webxml.jruby.compat.version = "1.9" ``` If you are ready, run: ```shell $ warble war ``` You would get `jrack-test.war` (the same name as your project directory name), and you can deploy this war file to tomcat! I tested on tomcat 6.0.20, and it works well with quick response. --- # Advanced Usage: Standalone Usage in Sinatra # Standalone Usage in Sinatra Padrino is by default a full-stack framework which provides a large number of enhancements to Sinatra and uses a new base application `Padrino::Application`. However, there are clearly times when even Padrino itself is far too 'heavyweight' for an application. In these instances, the ideal situation would be to cherry-pick individual enhancements and use them in your existing Sinatra application. Fortunately, Padrino is committed to allowing you to do exactly that! Each major component within Padrino can be used in isolation and applied to an existing Sinatra application. This guide will walk you through that process for each component. You can also find some examples [here](https://github.com/padrino/padrino-integration/tree/master/fixtures/single-apps "link to padrino single-apps"). -------------------------------------------------------------------------------- ## Padrino Helpers This component provides a great deal of view helpers related to html markup generation. There are helpers for generating tags, forms, links, images, and more. Most of the basic methods should be very familiar to anyone who has used rails view helpers. You can check out the details of these helpers in the [Application Helpers](/guides/application-helpers/overview "Application Helpers guide") guide. To register these helpers within your Sinatra application: ```ruby # app.rb require 'sinatra/base' require 'padrino-helpers' class Application < Sinatra::Base register Padrino::Helpers end ``` -------------------------------------------------------------------------------- ## Padrino Mailer This component provides a powerful but simple mail delivery system within Padrino (and Sinatra). There is full support for using an html content type as well as for file attachments. The Padrino Mailer has many similarities to ActionMailer but is much lighter-weight and easier to use. You can check out the details of the mailer in the [Padrino Mailer](/guides/features/padrino-mailer "Padrino Mailer guide") guide. To register this mailer within your Sinatra application: ```ruby # app.rb require 'sinatra/base' require 'padrino-mailer' class Application < Sinatra::Base register Padrino::Mailer mailer :sample do email :birthday do |name, age| subject 'Happy Birthday!' to 'john@fake.com' from 'noreply@birthday.com' locals name: name, age: age render 'sample/birthday' end end end ``` -------------------------------------------------------------------------------- ## Padrino Routing You can check out the details of the routing system in the [Routing](/guides/controllers/routing "Routing") guide. To register the routing and controller functionality within your Sinatra application: ```ruby # app.rb require 'sinatra/base' require 'padrino-core/application/routing' # # Small example that show you some padrino routes. # Point your browser to: # # http://localhost:3000 # http://localhost:3000/bar # http://localhost:3000/bar.js # http://localhost:3000/custom-route/123 # # These routes didn't work: # # http://localhost:3000/bar.xml # http://localhost:3000/bar.jsl # http://localhost:3000/custom-route # class MyApp < Sinatra::Application register Padrino::Routing get :foo, map: '/' do 'This is foo mapped as index' end get :bar, provides: [:js, :html] do case content_type when :js then 'Bar for js' when :html then 'Bar for html' else 'You can never see this' end end get :custom, map: '/custom-route', with: :id do "This is a custom route with #{params[:id]} as params[:id]" end end # MyApp MyApp.run!(port: 3000) ``` -------------------------------------------------------------------------------- ## Padrino Rendering Padrino enhances the Sinatra 'render' method to have support for automatic template engine detection, among other more advanced features. ```ruby # app.rb require 'sinatra/base' require 'padrino-helpers' class Application < Sinatra::Base register Padrino::Rendering get('/') { render 'example/demo' } # Auto-renders 'views/example/demo.haml' get('/demo') { render :haml, 'example/demo' } # Renders 'views/example/demo.haml' end ``` -------------------------------------------------------------------------------- ## Padrino Cache Padrino-cache provides page and fragment caching via [Moneta](http://rubydoc.info/gems/moneta) backends. ```ruby # app.rb require 'sinatra/base' require 'padrino-cache' class Application < Sinatra::Base register Padrino::Cache end ``` This will allow for use of the caching functionality within Sinatra. --- # Advanced Usage: Padrino and OmniAuth Overview # Padrino and OmniAuth Overview This article will show you how to mix our [Access Control](https://github.com/padrino/padrino-framework/blob/master/padrino-admin/lib/padrino-admin/access_control.rb) described [here](/guides/features/padrino-admin/) with the beautiful [omniauth](https://github.com/intridea/omniauth) rack middleware. The Padrino admin authentication and access control system provides a simple foundation from which you can create your authentication system. Combined with [omniauth](https://github.com/intridea/omniauth) you can then easily leverage the system to allow authentication through a variety of methods. Read below for more details on how to integrate them. In this article we’ll cover two topics: - 1) Integrating Padrino Admin Authentication into all apps within your project - 2) Enabling custom authentication strategies within the authentication system Before we begin, it is important to note that our integrated authentication based on project-roles can interact easily with other systems. The only files that need to be changed are `session.rb` and `account.rb` in order to add your own custom code. So, let’s start by creating a project using activerecord: ```shell $ padrino g project foo --orm activerecord --tiny $ cd foo $ bundle install ``` Now we need to add a model called `Account` to act as our persistence model: ```shell $ padrino g model Account name:string email:string role:string uid:string provider:string ``` Now we need to create and migrate our database: ```shell $ padrino rake ar:create ar:migrate ``` NOTE: If your migration fails with something like this `Directly inheriting from ActiveRecord::Migration is not supported. Please specify the Rails release the migration was written ...` then you need to update the `db/migrate/001_create_account.rb` with `class CreateAccounts < ActiveRecord::Migration[4.2]` Open your favorite editor and browse and edit `Gemfile` and add `omniauth` gem and the providers for twitter and facebook. We also add the `haml` gem as it's not included by default in the "tiny" padrino template: ```ruby # Gemfile gem 'omniauth' gem 'omniauth-twitter' gem 'omniauth-facebook' gem 'haml' ``` Now, run bundle install to install dependencies: ```shell $ bundle install ``` Now we need to add the `omniauth middleware`. Here you can choose two ways: - 1) Add the middleware project-wide (so every subapp can use it) - 2) Add the middleware only to the one app that requires it In our example, we need to edit simply `app/app.rb` and add: ```ruby # app/app.rb use OmniAuth::Builder do provider :twitter, 'consumer_key', 'consumer_secret' provider :facebook, 'app_id', 'app_secret' end ``` If you are in a multiapp scenario you need to edit `config/boot.rb`: ```ruby # config/boot.rb Padrino.use OmniAuth::Builder do provider :twitter, 'consumer_key', 'consumer_secret' provider :facebook, 'app_id', 'app_secret' end # before the line Padrino.load! ``` To obtain an app\_id and secret for Facebook, you need to: - Go to the [Facebook Developers Page](http://www.facebook.com/developers) - Browse and click [create a new app](http://www.facebook.com/developers/createapp.php) - Set your app name and complete the form… - One you finish that you are allowed to edit your settings, go to **website** section - Add to site url: `http://localhost:3000` - Add to domain: `localhost` - Write the **Application ID** and **Application Secret** to the `OmniAuth::Builder` To obtain a consumer\_key and consumer\_secret for Twitter, you need to: - Go to [Twitter Applications Page](https://developer.twitter.com/apps/new) - Insert all details and be sure to check **Application Type: Browser** - Set a callback url with a valid domain ex: `www.mydomain.com/auth/twitter/callback` - Save and go to **Application Settings** and **Manage Domains** - Be sure that you can see your example domain: `www.mydomain.com/auth/twitter/callback` - Authorize domain: `localhost` - Now go to **Application Details** and save `Consumer key` and `Consumer Secret` to the `OmniAuth::Builder` **NOTE**: For domain, it is not important that this path exist because `omniauth` changes the callback url. Next, we can integrate our authentication system within in `app/app.rb`: ```ruby # app/app.rb # at the top after the enable :sessions register Padrino::Admin::AccessControl set :login_page, '/' # determines the url login occurs access_control.roles_for :any do |role| role.protect '/profile' role.protect '/admin' # here is a demo path end # now we add a role for users access_control.roles_for :users do |role| role.allow '/profile' end ``` And add a couple useful routes, edit `app/controllers.rb` with: ```ruby # app/controllers.rb get :index do haml <<-HAML.gsub(/^ {6}/, '') Login with =link_to('Facebook', '/auth/facebook') or =link_to('Twitter', '/auth/twitter') HAML end get :profile do content_type :text current_account.to_yaml end get :destroy do set_current_account(nil) redirect url(:index) end get :auth, :map => '/auth/:provider/callback' do auth = request.env['omniauth.auth'] account = Account.find_by_provider_and_uid(auth['provider'], auth['uid']) || Account.create_with_omniauth(auth) set_current_account(account) redirect "http://" + request.env['HTTP_HOST'] + url(:profile) end ``` We invoked a method `Account.create_with_omniauth` above, so edit `app/models/account.rb` and add: ```ruby # app/models/account.rb def self.create_with_omniauth(auth) create! do |account| account.provider = auth['provider'] account.uid = auth['uid'] account.name = auth['info']['name'] if auth['info'] account.email = auth['info']['email'] if auth['info'] # we get this only from FB account.role = "users" end end ``` That should just about do it! Let’s start the server: ```shell $ padrino start ``` Browse That will kickoff to the login\_page, in our case `/`. Now you can login here Follow your login process and then if needed That is all you need to setup a barebones authentication system in Padrino. This post has gotten you started with a working “Account” and role based authentication solution with integrated omniauth support. From here, obviously there are a number of other features you might want to add on top to flesh out, and that is left for another post or as an exercise to the reader. ---