moonrope 1.3.3 → 2.0.2
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 +5 -5
- data/Gemfile +9 -0
- data/Gemfile.lock +47 -0
- data/MIT-LICENCE +20 -0
- data/README.md +24 -0
- data/bin/moonrope +28 -0
- data/docs/authentication.md +114 -0
- data/docs/controllers.md +106 -0
- data/docs/exceptions.md +27 -0
- data/docs/introduction.md +29 -0
- data/docs/structures.md +214 -0
- data/example/authentication.rb +50 -0
- data/example/controllers/meta_controller.rb +14 -0
- data/example/controllers/users_controller.rb +92 -0
- data/example/structures/pet_structure.rb +12 -0
- data/example/structures/user_structure.rb +35 -0
- data/lib/moonrope.rb +5 -4
- data/lib/moonrope/action.rb +170 -40
- data/lib/moonrope/authenticator.rb +42 -0
- data/lib/moonrope/base.rb +67 -6
- data/lib/moonrope/controller.rb +4 -2
- data/lib/moonrope/doc_context.rb +94 -0
- data/lib/moonrope/doc_server.rb +123 -0
- data/lib/moonrope/dsl/action_dsl.rb +159 -9
- data/lib/moonrope/dsl/authenticator_dsl.rb +35 -0
- data/lib/moonrope/dsl/base_dsl.rb +21 -18
- data/lib/moonrope/dsl/controller_dsl.rb +60 -9
- data/lib/moonrope/dsl/filterable_dsl.rb +27 -0
- data/lib/moonrope/dsl/structure_dsl.rb +28 -2
- data/lib/moonrope/errors.rb +13 -0
- data/lib/moonrope/eval_environment.rb +82 -3
- data/lib/moonrope/eval_helpers.rb +47 -8
- data/lib/moonrope/eval_helpers/filter_helper.rb +82 -0
- data/lib/moonrope/guard.rb +35 -0
- data/lib/moonrope/html_generator.rb +65 -0
- data/lib/moonrope/param_set.rb +11 -1
- data/lib/moonrope/rack_middleware.rb +66 -37
- data/lib/moonrope/railtie.rb +31 -14
- data/lib/moonrope/request.rb +43 -15
- data/lib/moonrope/structure.rb +100 -18
- data/lib/moonrope/structure_attribute.rb +39 -0
- data/lib/moonrope/version.rb +1 -1
- data/moonrope.gemspec +21 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/specs/action_spec.rb +455 -0
- data/spec/specs/base_spec.rb +29 -0
- data/spec/specs/controller_spec.rb +31 -0
- data/spec/specs/param_set_spec.rb +31 -0
- data/templates/basic/_action_form.erb +77 -0
- data/templates/basic/_errors_table.erb +32 -0
- data/templates/basic/_structure_attributes_list.erb +55 -0
- data/templates/basic/action.erb +168 -0
- data/templates/basic/assets/lock.svg +3 -0
- data/templates/basic/assets/reset.css +101 -0
- data/templates/basic/assets/style.css +348 -0
- data/templates/basic/assets/tool.svg +4 -0
- data/templates/basic/assets/try.js +157 -0
- data/templates/basic/authenticator.erb +52 -0
- data/templates/basic/controller.erb +20 -0
- data/templates/basic/index.erb +114 -0
- data/templates/basic/layout.erb +46 -0
- data/templates/basic/structure.erb +23 -0
- data/test/test_helper.rb +81 -0
- data/test/tests/action_access_test.rb +63 -0
- data/test/tests/actions_test.rb +524 -0
- data/test/tests/authenticators_test.rb +87 -0
- data/test/tests/base_test.rb +35 -0
- data/test/tests/controllers_test.rb +49 -0
- data/test/tests/eval_environment_test.rb +136 -0
- data/test/tests/evel_helpers_test.rb +60 -0
- data/test/tests/examples_test.rb +11 -0
- data/test/tests/helpers_test.rb +97 -0
- data/test/tests/param_set_test.rb +44 -0
- data/test/tests/rack_middleware_test.rb +131 -0
- data/test/tests/request_test.rb +232 -0
- data/test/tests/structures_param_extensions_test.rb +159 -0
- data/test/tests/structures_test.rb +398 -0
- metadata +71 -56
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
|
|
2
|
+
<% if authenticator.name == :default %>
|
|
3
|
+
<% set_page_title "Authentication" %>
|
|
4
|
+
<% set_active_nav "authenticator-default" %>
|
|
5
|
+
<h1>Authentication</h1>
|
|
6
|
+
<% else %>
|
|
7
|
+
<% set_page_title "#{humanize(authenticator.name.to_s.capitalize)} Authenticator" %>
|
|
8
|
+
<h1><%= humanize(authenticator.name.to_s.capitalize) %> Authenticator</h1>
|
|
9
|
+
<% set_active_nav "authenticator-#{authenticator.name.to_s}" %>
|
|
10
|
+
<% end %>
|
|
11
|
+
|
|
12
|
+
<p class='text'>
|
|
13
|
+
<%= authenticator.description %>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
<h2>Authentication Headers</h2>
|
|
17
|
+
<p class='text'>
|
|
18
|
+
The following headers are used to identify yourself to the API client. These should be
|
|
19
|
+
sent as standard HTTP headers with any API request.
|
|
20
|
+
</p>
|
|
21
|
+
<table class='table paramTable'>
|
|
22
|
+
<thead>
|
|
23
|
+
<tr>
|
|
24
|
+
<th width="60%">Header</th>
|
|
25
|
+
<th width="40%">Example</th>
|
|
26
|
+
</tr>
|
|
27
|
+
</thead>
|
|
28
|
+
<% for name, options in authenticator.headers %>
|
|
29
|
+
<tr>
|
|
30
|
+
<td>
|
|
31
|
+
<p>
|
|
32
|
+
<span class='paramTable__name'><%= name %></span>
|
|
33
|
+
</p>
|
|
34
|
+
<% if options[:description] %>
|
|
35
|
+
<p class='paramTable__description'><%= options[:description] %></p>
|
|
36
|
+
<% end %>
|
|
37
|
+
</td>
|
|
38
|
+
<td><%= options[:eg] || options[:example] %> </td>
|
|
39
|
+
</tr>
|
|
40
|
+
<% end %>
|
|
41
|
+
</table>
|
|
42
|
+
|
|
43
|
+
<h2>Errors</h2>
|
|
44
|
+
<p class='text'>
|
|
45
|
+
The errors listed below may be raised if any issues occur when verifying your
|
|
46
|
+
identity with the API.
|
|
47
|
+
</p>
|
|
48
|
+
<% if authenticator.errors.empty? %>
|
|
49
|
+
<p><em>There are no errors which can be raised.</em></p>
|
|
50
|
+
<% else %>
|
|
51
|
+
<%= partial "errors_table", :errors => authenticator.errors %>
|
|
52
|
+
<% end %>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<% set_page_title controller.friendly_name || controller.name %>
|
|
2
|
+
<% set_active_nav "controller-#{controller.name}" %>
|
|
3
|
+
<h1><%= controller.friendly_name || controller.name %></h1>
|
|
4
|
+
<% if controller.description %>
|
|
5
|
+
<p class='text'><%= controller.description %></p>
|
|
6
|
+
<% end %>
|
|
7
|
+
<h2>Action</h2>
|
|
8
|
+
<p class='text'>
|
|
9
|
+
The following actions are available. Choose from the list below
|
|
10
|
+
to view full details of how to access them.
|
|
11
|
+
</p>
|
|
12
|
+
<ul class='standardList'>
|
|
13
|
+
<% for action in controller.actions.values.select { |a| a.doc != false } %>
|
|
14
|
+
<li>
|
|
15
|
+
<a class='link' href='<%= path("controllers/#{action.controller.name}/#{action.name}") %>'><%= action.title || action.name %></a>
|
|
16
|
+
<p class='apiURL'><span><%= full_prefix %>/</span><b><%= action.controller.name %>/<%= action.name %></b></p>
|
|
17
|
+
<% if action.description %><p class='meta'><%= action.description %></p><% end %>
|
|
18
|
+
</li>
|
|
19
|
+
<% end %>
|
|
20
|
+
</ul>
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
<% set_page_title "Welcome" %>
|
|
2
|
+
<% set_active_nav "home" %>
|
|
3
|
+
<h1>Welcome to our API documentation</h1>
|
|
4
|
+
<p class='text'>
|
|
5
|
+
From here you can browse the full documentation for our HTTP
|
|
6
|
+
API. Our API is split into sections which you can browse using
|
|
7
|
+
the menu on the right. If you have any questions, you can
|
|
8
|
+
<a href='#'>contact our team</a> and we'll be happy to help out.
|
|
9
|
+
</p>
|
|
10
|
+
<p class='text'>
|
|
11
|
+
Before you get started, take a few minutes to review the
|
|
12
|
+
information below about how to interact with our API. It includes
|
|
13
|
+
information about how to send requests, what response data is
|
|
14
|
+
sent in and how to handle errors.
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<h2>Making requests</h2>
|
|
18
|
+
<p class='text'>
|
|
19
|
+
Our API works over the HTTP protocol with JSON. It is implemented
|
|
20
|
+
in an RPC-like manner and everything you can do with the API has
|
|
21
|
+
its own <em>action</em>.
|
|
22
|
+
</p>
|
|
23
|
+
<p class='text'>
|
|
24
|
+
All HTTP requests must be made over HTTPS to the URL shown on the
|
|
25
|
+
action's page in this documentation. All responses you receive from
|
|
26
|
+
the API will be returned in JSON. Requests should be made using the
|
|
27
|
+
<code>POST</code> method with any parameters encoded as JSON in the
|
|
28
|
+
body of the request.
|
|
29
|
+
</p>
|
|
30
|
+
|
|
31
|
+
<h2>Receiving responses</h2>
|
|
32
|
+
<p class='text'>
|
|
33
|
+
All responses will be returned to you encoded as JSON. You will always
|
|
34
|
+
receive a hash as the response which will look like the JSON below:
|
|
35
|
+
</p>
|
|
36
|
+
<pre class='code'>
|
|
37
|
+
{
|
|
38
|
+
<span class='jsonKey'>"status"</span>:<span class='jsonString'>"success"</span>,
|
|
39
|
+
<span class='jsonKey'>"time"</span>:<span class='jsonString'>0.123</span>,
|
|
40
|
+
<span class='jsonKey'>"flags"</span>:{
|
|
41
|
+
<span class='jsonComment'>... additional information about the request ...</span>
|
|
42
|
+
},
|
|
43
|
+
<span class='jsonKey'>"data"</span>:{
|
|
44
|
+
<span class='jsonComment'>... the data returned from the action ...</span>
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
</pre>
|
|
48
|
+
<p class='text'>
|
|
49
|
+
The <b>status</b> attribute will give you can indication about whether the
|
|
50
|
+
request was performed successfully or whether an error occurred. Values which
|
|
51
|
+
may be returned are shown below:
|
|
52
|
+
</p>
|
|
53
|
+
<ul class='standardList'>
|
|
54
|
+
<li>
|
|
55
|
+
<code>success</code> - this means that the request completed successfully
|
|
56
|
+
and returned the data that was expected.
|
|
57
|
+
</li>
|
|
58
|
+
<li>
|
|
59
|
+
<code>parameter-error</code> - the parameters provided for the action are
|
|
60
|
+
not valid and should be revised.
|
|
61
|
+
</li>
|
|
62
|
+
<li>
|
|
63
|
+
<code>error</code> - an error occurred that didn't fit into the above categories.
|
|
64
|
+
This will be accompanied with an error code, a descriptive message and further
|
|
65
|
+
attributes which may be useful. The actual potential errors for each action are
|
|
66
|
+
shown in the documentation.
|
|
67
|
+
</li>
|
|
68
|
+
</ul>
|
|
69
|
+
<p class='text'>
|
|
70
|
+
The <b>time</b> attribute shows how long the request took to complete on the
|
|
71
|
+
server side.
|
|
72
|
+
</p>
|
|
73
|
+
<p class='text'>
|
|
74
|
+
The <b>flags</b> attribute contains a hash of additional attributes
|
|
75
|
+
which are relevant to your request. For example, if you receive an array of data
|
|
76
|
+
it may be paginated and this pagination data will be returned in this
|
|
77
|
+
hash.
|
|
78
|
+
</p>
|
|
79
|
+
<p class='text'>
|
|
80
|
+
The <b>data</b> attribute contains the result of your request. Depending on the
|
|
81
|
+
status, this will either contain the data requested or details of any
|
|
82
|
+
error which has occurred.
|
|
83
|
+
</p>
|
|
84
|
+
<h3>A note about HTTP status code</h3>
|
|
85
|
+
<p class='text'>
|
|
86
|
+
The API does not generally use HTTP status codes to return information
|
|
87
|
+
about the outcome of a request. There are two supported statuses with
|
|
88
|
+
the API:
|
|
89
|
+
</p>
|
|
90
|
+
<ul class='standardList'>
|
|
91
|
+
<li>
|
|
92
|
+
<code>200 OK</code> - This is the code you'll usually receive.
|
|
93
|
+
It indicates that the response was successfully delivered and
|
|
94
|
+
returned to our service (although does not nessesary mean that
|
|
95
|
+
the action you were expecting was successful). Further status
|
|
96
|
+
information will be provided in the <code>status</code> attribute
|
|
97
|
+
on your response body.
|
|
98
|
+
</li>
|
|
99
|
+
<li>
|
|
100
|
+
<code>301 Moved Permanently</code> or <code>308 Permanent Redirect</code> -
|
|
101
|
+
This means that the API request should be sent to an alternative
|
|
102
|
+
URL. This may just mean you need to send your request using <code>https</code>
|
|
103
|
+
rather than <code>http</code> as the protocol.
|
|
104
|
+
</li>
|
|
105
|
+
<li>
|
|
106
|
+
<code>500 Internal Server Error</code> - This will be returned
|
|
107
|
+
when an error occurred within the API itself. This was not
|
|
108
|
+
anticipated by us and should be reported to us.
|
|
109
|
+
</li>
|
|
110
|
+
<li>
|
|
111
|
+
<code>503 Service Unavailable</code> - This will be returned if
|
|
112
|
+
API is currently unavailable for maintenance or other issue.
|
|
113
|
+
</li>
|
|
114
|
+
</ul>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title><%= page_title %> - API Documentation</title>
|
|
5
|
+
<link href='https://fonts.googleapis.com/css?family=Lato:400,700,900' rel='stylesheet' type='text/css'>
|
|
6
|
+
<link href='https://fonts.googleapis.com/css?family=Droid+Sans+Mono' rel='stylesheet' type='text/css'>
|
|
7
|
+
<link rel="stylesheet" href="<%= asset_path('reset.css') %>">
|
|
8
|
+
<link rel="stylesheet" href="<%= asset_path('style.css') %>">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<section class='sidebarBackground'></section>
|
|
12
|
+
<section class="sidebar">
|
|
13
|
+
<nav>
|
|
14
|
+
<ul>
|
|
15
|
+
<li>
|
|
16
|
+
<a href='<%= path(:root) %>' class="<%= active_nav == 'home' ? 'active' : '' %>">
|
|
17
|
+
Home
|
|
18
|
+
</a>
|
|
19
|
+
</li>
|
|
20
|
+
<% base.authenticators.select { |k,v| v.friendly_name }.each do |id, authenticator| %>
|
|
21
|
+
<li>
|
|
22
|
+
<a href='<%= path('authenticators/' + id.to_s) %>' class="<%= active_nav == "authenticator-" + id.to_s ? 'active' : '' %>">
|
|
23
|
+
<%= authenticator.friendly_name %>
|
|
24
|
+
</a>
|
|
25
|
+
</li>
|
|
26
|
+
<% end %>
|
|
27
|
+
<% for controller in base.controllers.select { |c| c.doc != false }.sort_by { |c| c.name.to_s } %>
|
|
28
|
+
<li>
|
|
29
|
+
<a href='<%= path("controllers/#{controller.name}") %>' class="<%= active_nav == "controller-#{controller.name}" ? 'active' : '' %>">
|
|
30
|
+
<%= controller.friendly_name || controller.name %>
|
|
31
|
+
</a>
|
|
32
|
+
</li>
|
|
33
|
+
<% end %>
|
|
34
|
+
</ul>
|
|
35
|
+
</nav>
|
|
36
|
+
</section>
|
|
37
|
+
<section class='content'>
|
|
38
|
+
<%= body %>
|
|
39
|
+
</section>
|
|
40
|
+
<footer class='footer'>
|
|
41
|
+
<p>Generated by Moonrope at <%= Time.now.strftime("%H:%M on %A %e %B %Y") %> for <%= git_version[0,6] %></p>
|
|
42
|
+
</footer>
|
|
43
|
+
<script src='https://code.jquery.com/jquery-1.12.0.min.js'></script>
|
|
44
|
+
<script src='<%= asset_path('try.js') %>'></script>
|
|
45
|
+
</body>
|
|
46
|
+
</html>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<% set_page_title "#{humanize(structure.name.capitalize)} Structure" %>
|
|
2
|
+
|
|
3
|
+
<h1><%= humanize(structure.name.capitalize) %> Structure</h1>
|
|
4
|
+
|
|
5
|
+
<h2>Base Attributes</h2>
|
|
6
|
+
<%= partial 'structure_attributes_list', :structure => structure, :attributes => structure.attributes[:basic].select { |a| a.doc != false } %>
|
|
7
|
+
|
|
8
|
+
<% full_attrs = structure.attributes[:full].select { |a| a.doc != false } %>
|
|
9
|
+
<% unless full_attrs.empty? %>
|
|
10
|
+
<h2>Extended Attributes</h2>
|
|
11
|
+
<%= partial 'structure_attributes_list', :structure => structure, :attributes => full_attrs %>
|
|
12
|
+
<% end %>
|
|
13
|
+
|
|
14
|
+
<% if !structure.attributes[:expansion].empty? || !structure.expansions.empty? %>
|
|
15
|
+
<h2>Expansions</h2>
|
|
16
|
+
<p class='text'>
|
|
17
|
+
Expansions are embedded structures of other objects that are related to the structure
|
|
18
|
+
that you're viewing. Which expansions are returned by a specific action are shown on that
|
|
19
|
+
action's documentation however some actions allow you to choose which expansions are
|
|
20
|
+
returned.
|
|
21
|
+
</p>
|
|
22
|
+
<%= partial 'structure_attributes_list', :structure => structure, :attributes => structure.attributes[:expansion], :expansions => structure.expansions.select { |k,v| v[:doc] != false} %>
|
|
23
|
+
<% end %>
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require 'rack/test'
|
|
3
|
+
require 'moonrope'
|
|
4
|
+
|
|
5
|
+
class Test::Unit::TestCase
|
|
6
|
+
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def make_rack_env_hash(path, params = {}, other_env = {})
|
|
10
|
+
request = Rack::Test::Session.new(nil)
|
|
11
|
+
request.send :env_for, path, {:params => params, :method => 'POST'}.merge(other_env)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
#
|
|
17
|
+
# A fake base object for models
|
|
18
|
+
#
|
|
19
|
+
class ModelBase
|
|
20
|
+
def initialize(attributes = {})
|
|
21
|
+
attributes.each do |key, value|
|
|
22
|
+
instance_variable_set("@#{key}", value)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class Animal < ModelBase
|
|
28
|
+
attr_accessor :id, :name, :color, :user
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class User < ModelBase
|
|
32
|
+
attr_accessor :id, :username, :private_code, :admin
|
|
33
|
+
def animals
|
|
34
|
+
@animals ||= []
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class UserWithUnderscore < User
|
|
39
|
+
class << self
|
|
40
|
+
def name
|
|
41
|
+
s = Struct.new(:underscore, :to_s).new
|
|
42
|
+
s.to_s = 'User'
|
|
43
|
+
s.underscore = 'user'
|
|
44
|
+
s
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
#
|
|
50
|
+
# A fake request class for use in some tests
|
|
51
|
+
#
|
|
52
|
+
class FakeRequest
|
|
53
|
+
|
|
54
|
+
def initialize(options = {})
|
|
55
|
+
@options = options
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def params
|
|
59
|
+
@params ||= Moonrope::ParamSet.new(@options[:params] || {})
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def version
|
|
63
|
+
@options[:version]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def identity
|
|
67
|
+
@options[:identity]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def action
|
|
71
|
+
nil
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
#
|
|
77
|
+
# Require all tests
|
|
78
|
+
#
|
|
79
|
+
Dir[File.expand_path("../tests/**/*.rb", __FILE__)].each do |filename|
|
|
80
|
+
require filename
|
|
81
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
class ActionAccessTest < Test::Unit::TestCase
|
|
2
|
+
|
|
3
|
+
def setup
|
|
4
|
+
@base = Moonrope::Base.new do
|
|
5
|
+
authenticator :default do
|
|
6
|
+
rule :default, "AccessDenied" do
|
|
7
|
+
identity == :admin
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
rule :anonymous, "MustBeAnonymous" do
|
|
11
|
+
identity.nil?
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
@controller = Moonrope::Controller.new(@base, :users)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_action_uses_default_access_rule_by_default
|
|
19
|
+
action = Moonrope::Action.new(@controller, :list)
|
|
20
|
+
# no authentication has been provided
|
|
21
|
+
assert_equal false, action.check_access
|
|
22
|
+
# authentication which is not correct
|
|
23
|
+
authenticated_request = FakeRequest.new(:identity => :dave)
|
|
24
|
+
assert_equal false, action.check_access(authenticated_request)
|
|
25
|
+
# authentication which is correct
|
|
26
|
+
authenticated_request = FakeRequest.new(:identity => :admin)
|
|
27
|
+
assert_equal true, action.check_access(authenticated_request)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_action_can_use_controller_rule
|
|
31
|
+
controller = Moonrope::Controller.new(@base, :users) do
|
|
32
|
+
access_rule :anonymous
|
|
33
|
+
end
|
|
34
|
+
action = Moonrope::Action.new(controller, :list)
|
|
35
|
+
# anonymous is ok
|
|
36
|
+
assert_equal true, action.check_access
|
|
37
|
+
# with a user is not
|
|
38
|
+
authenticated_request = FakeRequest.new(:identity => :dave)
|
|
39
|
+
assert_equal false, action.check_access(authenticated_request)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def test_action_can_use_action_rule
|
|
43
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
44
|
+
access_rule :anonymous
|
|
45
|
+
end
|
|
46
|
+
# anonymous is ok
|
|
47
|
+
assert_equal true, action.check_access
|
|
48
|
+
# with a user is not
|
|
49
|
+
authenticated_request = FakeRequest.new(:identity => :dave)
|
|
50
|
+
assert_equal false, action.check_access(authenticated_request)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def test_that_invalid_rule_names_raise_errors
|
|
54
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
55
|
+
access_rule :missing
|
|
56
|
+
end
|
|
57
|
+
# anonymous is ok
|
|
58
|
+
assert_raises Moonrope::Errors::MissingAccessRule do
|
|
59
|
+
action.check_access
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end
|
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
class ActionsTest < Test::Unit::TestCase
|
|
2
|
+
|
|
3
|
+
def setup
|
|
4
|
+
@base = Moonrope::Base.new
|
|
5
|
+
@controller = Moonrope::Controller.new(@base, :users)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def test_basic_definition
|
|
9
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
10
|
+
description "An example action with a description"
|
|
11
|
+
end
|
|
12
|
+
assert action.is_a?(Moonrope::Action)
|
|
13
|
+
assert_equal :list, action.name
|
|
14
|
+
assert action.description.is_a?(String)
|
|
15
|
+
assert action.description.length > 0
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_defining_params
|
|
19
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
20
|
+
param :page
|
|
21
|
+
param :limit
|
|
22
|
+
end
|
|
23
|
+
assert action.params.is_a?(Hash)
|
|
24
|
+
assert_equal [:page, :limit], action.params.keys
|
|
25
|
+
assert action.params.values.all? { |p| p.is_a?(Hash) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_using_shares
|
|
29
|
+
controller = Moonrope::Controller.new(@base, :users) do
|
|
30
|
+
shared_action :user_properties do
|
|
31
|
+
error 'InvalidUsername', "Some description"
|
|
32
|
+
param :username, "Blah"
|
|
33
|
+
param :first_name
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
action :create do
|
|
37
|
+
use :user_properties
|
|
38
|
+
param :last_name
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
action = controller / :create
|
|
43
|
+
assert_equal(Hash, action.params.class)
|
|
44
|
+
assert_equal(Hash, action.params[:username].class)
|
|
45
|
+
assert_equal("Blah", action.params[:username][:description])
|
|
46
|
+
assert_equal(Hash, action.errors['InvalidUsername'].class)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def test_action
|
|
50
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
51
|
+
action { true }
|
|
52
|
+
end
|
|
53
|
+
assert action.actions.is_a?(Array)
|
|
54
|
+
assert action.actions.first.is_a?(Proc)
|
|
55
|
+
assert_equal true, action.actions.first.call
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_calling_actions
|
|
59
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
60
|
+
action { [1,2,3,4] }
|
|
61
|
+
end
|
|
62
|
+
assert result = action.execute
|
|
63
|
+
assert result.is_a?(Moonrope::ActionResult)
|
|
64
|
+
assert_equal 'success', result.status
|
|
65
|
+
assert_equal [1,2,3,4], result.data
|
|
66
|
+
assert_equal Float, result.time.class
|
|
67
|
+
assert_equal({}, result.flags)
|
|
68
|
+
assert_equal({}, result.headers)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def test_structure_method_can_be_called
|
|
72
|
+
# Create a new structure to test with
|
|
73
|
+
user_structure = Moonrope::Structure.new(@base, :user) do
|
|
74
|
+
basic { {:id => o.id, :username => o.username}}
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Create an action which uses this structure
|
|
78
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
79
|
+
action do
|
|
80
|
+
user = User.new(:id => 1, :username => 'adamcooke')
|
|
81
|
+
structure user_structure, user
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Test the structure was returned
|
|
86
|
+
assert result = action.execute
|
|
87
|
+
assert result.is_a?(Moonrope::ActionResult), "result is not a ActionResult"
|
|
88
|
+
assert_equal 1, result.data[:id]
|
|
89
|
+
assert_equal 'adamcooke', result.data[:username]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def test_structure_methods_can_be_called_with_opts_from_dsl
|
|
93
|
+
# Create a new structure to test with
|
|
94
|
+
user_structure = Moonrope::Structure.new(@base, :user) do
|
|
95
|
+
basic :id
|
|
96
|
+
full :username
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Create an action which uses this structure
|
|
100
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
101
|
+
returns :hash, :structure => :user, :structure_opts => {:full => true}
|
|
102
|
+
action do
|
|
103
|
+
user = User.new(:id => 1, :username => 'adamcooke')
|
|
104
|
+
structure user_structure, user, :return => true
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Test the structure was returned
|
|
109
|
+
assert result = action.execute
|
|
110
|
+
assert_equal 1, result.data[:id]
|
|
111
|
+
assert_equal 'adamcooke', result.data[:username]
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def test_default_params
|
|
115
|
+
action = Moonrope::Action.new(@controller, :default_params_test) do
|
|
116
|
+
param :page, :default => 1234
|
|
117
|
+
param :limit
|
|
118
|
+
action { {:page => params.page, :limit => params.limit} }
|
|
119
|
+
end
|
|
120
|
+
result = action.execute
|
|
121
|
+
assert_equal({'page' => 1234}, action.default_params)
|
|
122
|
+
assert_equal 1234, result.data[:page]
|
|
123
|
+
assert_equal nil, result.data[:limit]
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def test_before_filters_are_executed
|
|
127
|
+
controller = Moonrope::Controller.new(@base, :users) do
|
|
128
|
+
before { set_flag :before_all, true }
|
|
129
|
+
before(:other) { set_flag :before_other, true }
|
|
130
|
+
before(:list) { set_flag :before_list, true }
|
|
131
|
+
before(:list, :potato) { set_flag :before_list_and_potato, true }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
action = Moonrope::Action.new(controller, :list) do
|
|
135
|
+
action { true }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
assert result = action.execute
|
|
139
|
+
assert_equal true, result.flags[:before_all]
|
|
140
|
+
assert_equal true, result.flags[:before_list]
|
|
141
|
+
assert_equal true, result.flags[:before_list_and_potato]
|
|
142
|
+
assert_equal nil, result.flags[:before_other]
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def test_result_can_be_expressed_as_a_hash
|
|
146
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
147
|
+
action { [1,2,3] }
|
|
148
|
+
end
|
|
149
|
+
assert result = action.execute
|
|
150
|
+
assert hash = result.to_hash
|
|
151
|
+
assert hash.is_a?(Hash), "result.to_hash does not return a hash"
|
|
152
|
+
assert_equal 'success', hash[:status]
|
|
153
|
+
assert hash[:time].is_a?(Float)
|
|
154
|
+
assert hash[:flags].is_a?(Hash)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def test_result_can_be_expressed_as_json
|
|
158
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
159
|
+
action { [1,2,3] }
|
|
160
|
+
end
|
|
161
|
+
assert result = action.execute
|
|
162
|
+
assert json = result.to_json
|
|
163
|
+
assert json.is_a?(String)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def test_that_param_validation_happens_on_executin
|
|
167
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
168
|
+
param :page, "Page number", :required => true
|
|
169
|
+
action { [1,2,3] }
|
|
170
|
+
end
|
|
171
|
+
assert result = action.execute
|
|
172
|
+
assert_equal 'parameter-error', result.status
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def test_actions_params_can_be_validated_for_presence
|
|
176
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
177
|
+
param :page, "Page number", :required => true
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# request without the param
|
|
181
|
+
assert_raises Moonrope::Errors::ParameterError do
|
|
182
|
+
action.validate_parameters(Moonrope::ParamSet.new)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# request with the param
|
|
186
|
+
assert_equal true, action.validate_parameters(Moonrope::ParamSet.new('page' => 1))
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def test_actions_params_can_be_validated_for_type
|
|
190
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
191
|
+
param :page, "Page number", :type => Integer
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# request with a string valuee
|
|
195
|
+
assert_raises Moonrope::Errors::ParameterError do
|
|
196
|
+
action.validate_parameters(Moonrope::ParamSet.new('page' => 'stringy'))
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# request with an integer value
|
|
200
|
+
assert_equal true, action.validate_parameters(Moonrope::ParamSet.new('page' => 1))
|
|
201
|
+
|
|
202
|
+
# request with an nil value
|
|
203
|
+
assert_equal true, action.validate_parameters(Moonrope::ParamSet.new('page' => nil))
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def test_actions_params_can_be_validated_for_boolean_types
|
|
207
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
208
|
+
param :hungry, "Are you hungry", :type => :boolean
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# request with a string valuee
|
|
212
|
+
assert_raises Moonrope::Errors::ParameterError do
|
|
213
|
+
action.validate_parameters(Moonrope::ParamSet.new('hungry' => 'randomstring'))
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
assert_raises Moonrope::Errors::ParameterError do
|
|
217
|
+
action.validate_parameters(Moonrope::ParamSet.new('hungry' => 2))
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
assert_raises Moonrope::Errors::ParameterError do
|
|
221
|
+
action.validate_parameters(Moonrope::ParamSet.new('hungry' => 123))
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# request with an boolean value
|
|
225
|
+
assert_equal true, action.validate_parameters(Moonrope::ParamSet.new('hungry' => true))
|
|
226
|
+
assert_equal true, action.validate_parameters(Moonrope::ParamSet.new('hungry' => false))
|
|
227
|
+
|
|
228
|
+
# request with string values
|
|
229
|
+
set = Moonrope::ParamSet.new('hungry' => 'true')
|
|
230
|
+
assert_equal true, action.validate_parameters(set)
|
|
231
|
+
assert_equal true, set.hungry
|
|
232
|
+
|
|
233
|
+
set = Moonrope::ParamSet.new('hungry' => 'false')
|
|
234
|
+
assert_equal true, action.validate_parameters(set)
|
|
235
|
+
assert_equal false, set.hungry
|
|
236
|
+
|
|
237
|
+
# request with an numeric values
|
|
238
|
+
assert_equal true, action.validate_parameters(Moonrope::ParamSet.new('hungry' => 1))
|
|
239
|
+
assert_equal true, action.validate_parameters(Moonrope::ParamSet.new('hungry' => 0))
|
|
240
|
+
|
|
241
|
+
# request with nil vlaues
|
|
242
|
+
assert_equal true, action.validate_parameters(Moonrope::ParamSet.new('hungry' => nil))
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def test_actions_params_can_have_symbols_as_types_which_do_nothing
|
|
246
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
247
|
+
param :created_at, "Timestamp", :type => :timestamp
|
|
248
|
+
end
|
|
249
|
+
assert_equal true, action.validate_parameters(Moonrope::ParamSet.new('created_at' => 'something'))
|
|
250
|
+
assert_equal true, action.validate_parameters(Moonrope::ParamSet.new('created_at' => nil))
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def test_actions_params_can_be_validated_for_regex_matches
|
|
254
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
255
|
+
param :username, "Username", :regex => /\A[a-z]+\z/
|
|
256
|
+
end
|
|
257
|
+
# request with a nil value
|
|
258
|
+
assert_equal true, action.validate_parameters(Moonrope::ParamSet.new)
|
|
259
|
+
|
|
260
|
+
# request with a matching value
|
|
261
|
+
assert_equal true, action.validate_parameters(Moonrope::ParamSet.new('username' => 'adam'))
|
|
262
|
+
|
|
263
|
+
# request with a string valuee
|
|
264
|
+
assert_raises Moonrope::Errors::ParameterError do
|
|
265
|
+
action.validate_parameters(Moonrope::ParamSet.new('username' => 'invalid-username1234'))
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def test_actions_params_can_be_validated_for_option_matches
|
|
270
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
271
|
+
param :sort_by, :options => ["name", "age"]
|
|
272
|
+
end
|
|
273
|
+
# request with a nil value
|
|
274
|
+
assert_equal true, action.validate_parameters(Moonrope::ParamSet.new)
|
|
275
|
+
|
|
276
|
+
# request with a matching value
|
|
277
|
+
assert_equal true, action.validate_parameters(Moonrope::ParamSet.new('sort_by' => 'name'))
|
|
278
|
+
|
|
279
|
+
# request with a string valuee
|
|
280
|
+
assert_raises Moonrope::Errors::ParameterError do
|
|
281
|
+
action.validate_parameters(Moonrope::ParamSet.new('sort_by' => 'somethingelse'))
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def test_actions_can_raise_errors
|
|
287
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
288
|
+
action do
|
|
289
|
+
error :not_found, "Something wasn't found"
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
assert result = action.execute
|
|
293
|
+
assert_equal "not-found", result.status
|
|
294
|
+
assert_equal({:message => "Something wasn't found"}, result.data)
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def test_actions_can_raise_structured_errors
|
|
298
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
299
|
+
action do
|
|
300
|
+
structured_error 'feature-disabled', "The feature you have requested is not currently available for this resource.", :number => 1000
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
assert result = action.execute
|
|
304
|
+
assert_equal "error", result.status
|
|
305
|
+
assert_equal("feature-disabled", result.data[:code])
|
|
306
|
+
assert_equal("The feature you have requested is not currently available for this resource.", result.data[:message])
|
|
307
|
+
assert_equal(1000, result.data[:number])
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def test_actions_can_raise_structured_errors_through_the_error_method
|
|
311
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
312
|
+
action do
|
|
313
|
+
error :structured_error, 'feature-disabled', "The feature you have requested is not currently available for this resource."
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
assert result = action.execute
|
|
317
|
+
assert_equal "error", result.status
|
|
318
|
+
assert_equal("feature-disabled", result.data[:code])
|
|
319
|
+
assert_equal("The feature you have requested is not currently available for this resource.", result.data[:message])
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def test_actions_can_raise_structured_errors_through_the_error_method_using_a_string
|
|
323
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
324
|
+
action do
|
|
325
|
+
error 'feature-disabled', "The feature you have requested is not currently available for this resource."
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
assert result = action.execute
|
|
329
|
+
assert_equal "error", result.status
|
|
330
|
+
assert_equal("feature-disabled", result.data[:code])
|
|
331
|
+
assert_equal("The feature you have requested is not currently available for this resource.", result.data[:message])
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def test_actions_can_raise_structured_errors_referencing_action_errors
|
|
335
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
336
|
+
error "NoWidgetsFound", "No widgets were found with level {widget_level}"
|
|
337
|
+
action do
|
|
338
|
+
error 'NoWidgetsFound', :widget_level => 42
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
assert result = action.execute
|
|
342
|
+
assert_equal "error", result.status
|
|
343
|
+
assert_equal "NoWidgetsFound", result.data[:code]
|
|
344
|
+
assert_equal "No widgets were found with level 42", result.data[:message]
|
|
345
|
+
assert_equal 42, result.data[:widget_level]
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
class DummyError < StandardError; end
|
|
349
|
+
|
|
350
|
+
def test_catching_external_errors
|
|
351
|
+
@controller.base.register_external_error DummyError do |exp, res|
|
|
352
|
+
res.status = 'dummy-error'
|
|
353
|
+
res.data = {:message => exp.message}
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
357
|
+
action do
|
|
358
|
+
raise DummyError, "Something happened"
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
assert result = action.execute
|
|
363
|
+
assert_equal 'dummy-error', result.status
|
|
364
|
+
assert_equal({:message => 'Something happened'}, result.data)
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
class DummyError2 < StandardError; end
|
|
368
|
+
|
|
369
|
+
def test_non_defined_errors_are_raised
|
|
370
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
371
|
+
action do
|
|
372
|
+
raise DummyError2, "Something happened"
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
assert_raises(DummyError2) { action.execute }
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
def test_can_change_full_attribute
|
|
379
|
+
# can't change when no ops
|
|
380
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
381
|
+
returns :hash, :structure => :animal
|
|
382
|
+
end
|
|
383
|
+
assert_equal(false, action.can_change_full?)
|
|
384
|
+
|
|
385
|
+
# can change when paramable is true
|
|
386
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
387
|
+
returns :hash, :structure => :animal, :structure_opts => {:paramable => true}
|
|
388
|
+
end
|
|
389
|
+
assert_equal(true, action.can_change_full?)
|
|
390
|
+
|
|
391
|
+
# can change when specified
|
|
392
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
393
|
+
returns :hash, :structure => :animal, :structure_opts => {:paramable => {:full => true}}
|
|
394
|
+
end
|
|
395
|
+
assert_equal(true, action.can_change_full?)
|
|
396
|
+
|
|
397
|
+
# can't change when not specified
|
|
398
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
399
|
+
returns :hash, :structure => :animal, :structure_opts => {:paramable => {}}
|
|
400
|
+
end
|
|
401
|
+
assert_equal(false, action.can_change_full?)
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
def test_includes_full_attributes
|
|
405
|
+
#not included by default
|
|
406
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
407
|
+
returns :hash, :structure => :animal
|
|
408
|
+
end
|
|
409
|
+
assert_equal(false, action.includes_full_attributes?)
|
|
410
|
+
|
|
411
|
+
# not included when paramable is just true
|
|
412
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
413
|
+
returns :hash, :structure => :animal, :structure_opts => {:paramable => true}
|
|
414
|
+
end
|
|
415
|
+
assert_equal(false, action.includes_full_attributes?)
|
|
416
|
+
|
|
417
|
+
# included when paramable sets the default to true
|
|
418
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
419
|
+
returns :hash, :structure => :animal, :structure_opts => {:paramable => {:full => true}}
|
|
420
|
+
end
|
|
421
|
+
assert_equal(true, action.includes_full_attributes?)
|
|
422
|
+
|
|
423
|
+
# included when it's full anyway
|
|
424
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
425
|
+
returns :hash, :structure => :animal, :structure_opts => {:full => true}
|
|
426
|
+
end
|
|
427
|
+
assert_equal(true, action.includes_full_attributes?)
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def test_can_change_expansions_attribute
|
|
432
|
+
# can't change when no ops
|
|
433
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
434
|
+
returns :hash, :structure => :animal
|
|
435
|
+
end
|
|
436
|
+
assert_equal(false, action.can_change_expansions?)
|
|
437
|
+
|
|
438
|
+
# can change when paramable is true
|
|
439
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
440
|
+
returns :hash, :structure => :animal, :structure_opts => {:paramable => true}
|
|
441
|
+
end
|
|
442
|
+
assert_equal(true, action.can_change_expansions?)
|
|
443
|
+
|
|
444
|
+
# can change when specified
|
|
445
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
446
|
+
returns :hash, :structure => :animal, :structure_opts => {:paramable => {:expansions => true}}
|
|
447
|
+
end
|
|
448
|
+
assert_equal(true, action.can_change_expansions?)
|
|
449
|
+
|
|
450
|
+
# can't change when not specified
|
|
451
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
452
|
+
returns :hash, :structure => :animal, :structure_opts => {:paramable => {}}
|
|
453
|
+
end
|
|
454
|
+
assert_equal(false, action.can_change_expansions?)
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
def test_includes_expansion
|
|
458
|
+
#not included by default
|
|
459
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
460
|
+
returns :hash, :structure => :animal
|
|
461
|
+
end
|
|
462
|
+
assert_equal(false, action.includes_expansion?(:blah))
|
|
463
|
+
|
|
464
|
+
# not included when paramable is just true
|
|
465
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
466
|
+
returns :hash, :structure => :animal, :structure_opts => {:paramable => true}
|
|
467
|
+
end
|
|
468
|
+
assert_equal(false, action.includes_expansion?(:blah))
|
|
469
|
+
|
|
470
|
+
# included when paramable sets the default to true
|
|
471
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
472
|
+
returns :hash, :structure => :animal, :structure_opts => {:paramable => {:expansions => true}}
|
|
473
|
+
end
|
|
474
|
+
assert_equal(true, action.includes_expansion?(:blah))
|
|
475
|
+
|
|
476
|
+
# included when it's expansions anyway
|
|
477
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
478
|
+
returns :hash, :structure => :animal, :structure_opts => {:expansions => true}
|
|
479
|
+
end
|
|
480
|
+
assert_equal(true, action.includes_expansion?(:blah))
|
|
481
|
+
|
|
482
|
+
# included when expansions is an array
|
|
483
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
484
|
+
returns :hash, :structure => :animal, :structure_opts => {:expansions => [:blah]}
|
|
485
|
+
end
|
|
486
|
+
assert_equal(true, action.includes_expansion?(:blah))
|
|
487
|
+
assert_equal(false, action.includes_expansion?(:another))
|
|
488
|
+
|
|
489
|
+
# included when paramable expansions is an array
|
|
490
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
491
|
+
returns :hash, :structure => :animal, :structure_opts => {:paramable => {:expansions => [:blah]}}
|
|
492
|
+
end
|
|
493
|
+
assert_equal(true, action.includes_expansion?(:blah))
|
|
494
|
+
assert_equal(false, action.includes_expansion?(:another))
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
def test_available_expansions_array_on_actions
|
|
498
|
+
# if an array is provided, it should return the items in the array
|
|
499
|
+
action = Moonrope::Action.new(@controller, :list) do
|
|
500
|
+
returns :hash, :structure => :animal, :structure_opts => {:paramable => {:expansions => [:user]}}
|
|
501
|
+
end
|
|
502
|
+
assert_equal([:user], action.available_expansions)
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
def test_that_param_can_copy_data_from_structures
|
|
506
|
+
base = Moonrope::Base.new do
|
|
507
|
+
structure :user do
|
|
508
|
+
basic :username, "The username for the user", :type => String, :eg => 123
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
controller :users do
|
|
512
|
+
action :save do
|
|
513
|
+
param :username, :from_structure => :user
|
|
514
|
+
end
|
|
515
|
+
end
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
action = base/:users/:save
|
|
519
|
+
assert_equal "The username for the user", action.params[:username][:description]
|
|
520
|
+
assert_equal String, action.params[:username][:type]
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
end
|