moonrope 1.4.1 → 2.0.0

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.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +9 -0
  3. data/Gemfile.lock +47 -0
  4. data/MIT-LICENCE +20 -0
  5. data/README.md +24 -0
  6. data/bin/moonrope +28 -0
  7. data/docs/authentication.md +114 -0
  8. data/docs/controllers.md +106 -0
  9. data/docs/exceptions.md +27 -0
  10. data/docs/introduction.md +29 -0
  11. data/docs/structures.md +214 -0
  12. data/example/authentication.rb +50 -0
  13. data/example/controllers/meta_controller.rb +14 -0
  14. data/example/controllers/users_controller.rb +92 -0
  15. data/example/structures/pet_structure.rb +12 -0
  16. data/example/structures/user_structure.rb +35 -0
  17. data/html/assets/lock.svg +3 -0
  18. data/html/assets/reset.css +101 -0
  19. data/html/assets/style.css +348 -0
  20. data/html/assets/tool.svg +4 -0
  21. data/html/assets/try.js +151 -0
  22. data/html/authenticators/default.html +191 -0
  23. data/html/controllers/meta/version.html +144 -0
  24. data/html/controllers/meta.html +73 -0
  25. data/html/controllers/users/create.html +341 -0
  26. data/html/controllers/users/list.html +348 -0
  27. data/html/controllers/users/show.html +261 -0
  28. data/html/controllers/users/update.html +387 -0
  29. data/html/controllers/users.html +93 -0
  30. data/html/index.html +166 -0
  31. data/html/moonrope.txt +0 -0
  32. data/html/structures/pet.html +176 -0
  33. data/html/structures/user.html +338 -0
  34. data/lib/moonrope/action.rb +165 -37
  35. data/lib/moonrope/authenticator.rb +39 -0
  36. data/lib/moonrope/base.rb +24 -6
  37. data/lib/moonrope/controller.rb +4 -2
  38. data/lib/moonrope/doc_context.rb +94 -0
  39. data/lib/moonrope/doc_server.rb +123 -0
  40. data/lib/moonrope/dsl/action_dsl.rb +159 -9
  41. data/lib/moonrope/dsl/authenticator_dsl.rb +31 -0
  42. data/lib/moonrope/dsl/base_dsl.rb +21 -18
  43. data/lib/moonrope/dsl/controller_dsl.rb +60 -9
  44. data/lib/moonrope/dsl/filterable_dsl.rb +27 -0
  45. data/lib/moonrope/dsl/structure_dsl.rb +27 -2
  46. data/lib/moonrope/errors.rb +3 -0
  47. data/lib/moonrope/eval_environment.rb +82 -3
  48. data/lib/moonrope/eval_helpers/filter_helper.rb +82 -0
  49. data/lib/moonrope/eval_helpers.rb +28 -5
  50. data/lib/moonrope/guard.rb +35 -0
  51. data/lib/moonrope/html_generator.rb +65 -0
  52. data/lib/moonrope/param_set.rb +11 -1
  53. data/lib/moonrope/rack_middleware.rb +1 -1
  54. data/lib/moonrope/railtie.rb +31 -14
  55. data/lib/moonrope/request.rb +25 -14
  56. data/lib/moonrope/structure.rb +74 -11
  57. data/lib/moonrope/structure_attribute.rb +15 -0
  58. data/lib/moonrope/version.rb +1 -1
  59. data/lib/moonrope.rb +5 -4
  60. data/moonrope.gemspec +21 -0
  61. data/spec/spec_helper.rb +32 -0
  62. data/spec/specs/action_spec.rb +455 -0
  63. data/spec/specs/base_spec.rb +29 -0
  64. data/spec/specs/controller_spec.rb +31 -0
  65. data/spec/specs/param_set_spec.rb +31 -0
  66. data/templates/basic/_action_form.erb +77 -0
  67. data/templates/basic/_errors_table.erb +32 -0
  68. data/templates/basic/_structure_attributes_list.erb +55 -0
  69. data/templates/basic/action.erb +168 -0
  70. data/templates/basic/assets/lock.svg +3 -0
  71. data/templates/basic/assets/reset.css +101 -0
  72. data/templates/basic/assets/style.css +348 -0
  73. data/templates/basic/assets/tool.svg +4 -0
  74. data/templates/basic/assets/try.js +151 -0
  75. data/templates/basic/authenticator.erb +51 -0
  76. data/templates/basic/controller.erb +20 -0
  77. data/templates/basic/index.erb +114 -0
  78. data/templates/basic/layout.erb +46 -0
  79. data/templates/basic/structure.erb +23 -0
  80. data/test/test_helper.rb +81 -0
  81. data/test/tests/action_access_test.rb +63 -0
  82. data/test/tests/actions_test.rb +524 -0
  83. data/test/tests/authenticators_test.rb +87 -0
  84. data/test/tests/base_test.rb +35 -0
  85. data/test/tests/controllers_test.rb +49 -0
  86. data/test/tests/eval_environment_test.rb +136 -0
  87. data/test/tests/evel_helpers_test.rb +60 -0
  88. data/test/tests/examples_test.rb +11 -0
  89. data/test/tests/helpers_test.rb +97 -0
  90. data/test/tests/param_set_test.rb +44 -0
  91. data/test/tests/rack_middleware_test.rb +109 -0
  92. data/test/tests/request_test.rb +232 -0
  93. data/test/tests/structures_param_extensions_test.rb +159 -0
  94. data/test/tests/structures_test.rb +335 -0
  95. metadata +82 -48
@@ -0,0 +1,151 @@
1
+ $(document).ready(function() {
2
+
3
+ $form = $('form.tryForm')
4
+
5
+ // Get all fields which will be added as headers
6
+ var headerFields = $('input.headerField', $form)
7
+
8
+ // Add stored values to header fields
9
+ if(typeof(Storage) !== "undefined") {
10
+ headerFields.each(function() {
11
+ $field = $(this)
12
+ $field.val(localStorage.getItem("header__" + $(this).attr('name')))
13
+ })
14
+
15
+ }
16
+
17
+
18
+ //
19
+ // Form submission
20
+ //
21
+ $form.on('submit', function() {
22
+
23
+ // Gets values used to make up the URL which should be
24
+ // requested for this request.
25
+ var host = $("input[name=host]", $(this)).val()
26
+ var version = $("input[name=version]", $(this)).val()
27
+ var controller = $("input[name=controller]", $(this)).val()
28
+ var action = $("input[name=action]", $(this)).val()
29
+ var url = host + "/api/" + version + "/" + controller + "/" + action
30
+ // Get the output box ready for use
31
+ var outputBox = $('.tryForm__output', $(this))
32
+ // Create a hash fo all parameters which will be submitted
33
+ var parameters = {}
34
+ $('input.paramField').each(function(){
35
+ $this = $(this)
36
+ value = $this.val()
37
+ type = $this.data('type')
38
+ name = $this.attr('name')
39
+ if(value.length) {
40
+ if(type == 'Integer') {
41
+ parameters[name] = parseInt(value);
42
+ } else if (type == "Hash" || type == "Array") {
43
+ parameters[name] = JSON.parse(value);
44
+ } else {
45
+ parameters[name] = value;
46
+ }
47
+ }
48
+ });
49
+
50
+ // Include/exclude full attributes as needed
51
+ var fullAttrsCheckbox = $('#full_attrs')
52
+ if(fullAttrsCheckbox.length) {
53
+ parameters['_full'] = !!fullAttrsCheckbox.prop('checked')
54
+ }
55
+
56
+ // Include/exclude expansions
57
+ var expansionCheckboxes = $('.tryForm__expansions')
58
+ if(expansionCheckboxes.length) {
59
+ parameters['_expansions'] = []
60
+ expansionCheckboxes.each(function() {
61
+ $this = $(this)
62
+ name = $(this).attr('name')
63
+ if($(this).prop('checked')) {
64
+ parameters['_expansions'].push(name)
65
+ }
66
+ })
67
+ }
68
+
69
+ // Make the AJAX request
70
+ $.ajax({
71
+ url: url,
72
+ method: 'POST',
73
+ contentType: 'application/json',
74
+ data: JSON.stringify(parameters),
75
+ beforeSend: function(xhr) {
76
+ // Add any headers which have been added
77
+ headerFields.each(function() {
78
+ $field = $(this)
79
+ name = $field.attr('name')
80
+ value = $field.val()
81
+ if(typeof(Storage) !== "undefined") {
82
+ localStorage.setItem("header__" + name, value)
83
+ }
84
+ if(value.length) {
85
+ xhr.setRequestHeader(name, $field.val())
86
+ }
87
+ })
88
+ },
89
+ success: function(data) {
90
+ // Success means that we got a 200 OK which means we can be pretty
91
+ // sure that we've got a moonrope response.
92
+ if(data.status == "success") {
93
+ outputBox.addClass('tryForm__output--success').removeClass('tryForm__output--error')
94
+ } else {
95
+ outputBox.addClass('tryForm__output--error').removeClass('tryForm__output--success')
96
+ }
97
+ outputBox.text(JSON.stringify(data, null, 4))
98
+ outputBox.show()
99
+ },
100
+ error: function() {
101
+ // Errors which occurr aren't very well reported at the moment.
102
+ // They should be.
103
+ outputBox.show()
104
+ outputBox.text("Failed to make request.")
105
+ outputBox.addClass('tryForm__output--error').removeClass('tryForm__output--success')
106
+ }
107
+ })
108
+ return false
109
+ });
110
+
111
+ //
112
+ // Open the try form
113
+ //
114
+ $('p.tryFormActivate a').on('click', function() {
115
+ $form = $('form.tryForm')
116
+ $parent = $(this).parents('p')
117
+ $form.show('fast')
118
+ $parent.hide()
119
+ return false
120
+ });
121
+
122
+ //
123
+ // Close the try form
124
+ //
125
+ $('button.tryFormCancel').on('click', function() {
126
+ $form = $('form.tryForm')
127
+ $parent = $('p.tryFormActivate')
128
+ $form.hide()
129
+ $parent.show()
130
+ return false
131
+ });
132
+
133
+ // http://stackoverflow.com/questions/8100770/auto-scaling-inputtype-text-to-width-of-value
134
+ // http://jsfiddle.net/MqM76/217/
135
+ $.fn.textWidth = function(text, font) {
136
+ if (!$.fn.textWidth.fakeEl) $.fn.textWidth.fakeEl = $('<span>').hide().appendTo(document.body);
137
+ $.fn.textWidth.fakeEl.text(text || this.val() || this.text()).css('font', font || this.css('font'));
138
+ return $.fn.textWidth.fakeEl.width();
139
+ };
140
+
141
+ // Automatically ensure that the size for the header inputs is
142
+ // correct
143
+ function resizeInput() {
144
+ $this = $(this)
145
+ $this.css('width', $this.textWidth() + "px")
146
+ $this.attr('size', $this.val().length)
147
+ }
148
+
149
+ $('form.tryForm .tryForm__header input').on('input', resizeInput).trigger('input')
150
+
151
+ });
@@ -0,0 +1,51 @@
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
+ <% end %>
10
+
11
+ <p class='text'>
12
+ <%= authenticator.description %>
13
+ </p>
14
+
15
+ <h2>Authentication Headers</h2>
16
+ <p class='text'>
17
+ The following headers are used to identify yourself to the API client. These should be
18
+ sent as standard HTTP headers with any API request.
19
+ </p>
20
+ <table class='table paramTable'>
21
+ <thead>
22
+ <tr>
23
+ <th width="60%">Header</th>
24
+ <th width="40%">Example</th>
25
+ </tr>
26
+ </thead>
27
+ <% for name, options in authenticator.headers %>
28
+ <tr>
29
+ <td>
30
+ <p>
31
+ <span class='paramTable__name'><%= name %></span>
32
+ </p>
33
+ <% if options[:description] %>
34
+ <p class='paramTable__description'><%= options[:description] %></p>
35
+ <% end %>
36
+ </td>
37
+ <td><%= options[:eg] || options[:example] %> </td>
38
+ </tr>
39
+ <% end %>
40
+ </table>
41
+
42
+ <h2>Errors</h2>
43
+ <p class='text'>
44
+ The errors listed below may be raised if any issues occur when verifying your
45
+ identity with the API.
46
+ </p>
47
+ <% if authenticator.errors.empty? %>
48
+ <p><em>There are no errors which can be raised.</em></p>
49
+ <% else %>
50
+ <%= partial "errors_table", :errors => authenticator.errors %>
51
+ <% 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
+ <% if base.authenticators[:default] %>
21
+ <li>
22
+ <a href='<%= path('authenticators/default') %>' class="<%= active_nav == 'authenticator-default' ? 'active' : '' %>">
23
+ Authentication
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 %>
@@ -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