forked from ruby-grape/grape-entity
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 056dd9a
Showing
18 changed files
with
1,384 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
## MAC OS | ||
.DS_Store | ||
.com.apple.timemachine.supported | ||
|
||
## TEXTMATE | ||
*.tmproj | ||
tmtags | ||
|
||
## EMACS | ||
*~ | ||
\#* | ||
.\#* | ||
|
||
## REDCAR | ||
.redcar | ||
|
||
## VIM | ||
*.swp | ||
*.swo | ||
|
||
## RUBYMINE | ||
.idea | ||
|
||
## PROJECT::GENERAL | ||
coverage | ||
doc | ||
pkg | ||
.rvmrc | ||
.bundle | ||
.yardoc/* | ||
dist | ||
Gemfile.lock | ||
|
||
## Rubinius | ||
.rbx | ||
|
||
## PROJECT::SPECIFIC |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
--color | ||
--format=progress |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
rvm: | ||
- 1.8.7 | ||
- 1.9.2 | ||
- 1.9.3 | ||
- jruby | ||
- rbx | ||
- ree | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
--markup-provider=redcarpet | ||
--markup=markdown |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
Next Release | ||
============ | ||
* Your contribution here. | ||
|
||
0.1.0 (01/11/2013) | ||
================== | ||
|
||
* Initial Release |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
source 'http://rubygems.org' | ||
|
||
gemspec | ||
|
||
group :development, :test do | ||
gem 'pry' | ||
gem 'guard' | ||
gem 'guard-rspec' | ||
gem 'guard-bundler' | ||
gem 'rb-fsevent' | ||
gem 'growl' | ||
gem 'json' | ||
gem 'rspec' | ||
gem 'rack-test', "~> 0.6.2", :require => "rack/test" | ||
gem 'github-markup' | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# A sample Guardfile | ||
# More info at https://github.com/guard/guard#readme | ||
|
||
guard 'rspec', :version => 2 do | ||
watch(%r{^spec/.+_spec\.rb$}) | ||
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } | ||
watch(%r{^spec/support/shared_versioning_examples.rb$}) { |m| "spec/" } | ||
watch('spec/spec_helper.rb') { "spec/" } | ||
end | ||
|
||
|
||
guard 'bundler' do | ||
watch('Gemfile') | ||
watch(/^.+\.gemspec/) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
Copyright (c) 2010 Michael Bleigh and Intridea, Inc. | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining | ||
a copy of this software and associated documentation files (the | ||
"Software"), to deal in the Software without restriction, including | ||
without limitation the rights to use, copy, modify, merge, publish, | ||
distribute, sublicense, and/or sell copies of the Software, and to | ||
permit persons to whom the Software is furnished to do so, subject to | ||
the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be | ||
included in all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
# GrapeEntity | ||
|
||
## CI | ||
[![Build Status](https://travis-ci.org/agileanimal/grape_entity.png?branch=master)](https://travis-ci.org/agileanimal/grape_entity) | ||
|
||
## Introduction | ||
|
||
This gem is the Entity extracted out of [Grape](https://github.com/intridea/grape). | ||
|
||
Grape's Entity is a great idea: a API focussed Facade that sits on top of a model object. | ||
|
||
The intend is to allow the Entity to be used outside of Grape and provide additional exposure to parts of the entity needed to simplify testing parts of an API. | ||
|
||
We intend to use the specs from grape to ensure we maintain compatability with Grape through our changes so that we can use the Entity to replace Grape's internal Entity. | ||
|
||
## Project Tracking | ||
|
||
* - Need to setup something up for this - | ||
|
||
## Reusable Responses with Entities | ||
|
||
Entities are a reusable means for converting Ruby objects to API responses. | ||
Entities can be used to conditionally include fields, nest other entities, and build | ||
ever larger responses, using inheritance. | ||
|
||
### Defining Entities | ||
|
||
Entities inherit from GrapeEntity::Entity, and define a simple DSL. Exposures can use | ||
runtime options to determine which fields should be visible, these options are | ||
available to `:if`, `:unless`, and `:proc`. The option keys `:version` and `:collection` | ||
will always be defined. The `:version` key is defined as `api.version`. The | ||
`:collection` key is boolean, and defined as `true` if the object presented is an | ||
array. | ||
|
||
* `expose SYMBOLS` | ||
* define a list of fields which will always be exposed | ||
* `expose SYMBOLS, HASH` | ||
* HASH keys include `:if`, `:unless`, `:proc`, `:as`, `:using`, `:format_with`, `:documentation` | ||
* `:if` and `:unless` accept hashes (passed during runtime) or procs (arguments are object and options) | ||
* `expose SYMBOL, { :format_with => :formatter }` | ||
* expose a value, formatting it first | ||
* `:format_with` can only be applied to one exposure at a time | ||
* `expose SYMBOL, { :as => "alias" }` | ||
* Expose a value, changing its hash key from SYMBOL to alias | ||
* `:as` can only be applied to one exposure at a time | ||
* `expose SYMBOL BLOCK` | ||
* block arguments are object and options | ||
* expose the value returned by the block | ||
* block can only be applied to one exposure at a time | ||
|
||
```ruby | ||
module API | ||
module Entities | ||
class Status < GrapeEntity::Entity | ||
expose :user_name | ||
expose :text, :documentation => { :type => "string", :desc => "Status update text." } | ||
expose :ip, :if => { :type => :full } | ||
expose :user_type, user_id, :if => lambda{ |status, options| status.user.public? } | ||
expose :digest { |status, options| Digest::MD5.hexdigest(satus.txt) } | ||
expose :replies, :using => API::Status, :as => :replies | ||
end | ||
end | ||
end | ||
|
||
module API | ||
module Entities | ||
class StatusDetailed < API::Entities::Status | ||
expose :internal_id | ||
end | ||
end | ||
end | ||
``` | ||
|
||
#### Using the Exposure DSL | ||
|
||
Grape ships with a DSL to easily define entities within the context | ||
of an existing class: | ||
|
||
```ruby | ||
class Status | ||
include GrapeEntity::Entity::DSL | ||
|
||
entity :text, :user_id do | ||
expose :detailed, if: :conditional | ||
end | ||
end | ||
``` | ||
|
||
The above will automatically create a `Status::Entity` class and define properties on it according | ||
to the same rules as above. If you only want to define simple exposures you don't have to supply | ||
a block and can instead simply supply a list of comma-separated symbols. | ||
|
||
### Using Entities | ||
|
||
Once an entity is defined, it can be used within endpoints, by calling `present`. The `present` | ||
method accepts two arguments, the object to be presented and the options associated with it. The | ||
options hash must always include `:with`, which defines the entity to expose. | ||
|
||
If the entity includes documentation it can be included in an endpoint's description. | ||
|
||
```ruby | ||
module API | ||
class Statuses < GrapeEntity::API | ||
version 'v1' | ||
|
||
desc 'Statuses index', { | ||
:object_fields => API::Entities::Status.documentation | ||
} | ||
get '/statuses' do | ||
statuses = Status.all | ||
type = current_user.admin? ? :full : :default | ||
present statuses, with: API::Entities::Status, :type => type | ||
end | ||
end | ||
end | ||
``` | ||
|
||
### Entity Organization | ||
|
||
In addition to separately organizing entities, it may be useful to put them as namespaced | ||
classes underneath the model they represent. | ||
|
||
```ruby | ||
class Status | ||
def entity | ||
Status.new(self) | ||
end | ||
|
||
class Entity < GrapeEntity::Entity | ||
expose :text, :user_id | ||
end | ||
end | ||
``` | ||
|
||
If you organize your entities this way, Grape will automatically detect the `Entity` class and | ||
use it to present your models. In this example, if you added `present User.new` to your endpoint, | ||
Grape would automatically detect that there is a `Status::Entity` class and use that as the | ||
representative entity. This can still be overridden by using the `:with` option or an explicit | ||
`represents` call. | ||
|
||
### Caveats | ||
|
||
Entities with duplicate exposure names and conditions will silently overwrite one another. | ||
In the following example, when `object.check` equals "foo", only `field_a` will be exposed. | ||
However, when `object.check` equals "bar" both `field_b` and `foo` will be exposed. | ||
|
||
```ruby | ||
module API | ||
module Entities | ||
class Status < GrapeEntity::Entity | ||
expose :field_a, :foo, :if => lambda { |object, options| object.check == "foo" } | ||
expose :field_b, :foo, :if => lambda { |object, options| object.check == "bar" } | ||
end | ||
end | ||
end | ||
``` | ||
|
||
This can be problematic, when you have mixed collections. Using `respond_to?` is safer. | ||
|
||
```ruby | ||
module API | ||
module Entities | ||
class Status < GrapeEntity::Entity | ||
expose :field_a, :if => lambda { |object, options| object.check == "foo" } | ||
expose :field_b, :if => lambda { |object, options| object.check == "bar" } | ||
expose :foo, :if => lambda { |object, options| object.respond_to?(:foo) } | ||
end | ||
end | ||
end | ||
``` | ||
|
||
## Installation | ||
|
||
Add this line to your application's Gemfile: | ||
|
||
gem 'grape-entity' | ||
|
||
And then execute: | ||
|
||
$ bundle | ||
|
||
Or install it yourself as: | ||
|
||
$ gem install grape-entity | ||
|
||
## Contributing | ||
|
||
1. Fork it | ||
2. Create your feature branch (`git checkout -b my-new-feature`) | ||
3. Commit your changes (`git commit -am 'Add some feature'`) | ||
4. Push to the branch (`git push origin my-new-feature`) | ||
5. Create new Pull Request | ||
|
||
## License | ||
|
||
MIT License. See LICENSE for details. | ||
|
||
## Copyright | ||
|
||
Copyright (c) 2010-2012 Michael Bleigh, and Intridea, Inc. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
require 'rubygems' | ||
require 'bundler' | ||
Bundler.setup :default, :test, :development | ||
|
||
Bundler::GemHelper.install_tasks | ||
|
||
require 'rspec/core/rake_task' | ||
RSpec::Core::RakeTask.new(:spec) do |spec| | ||
spec.pattern = 'spec/**/*_spec.rb' | ||
end | ||
|
||
RSpec::Core::RakeTask.new(:rcov) do |spec| | ||
spec.pattern = 'spec/**/*_spec.rb' | ||
spec.rcov = true | ||
end | ||
|
||
task :spec | ||
task :default => :spec | ||
|
||
# | ||
# TODO: setup a place for documentation and then get this going again. | ||
# | ||
# begin | ||
# require 'yard' | ||
# DOC_FILES = ['lib/**/*.rb', 'README.markdown'] | ||
# | ||
# YARD::Rake::YardocTask.new(:doc) do |t| | ||
# t.files = DOC_FILES | ||
# end | ||
# | ||
# namespace :doc do | ||
# YARD::Rake::YardocTask.new(:pages) do |t| | ||
# t.files = DOC_FILES | ||
# t.options = ['-o', '../grape.doc'] | ||
# end | ||
# | ||
# namespace :pages do | ||
# desc 'Generate and publish YARD docs to GitHub pages.' | ||
# task :publish => ['doc:pages'] do | ||
# Dir.chdir(File.dirname(__FILE__) + '/../grape.doc') do | ||
# system("git add .") | ||
# system("git add -u") | ||
# system("git commit -m 'Generating docs for version #{version}.'") | ||
# system("git push origin gh-pages") | ||
# end | ||
# end | ||
# end | ||
# end | ||
# rescue LoadError | ||
# puts "You need to install YARD." | ||
# end |
Oops, something went wrong.