├── .coveralls.yml ├── .gitignore ├── .rspec ├── .rubocop.yml ├── .rubocop_todo.yml ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Dangerfile ├── Gemfile ├── LICENSE ├── README.md ├── RELEASING.md ├── Rakefile ├── UPGRADING.md ├── grape-rabl.gemspec ├── lib ├── grape-rabl.rb ├── grape-rabl │ ├── configuration.rb │ ├── formatter.rb │ ├── render.rb │ ├── tilt.rb │ └── version.rb └── grape │ └── rabl.rb └── spec ├── grape_rabl_configuration.rb ├── grape_rabl_formatter_spec.rb ├── grape_rabl_layout_spec.rb ├── grape_rabl_partials_spec.rb ├── grape_rabl_spec.rb ├── grape_rabl_xml_spec.rb ├── spec_helper.rb ├── support └── my_helper.rb └── views ├── _partial.rabl ├── admin.rabl ├── helper.rabl ├── info.rabl ├── layout_test ├── layouts │ ├── another.rabl │ └── application.rabl └── user.rabl ├── project.rabl └── user.rabl /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci 2 | repo_token: budbVe8MwqMKtO4OalAxPvXy4P4kY284P 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format=documentation 3 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | Exclude: 3 | - vendor/** 4 | - .bundle 5 | 6 | inherit_from: .rubocop_todo.yml 7 | -------------------------------------------------------------------------------- /.rubocop_todo.yml: -------------------------------------------------------------------------------- 1 | # This configuration was generated by 2 | # `rubocop --auto-gen-config` 3 | # on 2017-11-17 09:51:46 -0500 using RuboCop version 0.51.0. 4 | # The point is for the user to remove these configuration records 5 | # one by one as the offenses are removed from the code base. 6 | # Note that changes in the inspected code, or installation of new 7 | # versions of RuboCop, may require this file to be generated again. 8 | 9 | # Offense count: 1 10 | Lint/RescueException: 11 | Exclude: 12 | - 'spec/grape_rabl_spec.rb' 13 | 14 | # Offense count: 8 15 | # Configuration parameters: CountComments, ExcludedMethods. 16 | Metrics/BlockLength: 17 | Max: 169 18 | 19 | # Offense count: 23 20 | # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. 21 | # URISchemes: http, https 22 | Metrics/LineLength: 23 | Max: 148 24 | 25 | # Offense count: 1 26 | # Configuration parameters: CountComments. 27 | Metrics/MethodLength: 28 | Max: 13 29 | 30 | # Offense count: 1 31 | # Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts, AllowedAcronyms. 32 | # AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS 33 | Naming/FileName: 34 | Exclude: 35 | - 'lib/grape-rabl.rb' 36 | 37 | # Offense count: 5 38 | Style/Documentation: 39 | Exclude: 40 | - 'spec/**/*' 41 | - 'test/**/*' 42 | - 'lib/grape-rabl.rb' 43 | - 'lib/grape-rabl/configuration.rb' 44 | - 'lib/grape-rabl/formatter.rb' 45 | - 'lib/grape-rabl/render.rb' 46 | - 'lib/grape/rabl.rb' 47 | 48 | # Offense count: 1 49 | Style/DoubleNegation: 50 | Exclude: 51 | - 'lib/grape-rabl/formatter.rb' 52 | 53 | # Offense count: 1 54 | Style/IfInsideElse: 55 | Exclude: 56 | - 'lib/grape-rabl/formatter.rb' 57 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | before_install: gem install bundler -v '1.15.0' 2 | rvm: 3 | - 2.2.0 4 | - 2.3.0 5 | - ruby-head 6 | 7 | matrix: 8 | include: 9 | - rvm: 2.3.1 10 | script: 11 | - bundle exec danger 12 | allow_failures: 13 | - rvm: ruby-head 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | #### Next 2 | 3 | * Your contribution here. 4 | 5 | #### v0.5.0 6 | 7 | * [#34](https://github.com/ruby-grape/grape-rabl/pulls/34): If no RABL template is specified, fallback to the default response format as determined by Grape - [@chrisbloom7](https://github.com/chrisbloom7). 8 | 9 | #### v0.4.3 10 | 11 | * [#44](https://github.com/ruby-grape/grape-rabl/issues/44): Don't require unused hashie - [@tsuwatch](https://github.com/tsuwatch). 12 | * [#44](https://github.com/ruby-grape/grape-rabl/issues/44): Support Ruby >= 2.2 - [@tsuwatch](https://github.com/tsuwatch). 13 | * [#45](https://github.com/ruby-grape/grape-rabl/pull/45): Added danger, PR linter - [@dblock](https://github.com/dblock). 14 | 15 | #### v0.4.2 16 | 17 | * [#43](https://github.com/ruby-grape/grape-rabl/pull/43): Fix template caching for multiple formats - [@kushkella](https://github.com/kushkella). 18 | 19 | #### v0.4.1 20 | 21 | * [#39](https://github.com/ruby-grape/grape-rabl/issues/39): Automatically require 'grape/rabl' - [@martinezcoder](https://github.com/martinezcoder). 22 | 23 | #### v0.4.0 24 | 25 | * [#37](https://github.com/ruby-grape/grape-rabl/issues/37): Make grape-rabl thread-safe - [@kushkella](https://github.com/kushkella). 26 | 27 | #### v0.3.1 28 | 29 | * [#35](https://github.com/ruby-grape/grape-rabl/issues/35): The `render` method will no longer modify endpoint options at runtime - [@yesmeck](https://github.com/yesmeck). 30 | 31 | #### v0.3.0 32 | 33 | * [#22](https://github.com/ruby-grape/grape-rabl/pull/22): Enable using a layout template in Rabl - [@koko1000ban](https://github.com/koko1000ban). 34 | * Implemented Rubocop, Ruby style linter - [@dblock](https://github.com/dblock). 35 | * Removed JRuby support - [@dblock](https://github.com/dblock). 36 | * Enable using locals with `.render` - [@hobofan](https://github.com/hobofan). 37 | * Enable support for template caching - [@kushkella](https://github.com/kushkella). 38 | 39 | 40 | #### v0.2.2 41 | 42 | * [#20](https://github.com/ruby-grape/grape-rabl/pull/20): Relaxed dependency on a specific version of Grape - [@cheef](https://github.com/cheef). 43 | 44 | #### v0.2.1 45 | 46 | * [#11](https://github.com/ruby-grape/grape-rabl/pull/11): Fix: render template according to request format - [@alovak](https://github.com/alovak). 47 | 48 | #### v0.2.0 49 | 50 | * [#10](https://github.com/ruby-grape/grape-rabl/pull/10): Allow to use partials in Grape - [@ichilton](https://github.com/ichilton). 51 | * Stick to gem conventions - [@LTe](https://github.com/lte). 52 | * [#13](https://github.com/ruby-grape/grape-rabl/pull/13): Update for Grape 0.3 compatibility - [@alovak](https://github.com/alovak). 53 | * Format fix - [@LTe](https://github.com/LTe). 54 | 55 | #### v0.1.0 56 | 57 | * Updated w/released Grape 0.2.3 - [@dblock](https://github.com/dblock). 58 | * Added link to Rabl - [@dblock](https://github.com/dblock). 59 | * Grape 0.2.x and put back dependency status - [@dblock](https://github.com/dblock). 60 | * Grape 0.2.3 - [@dblock](https://github.com/dblock). 61 | * Updated Grape dependency via .gemspec - [@dblock](https://github.com/dblock). 62 | 63 | #### v0.0.6 64 | 65 | * [#6](https://github.com/ruby-grape/grape-rabl/pull/6): Use Grape formatter syntax instead of monkey-patching - [@dblock](https://github.com/dblock). 66 | * Close block code in README - [@LTe](https://github.com/LTe). 67 | * Change home to user - [@LTe](https://github.com/LTe). 68 | 69 | #### v0.0.5 70 | 71 | * Respect `default_format` for rabl response - [@LTe](https://github.com/LTe). 72 | 73 | #### v0.0.4 74 | 75 | * Require `grape/rabl` - [@LTe](https://github.com/LTe). 76 | 77 | #### v0.0.3 78 | 79 | * Template without `.rabl` - [@LTe](https://github.com/LTe). 80 | 81 | #### v0.0.2 82 | 83 | * Add Travis - [@LTe](https://github.com/LTe). 84 | * Remove ruby debug - [@LTe](https://github.com/LTe). 85 | * Works with rubinius - [@LTe](https://github.com/LTe). 86 | * Add dependency status - [@LTe](https://github.com/LTe). 87 | 88 | #### v0.0.1 89 | 90 | * Initial public release - [@LTe](https://github.com/lte). 91 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing to Grape-Rabl 2 | ========================== 3 | 4 | Grape-Rabl is work of [many of contributors](https://github.com/ruby-grape/grape-rabl/graphs/contributors). You're encouraged to submit [pull requests](https://github.com/ruby-grape/grape-rabl/pulls), [propose features and discuss issues](https://github.com/ruby-grape/grape-rabl/issues). When in doubt, ask a question in the [Grape Google Group](http://groups.google.com/group/ruby-grape). 5 | 6 | #### Fork the Project 7 | 8 | Fork the [project on Github](https://github.com/ruby-grape/grape-rabl) and check out your copy. 9 | 10 | ``` 11 | git clone https://github.com/contributor/grape-rabl.git 12 | cd grape-rabl 13 | git remote add upstream https://github.com/ruby-grape/grape-rabl.git 14 | ``` 15 | 16 | #### Create a Topic Branch 17 | 18 | Make sure your fork is up-to-date and create a topic branch for your feature or bug fix. 19 | 20 | ``` 21 | git checkout master 22 | git pull upstream master 23 | git checkout -b my-feature-branch 24 | ``` 25 | 26 | #### Bundle Install and Test 27 | 28 | Ensure that you can build the project and run tests. 29 | 30 | ``` 31 | bundle install 32 | bundle exec rake 33 | ``` 34 | 35 | #### Write Tests 36 | 37 | Try to write a test that reproduces the problem you're trying to fix or describes a feature that you want to build. Add to [spec/grape-rabl](spec/grape-rabl). 38 | 39 | We definitely appreciate pull requests that highlight or reproduce a problem, even without a fix. 40 | 41 | #### Write Code 42 | 43 | Implement your feature or bug fix. 44 | 45 | Ruby style is enforced with [Rubocop](https://github.com/bbatsov/rubocop), run `bundle exec rubocop` and fix any style issues highlighted. 46 | 47 | Make sure that `bundle exec rake` completes without errors. 48 | 49 | #### Write Documentation 50 | 51 | Document any external behavior in the [README](README.md). 52 | 53 | #### Update Changelog 54 | 55 | Add a line to [CHANGELOG](CHANGELOG.md) under *Next Release*. Make it look like every other line, including your name and link to your Github account. 56 | 57 | #### Commit Changes 58 | 59 | Make sure git knows your name and email address: 60 | 61 | ``` 62 | git config --global user.name "Your Name" 63 | git config --global user.email "contributor@example.com" 64 | ``` 65 | 66 | Writing good commit logs is important. A commit log should describe what changed and why. 67 | 68 | ``` 69 | git add ... 70 | git commit 71 | ``` 72 | 73 | #### Push 74 | 75 | ``` 76 | git push origin my-feature-branch 77 | ``` 78 | 79 | #### Make a Pull Request 80 | 81 | Go to https://github.com/contributor/grape-rabl and select your feature branch. Click the 'Pull Request' button and fill out the form. Pull requests are usually reviewed within a few days. 82 | 83 | #### Rebase 84 | 85 | If you've been working on a change for a while, rebase with upstream/master. 86 | 87 | ``` 88 | git fetch upstream 89 | git rebase upstream/master 90 | git push origin my-feature-branch -f 91 | ``` 92 | 93 | #### Update CHANGELOG Again 94 | 95 | Update the [CHANGELOG](CHANGELOG.md) with the pull request number. A typical entry looks as follows. 96 | 97 | ``` 98 | * [#123](https://github.com/ruby-grape/grape-rabl/pull/123): Reticulated splines - [@contributor](https://github.com/contributor). 99 | ``` 100 | 101 | Amend your previous commit and force push the changes. 102 | 103 | ``` 104 | git commit --amend 105 | git push origin my-feature-branch -f 106 | ``` 107 | 108 | #### Check on Your Pull Request 109 | 110 | Go back to your pull request after a few minutes and see whether it passed muster with Travis-CI. Everything should look green, otherwise fix issues and amend your commit as described above. 111 | 112 | #### Be Patient 113 | 114 | It's likely that your change will not be merged and that the nitpicky maintainers will ask you to do more, or fix seemingly benign problems. Hang on there! 115 | 116 | #### Thank You 117 | 118 | Please do know that we really appreciate and value your time and work. We love you, really. 119 | -------------------------------------------------------------------------------- /Dangerfile: -------------------------------------------------------------------------------- 1 | danger.import_dangerfile(gem: 'ruby-grape-danger') 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | 5 | group :development do 6 | gem 'rubocop', '0.51.0' 7 | end 8 | 9 | group :test do 10 | gem 'coveralls', require: false 11 | gem 'json' 12 | gem 'rabl' 13 | gem 'rack-test' 14 | gem 'rake' 15 | gem 'rspec' 16 | gem 'ruby-grape-danger', '~> 0.1.1', require: false 17 | end 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Piotr Niełacny 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Grape::Rabl 2 | 3 | Use [Rabl](https://github.com/nesquena/rabl) templates in [Grape](https://github.com/ruby-grape/grape)! 4 | 5 | [![Gem Version](http://img.shields.io/gem/v/grape-rabl.svg)](http://badge.fury.io/rb/grape-rabl) 6 | [![Build Status](http://img.shields.io/travis/ruby-grape/grape-rabl.svg)](https://travis-ci.org/ruby-grape/grape-rabl) 7 | [![Code Climate](https://codeclimate.com/github/ruby-grape/grape-rabl.svg)](https://codeclimate.com/github/ruby-grape/grape-rabl) 8 | [![Coverage Status](https://img.shields.io/coveralls/ruby-grape/grape-rabl.svg)](https://coveralls.io/r/ruby-grape/grape-rabl?branch=master) 9 | 10 | ## Installation 11 | 12 | Add the `grape` and `grape-rabl` gems to Gemfile. 13 | 14 | ```ruby 15 | gem 'grape' 16 | gem 'grape-rabl' 17 | ``` 18 | 19 | And then execute: 20 | 21 | $ bundle 22 | 23 | ## Upgrading 24 | 25 | See [UPGRADING](UPGRADING.md). 26 | 27 | ## Usage 28 | 29 | ### Setup view root directory 30 | 31 | ```ruby 32 | # config.ru 33 | use Rack::Config do |env| 34 | env['api.tilt.root'] = '/path/to/view/root/directory' 35 | end 36 | ``` 37 | 38 | ### Tell your API to use Grape::Formatter::Rabl 39 | 40 | ```ruby 41 | class API < Grape::API 42 | format :json 43 | formatter :json, Grape::Formatter::Rabl 44 | end 45 | ``` 46 | 47 | ### Use rabl templates conditionally 48 | 49 | Add the template name to the API options. 50 | 51 | ```ruby 52 | get "/user/:id", :rabl => "user.rabl" do 53 | @user = User.find(params[:id]) 54 | end 55 | ``` 56 | 57 | You can use instance variables in the Rabl template. 58 | 59 | ```ruby 60 | object @user => :user 61 | attributes :name, :email 62 | 63 | child @project => :project do 64 | attributes :name 65 | end 66 | ``` 67 | 68 | ### Use rabl layout 69 | 70 | Gape-rabl first looks for a layout file in `#{env['api.tilt.root']}/layouts/application.rabl`. 71 | 72 | You can override the default layout conventions: 73 | 74 | ```ruby 75 | # config.ru 76 | use Rack::Config do |env| 77 | env['api.tilt.root'] = '/path/to/view/root/directory' 78 | env['api.tilt.layout'] = 'layouts/another' 79 | end 80 | ``` 81 | 82 | ### Enable template caching 83 | 84 | Grape-rabl allows for template caching after templates are loaded initially. 85 | 86 | You can enable template caching: 87 | 88 | ```ruby 89 | # config.ru 90 | Grape::Rabl.configure do |config| 91 | config.cache_template_loading = true # default: false 92 | end 93 | ``` 94 | 95 | ## You can omit .rabl 96 | 97 | The following are identical. 98 | 99 | ```ruby 100 | get "/home", :rabl => "view" 101 | get "/home", :rabl => "view.rabl" 102 | ``` 103 | 104 | ### Example 105 | 106 | ```ruby 107 | # config.ru 108 | use Rack::Config do |env| 109 | env['api.tilt.root'] = '/path/to/view/root/directory' 110 | end 111 | 112 | class UserAPI < Grape::API 113 | format :json 114 | formatter :json, Grape::Formatter::Rabl 115 | 116 | get '/user/:id' do 117 | @user = User.find(params[:id]) 118 | 119 | # use rabl with 'user.rabl' or 'admin.rabl' template 120 | if @user.admin? 121 | # pass locals with the #render method 122 | render rabl: 'admin', locals: { details: 'this user is an admin' } 123 | else 124 | render rabl: 'user' 125 | end 126 | end 127 | 128 | get '/admin/:id', :rabl => 'admin' do 129 | @user = User.find(params[:id]) 130 | 131 | # use rabl with 'super_admin.rabl' 132 | render rabl: 'super_admin' if @user.super_admin? 133 | # when render method has not been used use template from endpoint definition 134 | end 135 | 136 | # use rabl with 'user_history.rabl' template 137 | get '/user/:id/history', :rabl => 'user_history' do 138 | @history = User.find(params[:id]).history 139 | end 140 | 141 | # do not use rabl, fallback to the default Grape response formatter 142 | get '/users' do 143 | User.all 144 | end 145 | end 146 | ``` 147 | 148 | ```ruby 149 | # user.rabl 150 | object @user => :user 151 | 152 | attributes :name 153 | ``` 154 | 155 | ## Usage with rails 156 | 157 | Create grape application 158 | 159 | ```ruby 160 | # app/api/user.rb 161 | class MyAPI < Grape::API 162 | format :json 163 | formatter :json, Grape::Formatter::Rabl 164 | get '/user/:id', :rabl => "user" do 165 | @user = User.find(params[:id]) 166 | end 167 | end 168 | ``` 169 | 170 | ```ruby 171 | # app/views/api/user.rabl 172 | object @user => :user 173 | ``` 174 | 175 | Edit your **config/application.rb** and add view path 176 | 177 | ```ruby 178 | # application.rb 179 | class Application < Rails::Application 180 | config.middleware.use(Rack::Config) do |env| 181 | env['api.tilt.root'] = Rails.root.join "app", "views", "api" 182 | end 183 | end 184 | ``` 185 | 186 | Mount application to rails router 187 | 188 | ```ruby 189 | # routes.rb 190 | GrapeExampleRails::Application.routes.draw do 191 | mount MyAPI , :at => "/api" 192 | end 193 | ``` 194 | 195 | ## Specs 196 | 197 | See ["Writing Tests"](https://github.com/ruby-grape/grape#writing-tests) in [grape](https://github.com/ruby-grape/grape) README. 198 | 199 | Enjoy :) 200 | 201 | ## Contributing 202 | 203 | See [CONTRIBUTING](CONTRIBUTING.md). 204 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | # Releasing Grape-Rabl 2 | 3 | There're no particular rules about when to release grape-rabl. Release bug fixes frequently, features not so frequently and breaking API changes rarely. 4 | 5 | ### Release 6 | 7 | Run tests, check that all tests succeed locally. 8 | 9 | ``` 10 | bundle install 11 | rake 12 | ``` 13 | 14 | Check that the last build succeeded in [Travis CI](https://travis-ci.org/ruby-grape/grape-rabl) for all supported platforms. 15 | 16 | Increment the version, modify [lib/grape-rabl/version.rb](lib/grape-rabl/version.rb). 17 | 18 | * Increment the third number if the release has bug fixes and/or very minor features, only (eg. change `0.7.1` to `0.7.2`). 19 | * Increment the second number if the release contains major features or breaking API changes (eg. change `0.7.1` to `0.8.0`). 20 | 21 | Change "Next Release" in [CHANGELOG.md](CHANGELOG.md) to the new version. 22 | 23 | ``` 24 | ### 0.7.2 (February 6, 2014) 25 | ``` 26 | 27 | Remove the line with "Your contribution here.", since there will be no more contributions to this release. 28 | 29 | Commit your changes. 30 | 31 | ``` 32 | git add CHANGELOG.md lib/grape-rabl/version.rb 33 | git commit -m "Preparing for release, 0.7.2." 34 | git push origin master 35 | ``` 36 | 37 | Release. 38 | 39 | ``` 40 | $ rake release 41 | 42 | grape-rabl 0.7.2 built to pkg/grape-rabl-0.7.2.gem. 43 | Tagged v0.7.2. 44 | Pushed git commits and tags. 45 | Pushed grape-rabl 0.7.2 to rubygems.org. 46 | ``` 47 | 48 | ### Prepare for the Next Version 49 | 50 | Add the next release to [CHANGELOG.md](CHANGELOG.md). 51 | 52 | ``` 53 | #### Next 54 | 55 | * Your contribution here. 56 | ``` 57 | 58 | Commit your changes. 59 | 60 | ``` 61 | git add CHANGELOG.md 62 | git commit -m "Preparing for next development iteration, 0.7.3." 63 | git push origin master 64 | ``` 65 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | require 'bundler/gem_tasks' 3 | 4 | require 'rspec/core' 5 | require 'rspec/core/rake_task' 6 | RSpec::Core::RakeTask.new(:spec) do |spec| 7 | spec.rspec_opts = ['-fd -c'] 8 | spec.pattern = FileList['spec/**/*_spec.rb'] 9 | end 10 | 11 | require 'rubocop/rake_task' 12 | RuboCop::RakeTask.new(:rubocop) 13 | 14 | task default: %i[rubocop spec] 15 | -------------------------------------------------------------------------------- /UPGRADING.md: -------------------------------------------------------------------------------- 1 | # Upgrading Grape::Rabl 2 | 3 | ## Upgrading to >= 0.5 4 | 5 | ### Fallback rendering when no RABL template is defined 6 | 7 | Prior to v0.5.0, Grape::Rabl would always render content as JSON when no Rabl template was specified for a request. Beginning in v0.5.0 Grape::Rabl will now fallback to using the default response format [as determined by Grape](https://github.com/ruby-grape/grape#api-formats) 8 | 9 | ```ruby 10 | class SampleApi < Grape::API 11 | format :xml 12 | formatter :xml, Grape::Formatter::Rabl 13 | 14 | get 'list' do 15 | %w[thing] 16 | end 17 | end 18 | ``` 19 | 20 | #### Former behavior 21 | 22 | ```ruby 23 | response.body # => ["thing"] 24 | ``` 25 | 26 | #### Current behavior 27 | 28 | ```ruby 29 | response.body # => \n\n thing\n 30 | ``` 31 | 32 | See [#34](https://github.com/ruby-grape/grape-rabl/pull/34) for more information. 33 | -------------------------------------------------------------------------------- /grape-rabl.gemspec: -------------------------------------------------------------------------------- 1 | 2 | require File.expand_path('../lib/grape-rabl/version', __FILE__) 3 | 4 | Gem::Specification.new do |gem| 5 | gem.authors = ['Piotr Niełacny'] 6 | gem.email = ['piotr.nielacny@gmail.com'] 7 | gem.description = 'Use rabl in grape' 8 | gem.summary = 'Use rabl in grape' 9 | gem.homepage = 'https://github.com/ruby-grape/grape-rabl' 10 | 11 | gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) } 12 | gem.files = `git ls-files`.split("\n") 13 | gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 14 | gem.name = 'grape-rabl' 15 | gem.require_paths = ['lib'] 16 | gem.version = Grape::Rabl::VERSION 17 | gem.required_ruby_version = '>= 2.2.0' 18 | 19 | gem.add_dependency 'grape' 20 | gem.add_dependency 'i18n' 21 | gem.add_dependency 'rabl' 22 | gem.add_dependency 'tilt' 23 | end 24 | -------------------------------------------------------------------------------- /lib/grape-rabl.rb: -------------------------------------------------------------------------------- 1 | require 'rabl' 2 | require 'grape' 3 | require 'grape/rabl' 4 | require 'grape-rabl/tilt' 5 | require 'grape-rabl/version' 6 | require 'grape-rabl/formatter' 7 | require 'grape-rabl/render' 8 | require 'grape-rabl/configuration' 9 | 10 | module Grape 11 | module Rabl 12 | class << self 13 | def configure 14 | yield(configuration) 15 | configuration 16 | end 17 | 18 | def configuration 19 | @configuration ||= Configuration.new 20 | end 21 | 22 | def reset_configuration! 23 | @configuration = nil 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/grape-rabl/configuration.rb: -------------------------------------------------------------------------------- 1 | module Grape 2 | module Rabl 3 | class Configuration 4 | attr_accessor :cache_template_loading 5 | 6 | def initialize 7 | @cache_template_loading = false 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/grape-rabl/formatter.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | module Grape 4 | module Rabl 5 | class Formatter 6 | class << self 7 | def tilt_cache 8 | @tilt_cache ||= ::Tilt::Cache.new 9 | end 10 | end 11 | 12 | attr_reader :env, :endpoint, :object 13 | 14 | def initialize(object, env) 15 | @env = env 16 | @endpoint = env['api.endpoint'] 17 | @object = object 18 | end 19 | 20 | def render 21 | if rablable? 22 | rabl do |template| 23 | engine = tilt_template(template) 24 | output = engine.render endpoint, locals 25 | if layout_template 26 | layout_template.render(endpoint) { output } 27 | else 28 | output 29 | end 30 | end 31 | else 32 | fallback_formatter.call object, env 33 | end 34 | end 35 | 36 | private 37 | 38 | # Find a formatter to fallback to. `env[Grape::Env::API_FORMAT]` will always be a 39 | # valid formatter, otherwise a HTTP 406 error would have already have been thrown 40 | def fallback_formatter 41 | Grape::Formatter.formatter_for(env[Grape::Env::API_FORMAT]) 42 | end 43 | 44 | def view_path(template) 45 | if template.split('.')[-1] == 'rabl' 46 | File.join(env['api.tilt.root'], template) 47 | else 48 | File.join(env['api.tilt.root'], (template + '.rabl')) 49 | end 50 | end 51 | 52 | def rablable? 53 | !!rabl_template 54 | end 55 | 56 | def rabl 57 | raise 'missing rabl template' unless rabl_template 58 | set_view_root unless env['api.tilt.root'] 59 | yield rabl_template 60 | end 61 | 62 | def locals 63 | env['api.tilt.rabl_locals'] || endpoint.options[:route_options][:rabl_locals] || {} 64 | end 65 | 66 | def rabl_template 67 | env['api.tilt.rabl'] || endpoint.options[:route_options][:rabl] 68 | end 69 | 70 | def set_view_root 71 | raise "Use Rack::Config to set 'api.tilt.root' in config.ru" 72 | end 73 | 74 | def tilt_template(template) 75 | if Grape::Rabl.configuration.cache_template_loading 76 | Grape::Rabl::Formatter.tilt_cache.fetch(tilt_cache_key(template)) { ::Tilt.new(view_path(template), tilt_options) } 77 | else 78 | ::Tilt.new(view_path(template), tilt_options) 79 | end 80 | end 81 | 82 | def tilt_options 83 | { format: env['api.format'], view_path: env['api.tilt.root'] } 84 | end 85 | 86 | def layout_template 87 | layout_path = view_path(env['api.tilt.layout'] || 'layouts/application') 88 | if Grape::Rabl.configuration.cache_template_loading 89 | Grape::Rabl::Formatter.tilt_cache.fetch(tilt_cache_key(layout_path)) { ::Tilt.new(layout_path, tilt_options) if File.exist?(layout_path) } 90 | else 91 | ::Tilt.new(layout_path, tilt_options) if File.exist?(layout_path) 92 | end 93 | end 94 | 95 | def tilt_cache_key(path) 96 | Digest::MD5.hexdigest("#{path}#{tilt_options}") 97 | end 98 | end 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /lib/grape-rabl/render.rb: -------------------------------------------------------------------------------- 1 | module Grape 2 | module Rabl 3 | module Render 4 | def render(options = {}) 5 | env['api.tilt.rabl'] = options[:rabl] 6 | env['api.tilt.rabl_locals'] = options[:locals] 7 | end 8 | end 9 | end 10 | end 11 | 12 | Grape::Endpoint.send(:include, Grape::Rabl::Render) 13 | -------------------------------------------------------------------------------- /lib/grape-rabl/tilt.rb: -------------------------------------------------------------------------------- 1 | require 'tilt' 2 | 3 | Rabl.register! 4 | -------------------------------------------------------------------------------- /lib/grape-rabl/version.rb: -------------------------------------------------------------------------------- 1 | module Grape 2 | module Rabl 3 | VERSION = '0.5.0'.freeze 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/grape/rabl.rb: -------------------------------------------------------------------------------- 1 | require 'grape-rabl' 2 | 3 | module Grape 4 | module Formatter 5 | module Rabl 6 | class << self 7 | def call(object, env) 8 | Grape::Rabl::Formatter.new(object, env).render 9 | end 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/grape_rabl_configuration.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'Grape::Rabl configuration' do 4 | context 'configuration' do 5 | it 'returns default values' do 6 | expect(Grape::Rabl.configuration.cache_template_loading).to eq(false) 7 | end 8 | 9 | it 'should set and reset configuration' do 10 | Grape::Rabl.configure do |config| 11 | config.cache_template_loading = true 12 | end 13 | expect(Grape::Rabl.configuration.cache_template_loading).to eq(true) 14 | Grape::Rabl.reset_configuration! 15 | expect(Grape::Rabl.configuration.cache_template_loading).to eq(false) 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/grape_rabl_formatter_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'Grape::Rabl formatter' do 4 | subject do 5 | Class.new(Grape::API) 6 | end 7 | 8 | let(:xml_render) do 9 | %( 10 | 11 | 12 | bad 13 | things 14 | happened 15 | 16 | 17 | ) 18 | end 19 | 20 | def app 21 | subject 22 | end 23 | 24 | context 'rendering' do 25 | context 'when no rabl template is specified' do 26 | before do 27 | # Grape::API defaults to the following declarations: 28 | # content_type :xml, 'application/xml' 29 | # content_type :json, 'application/json' 30 | # content_type :binary, 'application/octet-stream' 31 | # content_type :txt, 'text/plain' 32 | # default_format :txt 33 | subject.formatter :xml, Grape::Formatter::Rabl 34 | subject.formatter :txt, Grape::Formatter::Rabl 35 | subject.get('/oops') { { errors: %w[bad things happened] } } 36 | expect_any_instance_of(Grape::Rabl::Formatter).to receive(:render).and_call_original 37 | end 38 | 39 | it 'falls back to :txt given no other format information' do 40 | get '/oops' 41 | expect(last_response.body).to eq('{:errors=>["bad", "things", "happened"]}') 42 | expect(last_response.headers['Content-Type']).to eq('text/plain') 43 | end 44 | 45 | it 'falls back to the file extension if it is a valid format' do 46 | get '/oops.xml' 47 | expect(last_response.body).to eq(xml_render) 48 | expect(last_response.headers['Content-Type']).to eq('application/xml') 49 | end 50 | 51 | it 'falls back to the value of the `format` parameter in the query string if it is provided' do 52 | get '/oops?format=xml' 53 | expect(last_response.body).to eq(xml_render) 54 | expect(last_response.headers['Content-Type']).to eq('application/xml') 55 | end 56 | 57 | it 'falls back to the format set by the `format` option if it is a valid format' do 58 | # `format` option must be declared before endpoint 59 | subject.format :xml 60 | subject.get('/oops/2') { { errors: %w[bad things happened] } } 61 | 62 | get '/oops/2' 63 | expect(last_response.body).to eq(xml_render) 64 | expect(last_response.headers['Content-Type']).to eq('application/xml') 65 | end 66 | 67 | it 'falls back to the `Accept` header if it is a valid format' do 68 | get '/oops', {}, 'HTTP_ACCEPT' => 'application/xml' 69 | expect(last_response.body).to eq(xml_render) 70 | expect(last_response.headers['Content-Type']).to eq('application/xml') 71 | end 72 | 73 | it 'falls back to the default_format option if it is a valid format' do 74 | # `default_format` option must be declared before endpoint 75 | subject.default_format :xml 76 | subject.get('/oops/2') { { errors: %w[bad things happened] } } 77 | 78 | get '/oops/2' 79 | expect(last_response.body).to eq(xml_render) 80 | expect(last_response.headers['Content-Type']).to eq('application/xml') 81 | end 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /spec/grape_rabl_layout_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'Grape::Rabl layout' do 4 | let(:parsed_response) { JSON.parse(last_response.body) } 5 | 6 | subject do 7 | Class.new(Grape::API) 8 | end 9 | 10 | before do 11 | subject.format :json 12 | subject.formatter :json, Grape::Formatter::Rabl 13 | subject.before do 14 | env['api.tilt.root'] = "#{File.dirname(__FILE__)}/views/layout_test" 15 | end 16 | end 17 | 18 | def app 19 | subject 20 | end 21 | 22 | context 'default' do 23 | it 'proper render with default layout' do 24 | subject.get('/about', rabl: 'user') do 25 | @user = OpenStruct.new(name: 'LTe') 26 | @project = OpenStruct.new(name: 'First') 27 | @status = 200 28 | end 29 | 30 | get('/about') 31 | expect(parsed_response).to eq( 32 | JSON.parse(%({"status":200,"result":{"user":{"name":"LTe","project":{"name":"First"}}}})) 33 | ) 34 | end 35 | end 36 | 37 | context 'tilt layout is setup' do 38 | before do 39 | subject.before { env['api.tilt.layout'] = 'layouts/another' } 40 | end 41 | 42 | it 'proper render with specified layout' do 43 | subject.get('/about', rabl: 'user') do 44 | @user = OpenStruct.new(name: 'LTe') 45 | @project = OpenStruct.new(name: 'First') 46 | @status = 200 47 | end 48 | 49 | get('/about') 50 | puts last_response.body 51 | expect(parsed_response).to eq( 52 | JSON.parse(%({"result":{"user":{"name":"LTe","project":{"name":"First"}}}})) 53 | ) 54 | end 55 | end 56 | 57 | context 'layout cache' do 58 | before do 59 | @views_dir = FileUtils.mkdir_p("#{File.expand_path('..', File.dirname(__FILE__))}/tmp")[0] 60 | @layout = "#{@views_dir}/layouts/application.rabl" 61 | FileUtils.cp_r("#{File.dirname(__FILE__)}/views/layout_test/.", @views_dir) 62 | subject.before { env['api.tilt.root'] = "#{File.expand_path('..', File.dirname(__FILE__))}/tmp" } 63 | subject.get('/home', rabl: 'user') do 64 | @user = OpenStruct.new(name: 'LTe', email: 'email@example.com') 65 | @project = OpenStruct.new(name: 'First') 66 | @status = 200 67 | end 68 | end 69 | 70 | after do 71 | Grape::Rabl.reset_configuration! 72 | FileUtils.rm_f(@views_dir) 73 | end 74 | 75 | it 'should serve from cache if cache_template_loading' do 76 | Grape::Rabl.configure do |config| 77 | config.cache_template_loading = true 78 | end 79 | get '/home' 80 | expect(last_response.status).to eq(200) 81 | old_response = last_response.body 82 | open(@layout, 'a') { |f| f << 'node(:test) { "test" }' } 83 | get '/home' 84 | expect(last_response.status).to eq(200) 85 | new_response = last_response.body 86 | expect(old_response).to eq(new_response) 87 | end 88 | 89 | it 'should serve new template if cache_template_loading' do 90 | get '/home' 91 | expect(last_response.status).to eq(200) 92 | old_response = last_response.body 93 | open(@layout, 'a') { |f| f << 'node(:test) { "test" }' } 94 | get '/home' 95 | expect(last_response.status).to eq(200) 96 | new_response = last_response.body 97 | expect(old_response).not_to eq(new_response) 98 | end 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /spec/grape_rabl_partials_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'Grape::Rabl partials' do 4 | let(:parsed_response) { JSON.parse(last_response.body) } 5 | 6 | subject do 7 | Class.new(Grape::API) 8 | end 9 | 10 | before do 11 | subject.format :json 12 | subject.formatter :json, Grape::Formatter::Rabl 13 | subject.before { env['api.tilt.root'] = "#{File.dirname(__FILE__)}/views" } 14 | end 15 | 16 | def app 17 | subject 18 | end 19 | 20 | it 'proper render partials' do 21 | subject.get('/home', rabl: 'project') do 22 | @author = OpenStruct.new(author: 'LTe') 23 | @type = OpenStruct.new(type: 'paper') 24 | @project = OpenStruct.new(name: 'First', type: @type, author: @author) 25 | end 26 | 27 | get('/home') 28 | expect(parsed_response).to eq( 29 | JSON.parse('{"project":{"name":"First","info":{"type":"paper"},"author":{"author":"LTe"}}}') 30 | ) 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/grape_rabl_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Grape::Rabl do 4 | subject do 5 | Class.new(Grape::API) 6 | end 7 | 8 | before do 9 | subject.default_format :json 10 | subject.formatter :json, Grape::Formatter::Rabl 11 | subject.formatter :xml, Grape::Formatter::Rabl 12 | subject.helpers MyHelper 13 | end 14 | 15 | def app 16 | subject 17 | end 18 | 19 | it 'should work without rabl template' do 20 | subject.get('/home') { 'Hello World' } 21 | get '/home' 22 | expect(last_response.body).to eq('"Hello World"') 23 | end 24 | 25 | it 'should raise error about root directory' do 26 | begin 27 | subject.get('/home', rabl: true) {} 28 | get '/home' 29 | rescue Exception => e 30 | expect(e.message).to include "Use Rack::Config to set 'api.tilt.root' in config.ru" 31 | end 32 | end 33 | 34 | context 'titl root is setup' do 35 | let(:parsed_response) { JSON.parse(last_response.body) } 36 | 37 | before do 38 | subject.before { env['api.tilt.root'] = "#{File.dirname(__FILE__)}/views" } 39 | end 40 | 41 | describe 'helpers' do 42 | it 'should execute helper' do 43 | subject.get('/home', rabl: 'helper') { @user = OpenStruct.new } 44 | get '/home' 45 | expect(parsed_response).to eq(JSON.parse('{"user":{"helper":"my_helper"}}')) 46 | end 47 | end 48 | 49 | describe '#render' do 50 | before do 51 | subject.get('/home', rabl: 'user') do 52 | @user = OpenStruct.new(name: 'LTe') 53 | render rabl: 'admin' 54 | end 55 | 56 | subject.get('/admin/:id', rabl: 'user') do 57 | @user = OpenStruct.new(name: 'LTe') 58 | 59 | render rabl: 'admin' if params[:id] == '1' 60 | end 61 | 62 | subject.get('/home-detail', rabl: 'user') do 63 | @user = OpenStruct.new(name: 'LTe') 64 | render rabl: 'admin', locals: { details: 'amazing detail' } 65 | end 66 | 67 | subject.get('/about', rabl: 'user') do 68 | @user = OpenStruct.new(name: 'LTe') 69 | end 70 | 71 | subject.get('/about-detail', rabl: 'user') do 72 | @user = OpenStruct.new(name: 'LTe') 73 | render locals: { details: 'just a user' } 74 | end 75 | end 76 | 77 | it 'renders template passed as argument to render method' do 78 | get('/home') 79 | expect(parsed_response).to eq(JSON.parse('{"admin":{"name":"LTe"}}')) 80 | end 81 | 82 | it 'renders admin template' do 83 | get('/admin/1') 84 | expect(parsed_response).to eq(JSON.parse('{"admin":{"name":"LTe"}}')) 85 | end 86 | 87 | it 'renders user template' do 88 | get('/admin/2') 89 | expect(parsed_response).to eq(JSON.parse('{"user":{"name":"LTe","project":null}}')) 90 | end 91 | 92 | it 'renders template passed as argument to render method with locals' do 93 | get('/home-detail') 94 | expect(parsed_response).to eq(JSON.parse('{"admin":{"name":"LTe","details":"amazing detail"}}')) 95 | end 96 | 97 | it 'renders with locals without overriding template' do 98 | get('/about-detail') 99 | expect(parsed_response).to eq(JSON.parse('{"user":{"name":"LTe","details":"just a user","project":null}}')) 100 | end 101 | 102 | it 'does not save rabl options after called #render method' do 103 | get('/home') 104 | get('/about') 105 | expect(parsed_response).to eq(JSON.parse('{"user":{"name":"LTe","project":null}}')) 106 | end 107 | 108 | it 'does not modify endpoint options' do 109 | get '/home' 110 | expect(last_request.env['api.endpoint'].options[:route_options][:rabl]).to eq 'user' 111 | end 112 | end 113 | 114 | it 'should respond with proper content-type' do 115 | subject.get('/home', rabl: 'user') {} 116 | get('/home') 117 | expect(last_response.headers['Content-Type']).to eq('application/json') 118 | end 119 | 120 | it 'should not raise error about root directory' do 121 | subject.get('/home', rabl: 'user') {} 122 | get '/home' 123 | expect(last_response.status).to eq 200 124 | expect(last_response.body).not_to include "Use Rack::Config to set 'api.tilt.root' in config.ru" 125 | end 126 | 127 | ['user', 'user.rabl'].each do |rabl_option| 128 | it "should render rabl template (#{rabl_option})" do 129 | subject.get('/home', rabl: rabl_option) do 130 | @user = OpenStruct.new(name: 'LTe', email: 'email@example.com') 131 | @project = OpenStruct.new(name: 'First') 132 | end 133 | 134 | get '/home' 135 | expect(parsed_response).to eq(JSON.parse('{"user":{"name":"LTe","email":"email@example.com","project":{"name":"First"}}}')) 136 | end 137 | end 138 | 139 | describe 'template cache' do 140 | before do 141 | @views_dir = FileUtils.mkdir_p("#{File.expand_path('..', File.dirname(__FILE__))}/tmp")[0] 142 | @template = "#{@views_dir}/user.rabl" 143 | FileUtils.cp("#{File.dirname(__FILE__)}/views/user.rabl", @template) 144 | subject.before { env['api.tilt.root'] = "#{File.expand_path('..', File.dirname(__FILE__))}/tmp" } 145 | subject.get('/home', rabl: 'user') do 146 | @user = OpenStruct.new(name: 'LTe', email: 'email@example.com') 147 | @project = OpenStruct.new(name: 'First') 148 | end 149 | end 150 | 151 | after do 152 | Grape::Rabl.reset_configuration! 153 | FileUtils.rm_r(@views_dir) 154 | end 155 | 156 | it 'should serve from cache if cache_template_loading' do 157 | Grape::Rabl.configure do |config| 158 | config.cache_template_loading = true 159 | end 160 | get '/home' 161 | expect(last_response.status).to eq(200) 162 | old_response = last_response.body 163 | open(@template, 'a') { |f| f << 'node(:test) { "test" }' } 164 | get '/home' 165 | expect(last_response.status).to eq(200) 166 | new_response = last_response.body 167 | expect(old_response).to eq(new_response) 168 | end 169 | 170 | it 'should maintain different cached templates for different formats' do 171 | Grape::Rabl.configure do |config| 172 | config.cache_template_loading = true 173 | end 174 | get '/home' 175 | expect(last_response.status).to eq(200) 176 | json_response = last_response.body 177 | get '/home.xml' 178 | expect(last_response.status).to eq(200) 179 | xml_response = last_response.body 180 | expect(json_response).not_to eq(xml_response) 181 | open(@template, 'a') { |f| f << 'node(:test) { "test" }' } 182 | get '/home.xml' 183 | expect(last_response.status).to eq(200) 184 | expect(last_response.body).to eq(xml_response) 185 | get '/home.json' 186 | expect(last_response.status).to eq(200) 187 | expect(last_response.body).to eq(json_response) 188 | end 189 | 190 | it 'should serve new template unless cache_template_loading' do 191 | get '/home' 192 | expect(last_response.status).to eq(200) 193 | old_response = last_response.body 194 | open(@template, 'a') { |f| f << 'node(:test) { "test" }' } 195 | get '/home' 196 | expect(last_response.status).to eq(200) 197 | new_response = last_response.body 198 | expect(old_response).not_to eq(new_response) 199 | end 200 | end 201 | end 202 | end 203 | -------------------------------------------------------------------------------- /spec/grape_rabl_xml_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Grape::Rabl do 4 | subject do 5 | Class.new(Grape::API) 6 | end 7 | 8 | before do 9 | subject.format :xml 10 | subject.formatter :xml, Grape::Formatter::Rabl 11 | end 12 | 13 | def app 14 | subject 15 | end 16 | 17 | context 'with xml format' do 18 | before do 19 | subject.before do 20 | env['api.tilt.root'] = "#{File.dirname(__FILE__)}/views" 21 | env['api.format'] = :xml 22 | end 23 | end 24 | 25 | it 'should respond with proper content-type' do 26 | subject.get('/home', rabl: 'user') {} 27 | get('/home') 28 | expect(last_response.headers['Content-Type']).to eq('application/xml') 29 | end 30 | 31 | ['user', 'user.rabl'].each do |rabl_option| 32 | it "should render rabl template (#{rabl_option})" do 33 | subject.get('/home', rabl: rabl_option) do 34 | @user = OpenStruct.new(name: 'LTe', email: 'email@example.com') 35 | @project = OpenStruct.new(name: 'First') 36 | end 37 | 38 | get '/home' 39 | 40 | expect(last_response.body).to eq(%( 41 | 42 | LTe 43 | email@example.com 44 | 45 | First 46 | 47 | 48 | )) 49 | end 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 2 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 3 | 4 | require 'bundler' 5 | Bundler.setup :default, :test 6 | 7 | require 'coveralls' 8 | Coveralls.wear! 9 | 10 | require 'active_support/core_ext/hash/conversions' 11 | require 'grape/rabl' 12 | require 'rspec' 13 | require 'rack/test' 14 | require 'ostruct' 15 | 16 | RSpec.configure do |config| 17 | config.include Rack::Test::Methods 18 | config.raise_errors_for_deprecations! 19 | end 20 | 21 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } 22 | -------------------------------------------------------------------------------- /spec/support/my_helper.rb: -------------------------------------------------------------------------------- 1 | module MyHelper 2 | def my_helper 3 | 'my_helper' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/views/_partial.rabl: -------------------------------------------------------------------------------- 1 | attributes :type 2 | -------------------------------------------------------------------------------- /spec/views/admin.rabl: -------------------------------------------------------------------------------- 1 | object @user => :admin 2 | attributes :name 3 | 4 | node :details, unless: ->(_n) { locals[:details].nil? } do |_m| 5 | locals[:details] 6 | end 7 | -------------------------------------------------------------------------------- /spec/views/helper.rabl: -------------------------------------------------------------------------------- 1 | object @user => :user 2 | 3 | node(:helper) { my_helper } 4 | -------------------------------------------------------------------------------- /spec/views/info.rabl: -------------------------------------------------------------------------------- 1 | attributes :author 2 | -------------------------------------------------------------------------------- /spec/views/layout_test/layouts/another.rabl: -------------------------------------------------------------------------------- 1 | node(:result) do 2 | JSON.parse(yield) 3 | end 4 | -------------------------------------------------------------------------------- /spec/views/layout_test/layouts/application.rabl: -------------------------------------------------------------------------------- 1 | node(:status) { @status } 2 | 3 | node(:result) do 4 | JSON.parse(yield) 5 | end 6 | -------------------------------------------------------------------------------- /spec/views/layout_test/user.rabl: -------------------------------------------------------------------------------- 1 | object @user => :user 2 | attributes :name, :email 3 | 4 | child @project => :project do 5 | attributes :name 6 | end 7 | -------------------------------------------------------------------------------- /spec/views/project.rabl: -------------------------------------------------------------------------------- 1 | object @project => :project 2 | attributes :name 3 | 4 | node :info do 5 | partial 'partial', object: @project.type 6 | end 7 | 8 | child @author => :author do 9 | extends 'info' 10 | end 11 | -------------------------------------------------------------------------------- /spec/views/user.rabl: -------------------------------------------------------------------------------- 1 | object @user => :user 2 | attributes :name, :email 3 | 4 | child @project => :project do 5 | attributes :name 6 | end 7 | 8 | node :details, unless: ->(_n) { locals[:details].nil? } do |_m| 9 | locals[:details] 10 | end 11 | --------------------------------------------------------------------------------