api-twister 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -0
- data/Gemfile.lock +27 -0
- data/README.rdoc +91 -0
- data/Rakefile +11 -0
- data/api-twister.gemspec +27 -0
- data/lib/api-twister.rb +11 -0
- data/lib/api-twister/api_association.rb +22 -0
- data/lib/api-twister/api_attribute.rb +12 -0
- data/lib/api-twister/api_definition.rb +66 -0
- data/lib/api-twister/api_documentation.rb +7 -0
- data/lib/api-twister/api_method.rb +4 -0
- data/lib/api-twister/api_twister.rb +114 -0
- data/lib/api-twister/code_table.rb +24 -0
- data/lib/api-twister/documentation.rb +30 -0
- data/lib/api-twister/version.rb +5 -0
- data/test/test_api_twister.rb +111 -0
- data/test/test_helper.rb +4 -0
- metadata +149 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
api-twister (0.0.1)
|
5
|
+
activesupport (>= 2.1.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activesupport (3.0.3)
|
11
|
+
i18n (0.5.0)
|
12
|
+
mocha (0.9.10)
|
13
|
+
rake
|
14
|
+
rake (0.8.7)
|
15
|
+
shoulda (2.11.3)
|
16
|
+
test-unit (2.1.2)
|
17
|
+
|
18
|
+
PLATFORMS
|
19
|
+
ruby
|
20
|
+
|
21
|
+
DEPENDENCIES
|
22
|
+
activesupport (>= 2.1.0)
|
23
|
+
api-twister!
|
24
|
+
i18n
|
25
|
+
mocha
|
26
|
+
shoulda
|
27
|
+
test-unit
|
data/README.rdoc
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
= API Twister Ruby Gem
|
2
|
+
|
3
|
+
== What
|
4
|
+
|
5
|
+
The api-twister gem is a ruby DSL to define non-trivial APIs.
|
6
|
+
|
7
|
+
It works with Rails 2, Rails 3, and without Rails (you will need ActiveSupport, though). It works with ruby 1.9.1 and 1.9.2, and possibly others.
|
8
|
+
|
9
|
+
== Why
|
10
|
+
|
11
|
+
Defining a simple REST API in Rails that returns simple objects is easy. Do it.
|
12
|
+
|
13
|
+
However, if you are returning nested objects, want to include methods in your responses, or want to exclude certain attributes, the basic configuration methods can get ugly quickly. Also if you have different serialization options and do not want to use HTTP response codes to describe them (for example, no results were found in a search), things can get ugly. This library hides much of the ugly. It puts knowledge of what attributes, methods, and associations to return in each model class. It allows you to define different named serialization options.
|
14
|
+
|
15
|
+
== Installation
|
16
|
+
|
17
|
+
gem install api-twister
|
18
|
+
|
19
|
+
=== Rails 3:
|
20
|
+
|
21
|
+
Add to your Gemfile:
|
22
|
+
|
23
|
+
gem 'api-twister'
|
24
|
+
|
25
|
+
=== Rails 2:
|
26
|
+
|
27
|
+
Add to your environment.rb:
|
28
|
+
|
29
|
+
config.gem 'api-twister'
|
30
|
+
|
31
|
+
== Development
|
32
|
+
|
33
|
+
Fork away. Please create a topic branch and write passing tests if you are submitting a pull request.
|
34
|
+
|
35
|
+
git clone git://github.com/yourname/api-twister.git
|
36
|
+
cd api-twister
|
37
|
+
bundle install
|
38
|
+
rake test
|
39
|
+
git checkout -b your_fix
|
40
|
+
|
41
|
+
== Show me some code!
|
42
|
+
|
43
|
+
class Monkey < ActiveRecord::Base
|
44
|
+
has_many :bananas
|
45
|
+
|
46
|
+
include ApiTwister
|
47
|
+
|
48
|
+
define_api do |api|
|
49
|
+
api.methods :behavior, :favorite_number, :status
|
50
|
+
api.association :bananas
|
51
|
+
end
|
52
|
+
|
53
|
+
api :request, :only => [:name]
|
54
|
+
api :response # default is everything in define_api
|
55
|
+
api :error_response, :only => [:status]
|
56
|
+
|
57
|
+
def behavior
|
58
|
+
name == 'Mr. Bananas' ? 'Good' : 'Bad'
|
59
|
+
end
|
60
|
+
|
61
|
+
def favorite_number
|
62
|
+
rand(10)
|
63
|
+
end
|
64
|
+
|
65
|
+
def status
|
66
|
+
valid? ? 'OK' : 'Error'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Banana < ActiveRecord::Base
|
71
|
+
belongs_to :monkey
|
72
|
+
|
73
|
+
include ApiTwister
|
74
|
+
|
75
|
+
define_api {|a| a.method :edible}
|
76
|
+
|
77
|
+
api :request
|
78
|
+
api :response
|
79
|
+
|
80
|
+
def edible
|
81
|
+
created_at > 10.days.ago ? 'Yes' : 'No'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# TODO - more docs
|
86
|
+
|
87
|
+
== Credits
|
88
|
+
|
89
|
+
Scott Jacobsen
|
90
|
+
|
91
|
+
Tee Parham
|
data/Rakefile
ADDED
data/api-twister.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "api-twister/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "api-twister"
|
7
|
+
s.version = Api::Twister::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Scott Jacobsen", "Tee Parham"]
|
10
|
+
s.email = ["hello@stackpilot.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Ruby DSL for non-trivial API specifications}
|
13
|
+
s.description = %q{Ruby DSL for non-trivial API specifications}
|
14
|
+
|
15
|
+
s.rubyforge_project = "api-twister"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n").delete_if {|file| %w(.rvmrc .gitignore).include? file}
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_dependency "activesupport", ">= 2.1.0" # for class_inheritable_accessor
|
23
|
+
s.add_development_dependency "i18n" #appears to be required by active support
|
24
|
+
s.add_development_dependency "test-unit"
|
25
|
+
s.add_development_dependency "shoulda"
|
26
|
+
s.add_development_dependency "mocha"
|
27
|
+
end
|
data/lib/api-twister.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'active_support/core_ext/class/inheritable_attributes'
|
2
|
+
require 'active_support/core_ext/hash/keys'
|
3
|
+
require 'active_support/core_ext/string/inflections'
|
4
|
+
|
5
|
+
require 'api-twister/api_association'
|
6
|
+
require 'api-twister/api_attribute'
|
7
|
+
require 'api-twister/api_method'
|
8
|
+
require "api-twister/code_table"
|
9
|
+
require 'api-twister/api_definition'
|
10
|
+
require 'api-twister/api_twister'
|
11
|
+
require "api-twister/documentation"
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ApiTwister
|
2
|
+
class ApiAssociation
|
3
|
+
attr_reader :association
|
4
|
+
|
5
|
+
def initialize(model, association)
|
6
|
+
@model = model
|
7
|
+
@association = association
|
8
|
+
end
|
9
|
+
|
10
|
+
def api_hash(name, options={})
|
11
|
+
hash = {}
|
12
|
+
assoc = @model.reflect_on_association(@association)
|
13
|
+
raise "There is no association named #{@association}" if assoc.nil?
|
14
|
+
klass = eval(assoc.class_name)
|
15
|
+
if klass.respond_to? :api_hash
|
16
|
+
hash[@association] = klass.api_hash(name, options)
|
17
|
+
else
|
18
|
+
hash[@association] = {}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module ApiTwister
|
2
|
+
class ApiAttribute
|
3
|
+
attr_accessor :name, :description, :data_type, :required
|
4
|
+
|
5
|
+
def initialize(name, data_type, description, required)
|
6
|
+
@name = name
|
7
|
+
@description = description
|
8
|
+
@data_type = data_type
|
9
|
+
@required = required
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module ApiTwister
|
2
|
+
class ApiDefinition
|
3
|
+
attr_reader :api_items
|
4
|
+
attr_accessor :model, :node_name
|
5
|
+
|
6
|
+
TYPE_MAP = {:association => ApiAssociation, :method => ApiMethod, :attribute => ApiAttribute, :code_table => CodeTable}
|
7
|
+
# Returns a list of symbols where the symbol is the name of the item
|
8
|
+
def all_items(type = nil)
|
9
|
+
if type
|
10
|
+
@api_items.select {|k, v| v.kind_of?(TYPE_MAP[type])}.keys
|
11
|
+
else
|
12
|
+
@api_items.keys
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def all_objects(type = nil)
|
17
|
+
if type
|
18
|
+
@api_items.select {|k, v| v.kind_of?(TYPE_MAP[type])}.values
|
19
|
+
else
|
20
|
+
@api_items.values
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(model, options = {})
|
25
|
+
@has_attributes = false
|
26
|
+
@model = model
|
27
|
+
@api_items = {}
|
28
|
+
@node_name = options[:node_name] || model.name.underscore.dasherize
|
29
|
+
end
|
30
|
+
|
31
|
+
def association(assoc)
|
32
|
+
@api_items[assoc] = ApiAssociation.new(model, assoc)
|
33
|
+
end
|
34
|
+
|
35
|
+
def associations(*assocs)
|
36
|
+
assocs.each {|a| association a}
|
37
|
+
end
|
38
|
+
|
39
|
+
def attribute(attrib, data_type = "string", description = nil, required = false)
|
40
|
+
@has_attributes = true
|
41
|
+
@api_items[attrib] = ApiAttribute.new(attrib, data_type, description, required)
|
42
|
+
end
|
43
|
+
|
44
|
+
def attributes(*attribs)
|
45
|
+
attribs.each {|a| attribute a }
|
46
|
+
end
|
47
|
+
|
48
|
+
alias_method :orig_method, :method
|
49
|
+
def method(meth, data_type = "string", description = nil, required = false)
|
50
|
+
@api_items[meth] = ApiMethod.new(meth, data_type, description, required)
|
51
|
+
end
|
52
|
+
|
53
|
+
alias_method :orig_methods, :methods
|
54
|
+
def methods(*meths)
|
55
|
+
meths.each {|m| method m}
|
56
|
+
end
|
57
|
+
|
58
|
+
def code_table(name, table_name, order_by=nil)
|
59
|
+
@api_items[name] = CodeTable.new(name, table_name, order_by)
|
60
|
+
end
|
61
|
+
|
62
|
+
def has_attributes?
|
63
|
+
@has_attributes
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module ApiTwister
|
2
|
+
def ApiTwister.included(base)
|
3
|
+
base.class_eval <<-eos
|
4
|
+
class_inheritable_accessor :_api_definition
|
5
|
+
class_inheritable_accessor :_api_specifications
|
6
|
+
extend ClassMethods
|
7
|
+
include InstanceMethods
|
8
|
+
eos
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def define_api(options={})
|
13
|
+
self._api_definition ||= ApiDefinition.new(self, options)
|
14
|
+
|
15
|
+
yield self._api_definition if block_given?
|
16
|
+
|
17
|
+
# if no attributes were added in block, add all the attributes here
|
18
|
+
unless self._api_definition.has_attributes?
|
19
|
+
self.new.attributes.symbolize_keys.keys.each { |k| _api_definition.attribute k }
|
20
|
+
end
|
21
|
+
|
22
|
+
self._api_definition
|
23
|
+
end
|
24
|
+
|
25
|
+
def api(name, options={})
|
26
|
+
self._api_specifications ||= {}
|
27
|
+
_api_specifications[name] ||= []
|
28
|
+
|
29
|
+
if options[:only]
|
30
|
+
options[:only].each do |only|
|
31
|
+
if [:methods, :attributes, :associations].include?(only)
|
32
|
+
_api_specifications[name] << _api_definition.all_items(only.to_s.singularize.to_sym)
|
33
|
+
else
|
34
|
+
_api_specifications[name] << only
|
35
|
+
end
|
36
|
+
end
|
37
|
+
elsif options[:except]
|
38
|
+
everything = _api_definition.all_items
|
39
|
+
except_list = []
|
40
|
+
options[:except].each do |except|
|
41
|
+
if [:methods, :attributes, :associations].include?(except)
|
42
|
+
except_list << _api_definition.all_items(except.to_s.singularize.to_sym)
|
43
|
+
else
|
44
|
+
except_list << except
|
45
|
+
end
|
46
|
+
end
|
47
|
+
except_list.flatten!
|
48
|
+
everything.flatten!
|
49
|
+
_api_specifications[name] = everything - except_list
|
50
|
+
else
|
51
|
+
self._api_specifications[name] = _api_definition.all_items
|
52
|
+
end
|
53
|
+
_api_specifications[name].flatten!
|
54
|
+
end
|
55
|
+
|
56
|
+
def api_hash(name, options = {})
|
57
|
+
spec = self._api_specifications[name]
|
58
|
+
hash = { :only => [] }
|
59
|
+
hash[:skip_types] = options[:skip_types] if options[:skip_types]
|
60
|
+
hash[:root] = options[:root] if options[:root]
|
61
|
+
user = options[:user]
|
62
|
+
|
63
|
+
spec.each do |item|
|
64
|
+
object = self._api_definition.api_items[item]
|
65
|
+
if object.is_a?(ApiMethod)
|
66
|
+
hash[:methods] ||= []
|
67
|
+
hash[:methods] << item if user_has_permission?(user, item)
|
68
|
+
elsif object.is_a?(ApiAttribute)
|
69
|
+
hash[:only] << item if user_has_permission?(user, item)
|
70
|
+
else
|
71
|
+
raise "Nil object. Spec: #{spec}, Item: #{item}" if object.nil?
|
72
|
+
hash[:include] ||= {}
|
73
|
+
|
74
|
+
hash[:include][item] = object.api_hash(name, options) if user_has_permission?(user, object.association)
|
75
|
+
end
|
76
|
+
end if spec
|
77
|
+
hash
|
78
|
+
end
|
79
|
+
|
80
|
+
def api_models(user)
|
81
|
+
#TODO: Test
|
82
|
+
models = [self] if user_has_permission?(user, self.name)
|
83
|
+
self._api_definition.all_objects(:association).each do |assoc|
|
84
|
+
models += assoc.model.api_models if user_has_permission?(user, assoc.association)
|
85
|
+
end
|
86
|
+
models
|
87
|
+
end
|
88
|
+
|
89
|
+
def exposes_as(item)
|
90
|
+
value = _api_definition.api_items[item.to_sym]
|
91
|
+
value.is_a?(ApiAssociation) ? :association : value
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def user_has_permission?(user, item)
|
97
|
+
user.nil? || user.has_permission?(self, item)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
module InstanceMethods
|
102
|
+
def api_hash(name, options={})
|
103
|
+
self.class.api_hash name, options
|
104
|
+
end
|
105
|
+
|
106
|
+
def to_api_xml(name, options={})
|
107
|
+
to_xml(api_hash(name, options))
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_api_json(name, options={})
|
111
|
+
to_json(api_hash(name, options))
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ApiTwister
|
2
|
+
class CodeTable
|
3
|
+
attr_accessor :name, :table_name
|
4
|
+
|
5
|
+
def initialize(name, table_name, order_by=nil)
|
6
|
+
@name = name
|
7
|
+
@table_name = table_name
|
8
|
+
@order_by = order_by
|
9
|
+
end
|
10
|
+
|
11
|
+
def code_values
|
12
|
+
@code_values ||= load_code_values
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def load_code_values
|
18
|
+
sql = "select * from #{table_name}"
|
19
|
+
sql << " order by #{@order_by}" if @order_by
|
20
|
+
ActiveRecord::Base.connection.select_all(sql).collect{|r| CodeValue.new(r)}
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module ApiTwister
|
2
|
+
module Documentation
|
3
|
+
def Documentation.included(base)
|
4
|
+
base.class_eval <<-eos
|
5
|
+
class_inheritable_accessor :_api_definition
|
6
|
+
class_inheritable_accessor :_api_specifications
|
7
|
+
extend ClassMethods
|
8
|
+
include InstanceMethods
|
9
|
+
eos
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def node_name(options = {})
|
14
|
+
#TODO: If a user is passed filter by permissions
|
15
|
+
self._api_definition.node_name
|
16
|
+
end
|
17
|
+
|
18
|
+
def doc_attributes(options = {})
|
19
|
+
# return a list of attributes and methods
|
20
|
+
#TODO: If a user is passed filter by permissions
|
21
|
+
self._api_definition.all_objects(:attribute) + self._api_definition.all_objects(:method)
|
22
|
+
end
|
23
|
+
|
24
|
+
def doc_code_tables(options = {})
|
25
|
+
#TODO: If a user is passed filter by permissions
|
26
|
+
self._api_definition.all_objects(:code_table)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require File.expand_path('test_helper.rb', File.dirname(__FILE__))
|
2
|
+
require 'mocha'
|
3
|
+
class ApiTwisterTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "A class with the default api specification" do
|
6
|
+
setup do
|
7
|
+
class AttributesOnly
|
8
|
+
|
9
|
+
def attributes
|
10
|
+
{"abc" => nil, "xyz" => nil}
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_method; end
|
14
|
+
|
15
|
+
include ApiTwister
|
16
|
+
define_api
|
17
|
+
api :request
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
should "should only have attributes in the api_hash" do
|
23
|
+
assert_equal({:only => [:abc, :xyz]}, AttributesOnly.api_hash(:request))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "A class that defines only the methods as part of the API" do
|
28
|
+
setup do
|
29
|
+
class KlassWithMethod
|
30
|
+
include ApiTwister
|
31
|
+
|
32
|
+
def attributes
|
33
|
+
{"abc" => nil, "xyz" => nil}
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_method; end
|
37
|
+
|
38
|
+
define_api do |api|
|
39
|
+
api.method :test_method
|
40
|
+
end
|
41
|
+
|
42
|
+
api :request
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
should "Have methods and attributes as part of the API because attributes are included by default" do
|
47
|
+
assert_equal({:methods => [:test_method], :only => [:abc, :xyz]}, KlassWithMethod.api_hash(:request))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "A class that defines only attributes as a part of the API" do
|
52
|
+
setup do
|
53
|
+
class KlassWithAttributes
|
54
|
+
include ApiTwister
|
55
|
+
extend Mocha::API
|
56
|
+
|
57
|
+
def attributes
|
58
|
+
{"abc" => nil, "xyz" => nil}
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_method; end
|
62
|
+
|
63
|
+
# stub of the ActiveRecord reflect_on_association method
|
64
|
+
def self.reflect_on_association(*args)
|
65
|
+
association_fake = stub(:nil? => false, :class_name => 'Hash')
|
66
|
+
end
|
67
|
+
|
68
|
+
define_api do |api|
|
69
|
+
api.association :another_class
|
70
|
+
end
|
71
|
+
|
72
|
+
api :request
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
should "Have associations and attributes as part of the API because attributes are included by default" do
|
77
|
+
assert_equal({:include => {:another_class => {}}, :only => [:abc, :xyz]}, KlassWithAttributes.api_hash(:request))
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "A class where a subset of the attributes are defined in the api" do
|
82
|
+
setup do
|
83
|
+
class KlassWithSubsetOfAttributes
|
84
|
+
include ApiTwister
|
85
|
+
|
86
|
+
def attributes; {"abc" => nil, "xyz" => nil}; end
|
87
|
+
def test_method; end
|
88
|
+
|
89
|
+
define_api do |api|
|
90
|
+
api.attribute :abc
|
91
|
+
end
|
92
|
+
|
93
|
+
api :request
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
should "Only have the specified attribute in the api_hash" do
|
98
|
+
assert_equal({:only => [:abc]}, KlassWithSubsetOfAttributes.api_hash(:request))
|
99
|
+
end
|
100
|
+
|
101
|
+
should "Include skip types if specified as an api_hash option" do
|
102
|
+
assert_equal({:only => [:abc], :skip_types => true}, KlassWithSubsetOfAttributes.api_hash(:request, :skip_types => true))
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context "ApiDefinition" do
|
107
|
+
should("respond to aliased orig_method") { assert ApiTwister::ApiDefinition.new(Hash).respond_to?(:orig_method) }
|
108
|
+
should("respond to aliased orig_methods") { assert ApiTwister::ApiDefinition.new(Hash).respond_to?(:orig_methods) }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: api-twister
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Scott Jacobsen
|
13
|
+
- Tee Parham
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-01-19 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: activesupport
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
segments:
|
30
|
+
- 2
|
31
|
+
- 1
|
32
|
+
- 0
|
33
|
+
version: 2.1.0
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: i18n
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: test-unit
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
type: :development
|
61
|
+
version_requirements: *id003
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: shoulda
|
64
|
+
prerelease: false
|
65
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
version: "0"
|
73
|
+
type: :development
|
74
|
+
version_requirements: *id004
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: mocha
|
77
|
+
prerelease: false
|
78
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
segments:
|
84
|
+
- 0
|
85
|
+
version: "0"
|
86
|
+
type: :development
|
87
|
+
version_requirements: *id005
|
88
|
+
description: Ruby DSL for non-trivial API specifications
|
89
|
+
email:
|
90
|
+
- hello@stackpilot.com
|
91
|
+
executables: []
|
92
|
+
|
93
|
+
extensions: []
|
94
|
+
|
95
|
+
extra_rdoc_files: []
|
96
|
+
|
97
|
+
files:
|
98
|
+
- Gemfile
|
99
|
+
- Gemfile.lock
|
100
|
+
- README.rdoc
|
101
|
+
- Rakefile
|
102
|
+
- api-twister.gemspec
|
103
|
+
- lib/api-twister.rb
|
104
|
+
- lib/api-twister/api_association.rb
|
105
|
+
- lib/api-twister/api_attribute.rb
|
106
|
+
- lib/api-twister/api_definition.rb
|
107
|
+
- lib/api-twister/api_documentation.rb
|
108
|
+
- lib/api-twister/api_method.rb
|
109
|
+
- lib/api-twister/api_twister.rb
|
110
|
+
- lib/api-twister/code_table.rb
|
111
|
+
- lib/api-twister/documentation.rb
|
112
|
+
- lib/api-twister/version.rb
|
113
|
+
- test/test_api_twister.rb
|
114
|
+
- test/test_helper.rb
|
115
|
+
has_rdoc: true
|
116
|
+
homepage: ""
|
117
|
+
licenses: []
|
118
|
+
|
119
|
+
post_install_message:
|
120
|
+
rdoc_options: []
|
121
|
+
|
122
|
+
require_paths:
|
123
|
+
- lib
|
124
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
125
|
+
none: false
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
segments:
|
130
|
+
- 0
|
131
|
+
version: "0"
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
none: false
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
segments:
|
138
|
+
- 0
|
139
|
+
version: "0"
|
140
|
+
requirements: []
|
141
|
+
|
142
|
+
rubyforge_project: api-twister
|
143
|
+
rubygems_version: 1.3.7
|
144
|
+
signing_key:
|
145
|
+
specification_version: 3
|
146
|
+
summary: Ruby DSL for non-trivial API specifications
|
147
|
+
test_files:
|
148
|
+
- test/test_api_twister.rb
|
149
|
+
- test/test_helper.rb
|