traxis 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +115 -0
- data/Rakefile +2 -0
- data/lib/traxis/controller.rb +53 -0
- data/lib/traxis/controller_actions.rb +31 -0
- data/lib/traxis/controller_helpers.rb +114 -0
- data/lib/traxis/response.rb +57 -0
- data/lib/traxis/responses.rb +63 -0
- data/lib/traxis/version.rb +3 -0
- data/lib/traxis.rb +74 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/support/db.rb +23 -0
- data/spec/support/media_types.rb +55 -0
- data/spec/support/people_controller.rb +16 -0
- data/spec/support/resource_definitions.rb +47 -0
- data/spec/test.db +0 -0
- data/spec/traxis/controller_actions_spec.rb +58 -0
- data/spec/traxis/response_definition_spec.rb +20 -0
- data/traxis.gemspec +37 -0
- metadata +285 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 91efd5dbd295a637a81456957a97f0c3774339b5
|
4
|
+
data.tar.gz: 166b94652666b78f38d03d1d025f3fd9109e2eda
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 37b395ae398973ea122e9fd1bd940f160292e0dfb066c4cc322da504c53eed198e4e40d8e7d269bc94dfd69ee0241896db99c420d4de39d41f3ba960a877ff93
|
7
|
+
data.tar.gz: 32c226172c6cf21b7d77de89310585cad99b306ba0da1c353012c6d04d500540c41d3936e344ecb2e33da7e80a2864a92a6728e0edbdef88f5291681eb77427e
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
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
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Jason Ayre
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
# Traxis
|
2
|
+
Bridging the gap between Rails and Praxis (mainly Active Record), and sprinkling some inherited resources
|
3
|
+
inspired conventions for some 1-800-94-Jenny, controllers.
|
4
|
+
|
5
|
+
### Quick example
|
6
|
+
|
7
|
+
**Just include ::Traxis::Controller into your ::Praxis::Controller,**
|
8
|
+
(please have mercy on my soul and forgive the awful library name,
|
9
|
+
and corny sounding rhymes you may encounter and cringe at, while reading these docs) --
|
10
|
+
|
11
|
+
**define "handles" method to link your controller with your resource,
|
12
|
+
and you'll be off to the races.**
|
13
|
+
|
14
|
+
``` ruby
|
15
|
+
includes ::Traxis::Controller
|
16
|
+
|
17
|
+
handles ::Post,
|
18
|
+
:collection => {
|
19
|
+
:serializer => ::V1::MediaTypes::Users,
|
20
|
+
:json_root => "users"
|
21
|
+
},
|
22
|
+
:resource => {
|
23
|
+
:serializer => ::V1::MediaTypes::User,
|
24
|
+
:json_root => "user"
|
25
|
+
}
|
26
|
+
```
|
27
|
+
|
28
|
+
Ill prob make ^ auto config by default and overrideable, but works for now.
|
29
|
+
|
30
|
+
``` ruby
|
31
|
+
module V1
|
32
|
+
class PostsController < ::V1::BaseController
|
33
|
+
include ::Traxis::Controller
|
34
|
+
implements ::V1::ApiResources::Post
|
35
|
+
|
36
|
+
handles ::Post,
|
37
|
+
:collection => {
|
38
|
+
:serializer => ::V1::MediaTypes::Users,
|
39
|
+
:json_root => "users"
|
40
|
+
},
|
41
|
+
:resource => {
|
42
|
+
:serializer => ::V1::MediaTypes::User,
|
43
|
+
:json_root => "user"
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
### Will likely change the dsl "handle" method name as I think I hate that name.
|
50
|
+
|
51
|
+
# Purpose / Rant / Why I only build apis using IR (previously, in rails)
|
52
|
+
|
53
|
+
**IT HAS NOTHING TO DO WITH WRITING LESS LINES OF CODE**
|
54
|
+
|
55
|
+
&
|
56
|
+
|
57
|
+
**EVERYTHING TO DO WITH:**
|
58
|
+
1. Convention over configuration
|
59
|
+
2. Open closed principle
|
60
|
+
3. Making controllers super composable (via lots of resource centric helpers)
|
61
|
+
4. Skinny Controllers in general
|
62
|
+
5. Encouraging SRP, via the concept of 1 resource, being controller by a single controller
|
63
|
+
(or in the case of the actual Inherited Resources library, shortcuts were used for
|
64
|
+
things like polymorphism, I probably wont try to emulate any of that. And one of the big
|
65
|
+
draws to Praxis in general is it seems like it will be cleaner to handle routes and
|
66
|
+
context of controllers in a cleaner manner, with the routing being part of the endpoint
|
67
|
+
definition and what not).
|
68
|
+
|
69
|
+
IMO, the golden rule is:
|
70
|
+
|
71
|
+
When you cant keep the controller interface the same, you probably are talking about
|
72
|
+
overlapping behavior of resources, which likely warrants a new controller. Youll be amazed how much
|
73
|
+
cleaner your code can be.
|
74
|
+
|
75
|
+
### Why bring ActiveRecord to Praxis
|
76
|
+
|
77
|
+
1. Its the best orm around for the sake of usability and discoverability.
|
78
|
+
2. Scopes and relations are incredibly composable (if used right)
|
79
|
+
|
80
|
+
Sure it may be heavy handed in some instances, but I'll take a library that
|
81
|
+
makes me happy to be programming in it over one Im fighting with any day.
|
82
|
+
|
83
|
+
### Note, I only became aware of praxis, like 3 days before building this:
|
84
|
+
And I think it was only released to public like 3 days ago. So needless to say,
|
85
|
+
Im still trying to figure it all out, and the library is brand new.
|
86
|
+
Things will likely break, and things will likely change. I have however built
|
87
|
+
a small mostly working API completely using praxis and this gem, and I can tell you
|
88
|
+
I was suprised overall at the ease of working with the library, which is why I
|
89
|
+
started breaking my code out into this gem.
|
90
|
+
|
91
|
+
## Installation
|
92
|
+
|
93
|
+
Add this line to your application's Gemfile:
|
94
|
+
|
95
|
+
gem 'traxis'
|
96
|
+
|
97
|
+
And then execute:
|
98
|
+
|
99
|
+
$ bundle
|
100
|
+
|
101
|
+
Or install it yourself as:
|
102
|
+
|
103
|
+
$ gem install traxis
|
104
|
+
|
105
|
+
## Usage
|
106
|
+
|
107
|
+
TODO: Write usage instructions here
|
108
|
+
|
109
|
+
## Contributing
|
110
|
+
|
111
|
+
1. Fork it ( https://github.com/[my-github-username]/traxis/fork )
|
112
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
113
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
114
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
115
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'traxis/controller_helpers'
|
2
|
+
require 'traxis/controller_actions'
|
3
|
+
|
4
|
+
module Traxis
|
5
|
+
module Controller
|
6
|
+
extend ::ActiveSupport::Concern
|
7
|
+
include ::Praxis::Controller
|
8
|
+
include ::Traxis::ControllerHelpers
|
9
|
+
include ::Traxis::ControllerActions
|
10
|
+
|
11
|
+
included do
|
12
|
+
class_attribute :collection_options
|
13
|
+
class_attribute :resource_options
|
14
|
+
|
15
|
+
self.collection_options = ::ActiveSupport::OrderedOptions.new
|
16
|
+
self.resource_options = ::ActiveSupport::OrderedOptions.new
|
17
|
+
|
18
|
+
self.collection_options.merge!({
|
19
|
+
:json_root => name.demodulize.underscore.pluralize
|
20
|
+
})
|
21
|
+
|
22
|
+
self.resource_options.merge!({
|
23
|
+
:finder_param => :id,
|
24
|
+
:finder => :find,
|
25
|
+
:json_root => name.demodulize.underscore.singularize
|
26
|
+
})
|
27
|
+
end
|
28
|
+
|
29
|
+
module ClassMethods
|
30
|
+
def handles(resource_klass, collection:, resource:)
|
31
|
+
resource_options.merge!(class: resource_klass)
|
32
|
+
collection_options.merge!(collection)
|
33
|
+
resource_options.merge!(resource)
|
34
|
+
end
|
35
|
+
|
36
|
+
def resource_class
|
37
|
+
resource_options[:class]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def collection_options
|
42
|
+
self.class.collection_options
|
43
|
+
end
|
44
|
+
|
45
|
+
def resource_options
|
46
|
+
self.class.resource_options
|
47
|
+
end
|
48
|
+
|
49
|
+
def resource_class
|
50
|
+
self.class.resource_class
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Traxis
|
2
|
+
module ControllerActions
|
3
|
+
def create(**params)
|
4
|
+
if create_resource.errors.any?
|
5
|
+
::Traxis::Responses::ResourceError.new(:body => serialized_resource, :json_root => resource_options[:json_root], :errors => resource.errors)
|
6
|
+
else
|
7
|
+
::Traxis::Responses::ResourceCreated.new(:body => serialized_resource, :json_root => resource_options[:json_root])
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def destroy(id:)
|
12
|
+
::Traxis::Responses::ResourceDeleted.new(:body => serialized_resource, :json_root => resource_options[:json_root])
|
13
|
+
end
|
14
|
+
|
15
|
+
def index(**params)
|
16
|
+
::Traxis::Responses::Collection.new(:body => serialized_collection, :json_root => collection_options[:json_root])
|
17
|
+
end
|
18
|
+
|
19
|
+
def show(id:)
|
20
|
+
::Traxis::Responses::Resource.new(:body => serialized_resource, :json_root => resource_options[:json_root])
|
21
|
+
end
|
22
|
+
|
23
|
+
def update(id:, **params)
|
24
|
+
if update_resource.errors.any?
|
25
|
+
::Traxis::Responses::ResourceError.new(:body => serialized_resource, :json_root => resource_options[:json_root], :errors => resource.errors)
|
26
|
+
else
|
27
|
+
::Traxis::Responses::Resource.new(:body => serialized_resource, :json_root => resource_options[:json_root])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Traxis
|
2
|
+
module ControllerHelpers
|
3
|
+
protected
|
4
|
+
|
5
|
+
def association_chain
|
6
|
+
@association_chain ||= begin_of_association_chain.all
|
7
|
+
end
|
8
|
+
|
9
|
+
def assign_object_attributes(object, attribute_hash)
|
10
|
+
attribute_hash.each_pair do |k,v|
|
11
|
+
object.__send__("#{k}=", v)
|
12
|
+
end
|
13
|
+
|
14
|
+
object
|
15
|
+
end
|
16
|
+
|
17
|
+
def begin_of_association_chain
|
18
|
+
self.class.resource_options[:class]
|
19
|
+
end
|
20
|
+
|
21
|
+
def collection
|
22
|
+
@collection ||= association_chain
|
23
|
+
end
|
24
|
+
|
25
|
+
def collection_serializer_class
|
26
|
+
collection_options[:serializer]
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_resource
|
30
|
+
save_resource
|
31
|
+
end
|
32
|
+
|
33
|
+
def params
|
34
|
+
@params ||= request.params.attributes
|
35
|
+
end
|
36
|
+
|
37
|
+
def payload_attributes
|
38
|
+
@payload_attributes ||= request.payload.attributes[:"#{resource_options[:json_root]}"].attributes
|
39
|
+
end
|
40
|
+
|
41
|
+
def resource_class
|
42
|
+
resource_options[:class]
|
43
|
+
end
|
44
|
+
|
45
|
+
def resource
|
46
|
+
@resource ||= begin
|
47
|
+
if request.action.name == :create
|
48
|
+
association_chain.new(payload_attributes)
|
49
|
+
elsif request.action.name == :update
|
50
|
+
assign_object_attributes(resource_query_result, payload_attributes)
|
51
|
+
else
|
52
|
+
association_chain.__send__(resource_options[:finder], params[resource_options[:finder_param]])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def resource_finder
|
58
|
+
:find
|
59
|
+
end
|
60
|
+
|
61
|
+
def resource_query_result
|
62
|
+
@resource_query_result ||= association_chain.__send__(resource_options[:finder], params[resource_options[:finder_param]])
|
63
|
+
end
|
64
|
+
|
65
|
+
def serialized_collection
|
66
|
+
collection_serializer_class.new(scoped_collection)
|
67
|
+
end
|
68
|
+
|
69
|
+
def serializer_class
|
70
|
+
resource_options[:serializer]
|
71
|
+
end
|
72
|
+
|
73
|
+
def serialized_resource
|
74
|
+
serializer_class.new(resource)
|
75
|
+
end
|
76
|
+
|
77
|
+
def scoped_collection
|
78
|
+
@scoped_collection ||= begin
|
79
|
+
return collection if request.query.blank?
|
80
|
+
|
81
|
+
request.query.map do |k,v|
|
82
|
+
collection.__send__("#{k}", *v)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def save_resource
|
88
|
+
resource.save
|
89
|
+
|
90
|
+
resource
|
91
|
+
end
|
92
|
+
|
93
|
+
def success?
|
94
|
+
@success ||= begin
|
95
|
+
case request.action.name
|
96
|
+
when :create
|
97
|
+
resource && !resource.errors.any?
|
98
|
+
when :update
|
99
|
+
resource && !resource.errors.any?
|
100
|
+
when :destroy
|
101
|
+
resource
|
102
|
+
when :show
|
103
|
+
resource.class == resource_options[:class]
|
104
|
+
else
|
105
|
+
true
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def update_resource
|
111
|
+
save_resource
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
|
3
|
+
module Traxis
|
4
|
+
module Response
|
5
|
+
module JSON
|
6
|
+
extend ::ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
attr_accessor :json_root
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(json_root:nil, **args)
|
13
|
+
@json_root = json_root
|
14
|
+
|
15
|
+
super(**args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def format!
|
19
|
+
response_body[json_root] = @body
|
20
|
+
@body = response_body
|
21
|
+
@body
|
22
|
+
end
|
23
|
+
|
24
|
+
def handle
|
25
|
+
headers['Content-Type'] = 'application/json'
|
26
|
+
end
|
27
|
+
|
28
|
+
def encode!
|
29
|
+
@body = @body.to_json
|
30
|
+
end
|
31
|
+
|
32
|
+
def response_body
|
33
|
+
@response_body ||= {}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module Meta
|
38
|
+
extend ::ActiveSupport::Concern
|
39
|
+
|
40
|
+
def response_body
|
41
|
+
super.merge!(:meta => {})
|
42
|
+
@response_body
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module Errors
|
47
|
+
extend ::ActiveSupport::Concern
|
48
|
+
|
49
|
+
def response_body
|
50
|
+
super[:meta].merge!(:errors => [])
|
51
|
+
@response_body
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
require 'traxis/responses'
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Traxis
|
2
|
+
module Responses
|
3
|
+
class Base < ::Praxis::Response
|
4
|
+
end
|
5
|
+
|
6
|
+
class Unauthorized < ::Traxis::Responses::Base
|
7
|
+
include ::Traxis::Response::JSON
|
8
|
+
include ::Traxis::Response::Meta
|
9
|
+
include ::Traxis::Response::Errors
|
10
|
+
|
11
|
+
self.status = 401
|
12
|
+
|
13
|
+
def response_body
|
14
|
+
@response_body = super
|
15
|
+
@response_body[:meta][:errors] << "You are not authorized to do that!"
|
16
|
+
@response_body
|
17
|
+
end
|
18
|
+
|
19
|
+
def format!
|
20
|
+
@body = response_body
|
21
|
+
@body
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Resource < ::Traxis::Responses::Base
|
26
|
+
include ::Traxis::Response::JSON
|
27
|
+
self.response_name = :resource
|
28
|
+
self.status = 200
|
29
|
+
end
|
30
|
+
|
31
|
+
class ResourceCreated < ::Traxis::Responses::Resource
|
32
|
+
self.response_name = :resource_created
|
33
|
+
self.status = 201
|
34
|
+
end
|
35
|
+
|
36
|
+
class ResourceDeleted < ::Traxis::Responses::Resource
|
37
|
+
self.response_name = :resource_deleted
|
38
|
+
self.status = 204
|
39
|
+
end
|
40
|
+
|
41
|
+
class ResourceError < ::Traxis::Responses::Resource
|
42
|
+
self.response_name = :resource_error
|
43
|
+
self.status = 422
|
44
|
+
|
45
|
+
def initialize(errors:[], **args)
|
46
|
+
@errors = errors
|
47
|
+
super(**args)
|
48
|
+
end
|
49
|
+
|
50
|
+
def response_body
|
51
|
+
super[:errors] = @errors
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Collection < ::Traxis::Responses::Base
|
56
|
+
include ::Traxis::Response::JSON
|
57
|
+
include ::Traxis::Response::Meta
|
58
|
+
|
59
|
+
self.response_name = :collection
|
60
|
+
self.status = 200
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/traxis.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require "traxis/version"
|
2
|
+
require "active_support"
|
3
|
+
require "active_support/all"
|
4
|
+
|
5
|
+
module Traxis
|
6
|
+
def self.bootstrap!
|
7
|
+
load_concerns if concerns.any?
|
8
|
+
load_responses if responses.any?
|
9
|
+
register_responses
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.controllers
|
13
|
+
@controllers ||=::Dir[root.join('app', 'controllers', '**', '*_controller', '*.rb')]
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.load_concerns
|
17
|
+
concerns.each do |path|
|
18
|
+
require path
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.concerns
|
23
|
+
@concerns ||= ::Dir[root.join('app', '**', '*concerns', '*.rb')]
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.eager_require_directory(*args)
|
27
|
+
::Dir[::Traxis.root.join(*args)].each do |path|
|
28
|
+
require path
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.load_responses
|
33
|
+
responses.each do |path|
|
34
|
+
require path
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Because praxis is using inherited hook,responses arent registered
|
39
|
+
# i.e. response.response_name is nil, as rest of class
|
40
|
+
# (where assignment is made) hasn't been loaded yet
|
41
|
+
def self.register_response(klass)
|
42
|
+
::Praxis::ApiDefinition.define do |api|
|
43
|
+
api.response_template klass.response_name do
|
44
|
+
status klass.status
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
if !klass.subclasses.empty?
|
49
|
+
klass.subclasses.each do |subklass|
|
50
|
+
::Traxis.register_response(subklass)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.register_responses
|
56
|
+
::Traxis::Responses::Base.subclasses.each do |klass|
|
57
|
+
::Traxis.register_response(klass)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.responses
|
62
|
+
@responses ||=::Dir[root.join('app', '**', '*concerns', '*.rb')]
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.root
|
66
|
+
::Praxis::Application.instance.root
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
require "traxis/response"
|
71
|
+
|
72
|
+
::Traxis.bootstrap!
|
73
|
+
|
74
|
+
require "traxis/controller"
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
$:.unshift File.expand_path(__dir__)
|
2
|
+
$:.unshift File.expand_path('../lib',__dir__)
|
3
|
+
$:.unshift File.expand_path('support',__dir__)
|
4
|
+
|
5
|
+
require 'bundler'
|
6
|
+
Bundler.setup :default, :test
|
7
|
+
|
8
|
+
require 'simplecov'
|
9
|
+
|
10
|
+
require 'praxis'
|
11
|
+
require 'traxis'
|
12
|
+
require 'rack/test'
|
13
|
+
require 'rspec/its'
|
14
|
+
require 'rspec/collection_matchers'
|
15
|
+
require 'pry'
|
16
|
+
|
17
|
+
Dir["#{File.dirname(__FILE__)}/support/*.rb"].each do |file|
|
18
|
+
require file
|
19
|
+
end
|
data/spec/support/db.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
ActiveRecord::Base.establish_connection(
|
4
|
+
:adapter => "sqlite3",
|
5
|
+
:database => "spec/test.db"
|
6
|
+
)
|
7
|
+
|
8
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
9
|
+
ActiveRecord::Base.connection.drop_table(table)
|
10
|
+
end
|
11
|
+
|
12
|
+
ActiveRecord::Schema.define(:version => 1) do
|
13
|
+
create_table :people do |t|
|
14
|
+
t.string :first_name
|
15
|
+
t.string :last_name
|
16
|
+
|
17
|
+
t.timestamps
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Person < ::ActiveRecord::Base
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module MediaTypes
|
2
|
+
class People < Praxis::MediaTypeCollection
|
3
|
+
identifier 'application/json'
|
4
|
+
|
5
|
+
view :default do
|
6
|
+
@contents.merge!(@schema.attributes)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Person < Praxis::MediaType
|
11
|
+
attributes do
|
12
|
+
attribute :id, Integer
|
13
|
+
attribute :name, String, example: /[:name:]/
|
14
|
+
attribute :href, String, example: proc { |person| "/people/#{person.id}" }
|
15
|
+
end
|
16
|
+
|
17
|
+
view :default do
|
18
|
+
attribute :id
|
19
|
+
attribute :name
|
20
|
+
end
|
21
|
+
|
22
|
+
view :link do
|
23
|
+
attribute :id
|
24
|
+
attribute :name
|
25
|
+
attribute :href
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Address < Praxis::MediaType
|
30
|
+
identifier 'application/json'
|
31
|
+
|
32
|
+
description 'Address MediaType'
|
33
|
+
|
34
|
+
attributes do
|
35
|
+
attribute :id, Integer
|
36
|
+
attribute :name, String
|
37
|
+
|
38
|
+
attribute :owner, Person
|
39
|
+
|
40
|
+
links do
|
41
|
+
link :owner
|
42
|
+
link :super, Person, using: :manager
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
view :default do
|
48
|
+
attribute :id
|
49
|
+
attribute :name
|
50
|
+
attribute :owner
|
51
|
+
|
52
|
+
attribute :links
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative 'resource_definitions'
|
2
|
+
|
3
|
+
class PeopleController
|
4
|
+
include ::Traxis::Controller
|
5
|
+
|
6
|
+
implements ::PeopleResource
|
7
|
+
|
8
|
+
handles ::Person, :collection => {
|
9
|
+
:serializer => ::MediaTypes::Person,
|
10
|
+
:json_root => "people"
|
11
|
+
},
|
12
|
+
:resource => {
|
13
|
+
:serializer => ::MediaTypes::Person,
|
14
|
+
:json_root => "person"
|
15
|
+
}
|
16
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require_relative 'media_types'
|
2
|
+
|
3
|
+
class PeopleResource
|
4
|
+
include Praxis::ResourceDefinition
|
5
|
+
|
6
|
+
description 'People resource'
|
7
|
+
|
8
|
+
media_type ::MediaTypes::Person
|
9
|
+
|
10
|
+
version '1.0'
|
11
|
+
|
12
|
+
routing do
|
13
|
+
prefix "/people"
|
14
|
+
end
|
15
|
+
|
16
|
+
action :index do
|
17
|
+
description 'index description'
|
18
|
+
routing do
|
19
|
+
get ''
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
action :create do
|
24
|
+
description 'create description'
|
25
|
+
routing do
|
26
|
+
post ''
|
27
|
+
end
|
28
|
+
|
29
|
+
payload do
|
30
|
+
attribute :name, String, required: true
|
31
|
+
end
|
32
|
+
|
33
|
+
response :resource_created, media_type: 'application/json'
|
34
|
+
response :resource_error
|
35
|
+
end
|
36
|
+
|
37
|
+
action :show do
|
38
|
+
description 'show description'
|
39
|
+
routing do
|
40
|
+
get '/:id'
|
41
|
+
end
|
42
|
+
params do
|
43
|
+
attribute :id, Integer, required: true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
data/spec/test.db
ADDED
Binary file
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Traxis::Controller do
|
4
|
+
subject do
|
5
|
+
::PeopleController
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:rack_input) { StringIO.new('something=given') }
|
9
|
+
let(:env) do
|
10
|
+
env = Rack::MockRequest.env_for('/people')
|
11
|
+
env['rack.input'] = rack_input
|
12
|
+
env['CONTENT_TYPE'] = 'application/json'
|
13
|
+
env['HTTP_VERSION'] = 'HTTP/1.1'
|
14
|
+
env
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:context) do
|
18
|
+
{
|
19
|
+
params: [Attributor::AttributeResolver::ROOT_PREFIX, "params".freeze],
|
20
|
+
headers: [Attributor::AttributeResolver::ROOT_PREFIX, "headers".freeze],
|
21
|
+
payload: [Attributor::AttributeResolver::Data, "payload".freeze]
|
22
|
+
}.freeze
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:payload_hash) {
|
26
|
+
{}
|
27
|
+
}
|
28
|
+
|
29
|
+
let(:request) do
|
30
|
+
request = Praxis::Request.new(env)
|
31
|
+
request.action = subject.actions[:create]
|
32
|
+
request
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#create" do
|
36
|
+
let!(:payload_attributes) {
|
37
|
+
::Attributor::Struct.new({
|
38
|
+
'name' => "Jason"
|
39
|
+
})
|
40
|
+
}
|
41
|
+
|
42
|
+
let(:rack_input) {
|
43
|
+
StringIO.new(payload_attributes.to_json)
|
44
|
+
}
|
45
|
+
|
46
|
+
before do
|
47
|
+
request.load_headers(context[:headers])
|
48
|
+
request.load_params(context[:params])
|
49
|
+
request.load_payload(context[:payload])
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should create new resource" do
|
53
|
+
expect(::Person).to receive(:create).with(payload_attributes)
|
54
|
+
subject.new(request).create
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Traxis::Response do
|
4
|
+
subject { described_class }
|
5
|
+
|
6
|
+
context "when response_definition is inherited" do
|
7
|
+
let(:test_subject) { ::Praxis::ApiDefinition }
|
8
|
+
|
9
|
+
let(:fake_response_klass) do
|
10
|
+
fake = Class.new(::Traxis::Response)
|
11
|
+
fake.stub(:response_name) { :my_fake_response_klass }
|
12
|
+
fake.stub(:status) { 200 }
|
13
|
+
end
|
14
|
+
|
15
|
+
it "registers the response definition" do
|
16
|
+
Traxis.should_receive(:register_response)
|
17
|
+
fake_response_klass
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/traxis.gemspec
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'traxis/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "traxis"
|
8
|
+
spec.version = Traxis::VERSION
|
9
|
+
spec.authors = ["Jason Ayre"]
|
10
|
+
spec.email = ["jasonayre@gmail.com"]
|
11
|
+
spec.summary = %q{Strongly opinionated restful api convention > config for praxis}
|
12
|
+
spec.description = %q{Making Praxis more like rails with active record, inherited resources inspired conventions}
|
13
|
+
spec.homepage = "http://github.com/jasonayre/traxis"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "activesupport"
|
22
|
+
|
23
|
+
spec.add_development_dependency 'activerecord'
|
24
|
+
spec.add_development_dependency 'sqlite3'
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "praxis"
|
28
|
+
spec.add_development_dependency 'pry', '~> 0'
|
29
|
+
spec.add_development_dependency 'rspec', '~> 3'
|
30
|
+
spec.add_development_dependency 'rspec-its', '~> 1'
|
31
|
+
spec.add_development_dependency 'rspec-collection_matchers', '~> 1'
|
32
|
+
spec.add_development_dependency 'guard', '~> 2'
|
33
|
+
spec.add_development_dependency 'guard-rspec', '~> 4'
|
34
|
+
spec.add_development_dependency 'guard-bundler', '~> 2'
|
35
|
+
spec.add_development_dependency 'rack-test', '~> 0'
|
36
|
+
spec.add_development_dependency 'simplecov', '~> 0'
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,285 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: traxis
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jason Ayre
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-09-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activerecord
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sqlite3
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.6'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.6'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: praxis
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pry
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rspec
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '3'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '3'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rspec-its
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '1'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '1'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rspec-collection_matchers
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '1'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '1'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: guard
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - "~>"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '2'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - "~>"
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '2'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: guard-rspec
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - "~>"
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '4'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - "~>"
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '4'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: guard-bundler
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - "~>"
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '2'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - "~>"
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '2'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: rack-test
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - "~>"
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0'
|
202
|
+
type: :development
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - "~>"
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: '0'
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
name: simplecov
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - "~>"
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: '0'
|
216
|
+
type: :development
|
217
|
+
prerelease: false
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - "~>"
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '0'
|
223
|
+
description: Making Praxis more like rails with active record, inherited resources
|
224
|
+
inspired conventions
|
225
|
+
email:
|
226
|
+
- jasonayre@gmail.com
|
227
|
+
executables: []
|
228
|
+
extensions: []
|
229
|
+
extra_rdoc_files: []
|
230
|
+
files:
|
231
|
+
- ".gitignore"
|
232
|
+
- ".rspec"
|
233
|
+
- Gemfile
|
234
|
+
- LICENSE.txt
|
235
|
+
- README.md
|
236
|
+
- Rakefile
|
237
|
+
- lib/traxis.rb
|
238
|
+
- lib/traxis/controller.rb
|
239
|
+
- lib/traxis/controller_actions.rb
|
240
|
+
- lib/traxis/controller_helpers.rb
|
241
|
+
- lib/traxis/response.rb
|
242
|
+
- lib/traxis/responses.rb
|
243
|
+
- lib/traxis/version.rb
|
244
|
+
- spec/spec_helper.rb
|
245
|
+
- spec/support/db.rb
|
246
|
+
- spec/support/media_types.rb
|
247
|
+
- spec/support/people_controller.rb
|
248
|
+
- spec/support/resource_definitions.rb
|
249
|
+
- spec/test.db
|
250
|
+
- spec/traxis/controller_actions_spec.rb
|
251
|
+
- spec/traxis/response_definition_spec.rb
|
252
|
+
- traxis.gemspec
|
253
|
+
homepage: http://github.com/jasonayre/traxis
|
254
|
+
licenses:
|
255
|
+
- MIT
|
256
|
+
metadata: {}
|
257
|
+
post_install_message:
|
258
|
+
rdoc_options: []
|
259
|
+
require_paths:
|
260
|
+
- lib
|
261
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
262
|
+
requirements:
|
263
|
+
- - ">="
|
264
|
+
- !ruby/object:Gem::Version
|
265
|
+
version: '0'
|
266
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
267
|
+
requirements:
|
268
|
+
- - ">="
|
269
|
+
- !ruby/object:Gem::Version
|
270
|
+
version: '0'
|
271
|
+
requirements: []
|
272
|
+
rubyforge_project:
|
273
|
+
rubygems_version: 2.2.2
|
274
|
+
signing_key:
|
275
|
+
specification_version: 4
|
276
|
+
summary: Strongly opinionated restful api convention > config for praxis
|
277
|
+
test_files:
|
278
|
+
- spec/spec_helper.rb
|
279
|
+
- spec/support/db.rb
|
280
|
+
- spec/support/media_types.rb
|
281
|
+
- spec/support/people_controller.rb
|
282
|
+
- spec/support/resource_definitions.rb
|
283
|
+
- spec/test.db
|
284
|
+
- spec/traxis/controller_actions_spec.rb
|
285
|
+
- spec/traxis/response_definition_spec.rb
|