moonrope 1.3.3 → 2.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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,82 @@
|
|
1
|
+
module Moonrope
|
2
|
+
module EvalHelpers
|
3
|
+
module FilterHelper
|
4
|
+
|
5
|
+
#
|
6
|
+
# Return information which has been passed through the filterable filters
|
7
|
+
#
|
8
|
+
def filter(collection, &block)
|
9
|
+
filters_flags = {}
|
10
|
+
if params.filters.is_a?(Hash)
|
11
|
+
params.filters.each do |key, value|
|
12
|
+
options = {}
|
13
|
+
if filter = action.filters[key.to_sym]
|
14
|
+
if value.is_a?(Hash)
|
15
|
+
options[:operator] = value['operator'] ? value['operator'].to_sym : filter[:operators].first
|
16
|
+
options[:value] = value['value']
|
17
|
+
else
|
18
|
+
# If no hash is provided, we'll always attempt to use the first operator
|
19
|
+
# for the filter.
|
20
|
+
options[:operator] = filter[:operators].first
|
21
|
+
options[:value] = value
|
22
|
+
end
|
23
|
+
|
24
|
+
# Check that the operator is supported
|
25
|
+
unless filter[:operators].include?(options[:operator])
|
26
|
+
error 'FilterError', :issue_code => 'InvalidOperator', :issue_message => "The operator '#{options[:operator]}' is not supported for the '#{key}' attribute."
|
27
|
+
end
|
28
|
+
|
29
|
+
# Add this item to the flags which will be set at the end of the method
|
30
|
+
filters_flags[key] = options
|
31
|
+
|
32
|
+
# Do the filtering...
|
33
|
+
if filter[:block]
|
34
|
+
# If a block is provided, we'll refer to that to do the lookups
|
35
|
+
# and ensure that return the collection
|
36
|
+
collection = instance_exec(options[:operator], options[:value], collection, &filter[:block])
|
37
|
+
else
|
38
|
+
# If no block is provided, we'll fall back to Active Record like where
|
39
|
+
# lookups on the original collection.
|
40
|
+
case options[:operator]
|
41
|
+
when :eq
|
42
|
+
collection = collection.where("#{key} = ?", options[:value].to_s)
|
43
|
+
when :not_eq
|
44
|
+
collection = collection.where("#{key} != ?", options[:value].to_s)
|
45
|
+
when :starts_with
|
46
|
+
collection = collection.where("#{key} LIKE ?", "#{options[:value].to_s}%")
|
47
|
+
when :ends_with
|
48
|
+
collection = collection.where("#{key} LIKE ?", "%#{options[:value].to_s}")
|
49
|
+
when :gt
|
50
|
+
collection = collection.where("#{key} > ?", options[:value].to_s)
|
51
|
+
when :gte
|
52
|
+
collection = collection.where("#{key} >= ?", options[:value].to_s)
|
53
|
+
when :lt
|
54
|
+
collection = collection.where("#{key} < ?", options[:value].to_s)
|
55
|
+
when :lte
|
56
|
+
collection = collection.where("#{key} <= ?", options[:value].to_s)
|
57
|
+
when :in, :not_in
|
58
|
+
# For checking with arrays, we must make sure the user has sent us
|
59
|
+
# an array otherwise we'll raise an error.
|
60
|
+
unless options[:value].is_a?(Array)
|
61
|
+
error 'FilterError', :issue_code => "ArrayNeeded", :issue_message => "An array value is needed for '#{key}' for in/not_in operator"
|
62
|
+
end
|
63
|
+
values = options[:value].map(&:to_s)
|
64
|
+
collection = options[:operator] == :in ? collection.where(key => values) : collection.where.not(key => values)
|
65
|
+
else
|
66
|
+
error 'FilterError', :issue_code => "UnsupportedOperator", :issue_message => "The operator '#{options[:operator]}' is not supported."
|
67
|
+
end
|
68
|
+
end
|
69
|
+
else
|
70
|
+
# Raise an error if the attribute has been provided by the consumer
|
71
|
+
# that isn't supported by the action.
|
72
|
+
error 'FilterError', :issue_code => "UnsupportedAttribute", :issue_message => "The '#{key}' attribute is not supported for filtering on this action."
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
set_flag :filters, filters_flags
|
77
|
+
instance_exec(collection, &block)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ::Guard
|
2
|
+
class Moonrope < Plugin
|
3
|
+
def initialize(options)
|
4
|
+
super
|
5
|
+
@options = options
|
6
|
+
@options[:source] ||= "api"
|
7
|
+
@options[:destination] ||= ".apidoc"
|
8
|
+
end
|
9
|
+
|
10
|
+
def start
|
11
|
+
UI.info "Starting Moonrope Watching"
|
12
|
+
end
|
13
|
+
|
14
|
+
def reload
|
15
|
+
stop ; start
|
16
|
+
end
|
17
|
+
|
18
|
+
def run_all
|
19
|
+
generate_moonrope_docs
|
20
|
+
end
|
21
|
+
|
22
|
+
def run_on_modifications(paths)
|
23
|
+
generate_moonrope_docs
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def generate_moonrope_docs
|
29
|
+
if File.exist?(File.join(@options[:destination], 'moonrope.txt'))
|
30
|
+
system("rm -Rf #{@options[:destination]}/*")
|
31
|
+
end
|
32
|
+
system("bundle exec moonrope #{@options[:source]} #{@options[:destination]}")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'moonrope/doc_context'
|
4
|
+
|
5
|
+
module Moonrope
|
6
|
+
class HtmlGenerator
|
7
|
+
|
8
|
+
def initialize(base, template_root_path)
|
9
|
+
@base = base
|
10
|
+
@template_root_path = template_root_path
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :base
|
14
|
+
attr_reader :template_root_path
|
15
|
+
|
16
|
+
def host
|
17
|
+
ENV['MR_HOST']
|
18
|
+
end
|
19
|
+
|
20
|
+
def prefix
|
21
|
+
ENV['MR_PREFIX'] || 'api'
|
22
|
+
end
|
23
|
+
|
24
|
+
def version
|
25
|
+
ENV['MR_VERSION'] || 'v1'
|
26
|
+
end
|
27
|
+
|
28
|
+
def generate(output_path)
|
29
|
+
FileUtils.mkdir_p(output_path)
|
30
|
+
FileUtils.cp_r(File.join(@template_root_path, 'assets'), File.join(output_path, 'assets'))
|
31
|
+
FileUtils.touch(File.join(output_path, 'moonrope.txt'))
|
32
|
+
# Index
|
33
|
+
generate_file(output_path, "index.html", "index")
|
34
|
+
# Controllers
|
35
|
+
@base.controllers.select { |c| c.doc != false }.each do |controller|
|
36
|
+
generate_file(output_path, File.join("controllers", "#{controller.name}.html"), "controller", {:controller => controller})
|
37
|
+
controller.actions.select { |_,a| a.doc != false }.each do |_, action|
|
38
|
+
generate_file(output_path, File.join("controllers", controller.name.to_s, "#{action.name}.html"), "action", {:controller => controller, :action => action})
|
39
|
+
end
|
40
|
+
end
|
41
|
+
# Structures
|
42
|
+
@base.structures.select { |s| s.doc != false }.each do |structure|
|
43
|
+
generate_file(output_path, File.join("structures", "#{structure.name}.html"), "structure", {:structure => structure})
|
44
|
+
end
|
45
|
+
# Authenticators
|
46
|
+
@base.authenticators.values.select { |s| s.doc != false }.each do |authenticator|
|
47
|
+
generate_file(output_path, File.join("authenticators", "#{authenticator.name}.html"), "authenticator", {:authenticator => authenticator})
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def generate_file(root_dir, output_file, template_file, variables = {})
|
55
|
+
file = DocContext.new(self, :html_extensions => true, :welcome_file => 'index', :output_file => output_file, :vars => variables)
|
56
|
+
file_string = file.render(File.join(@template_root_path, "#{template_file}.erb"))
|
57
|
+
layout = DocContext.new(self, :html_extensions => true, :welcome_file => 'index', :output_file => output_file, :vars => {:page_title => file.vars[:page_title], :active_nav =>file.vars[:active_nav], :body => file_string}).render(File.join(@template_root_path, "layout.erb"))
|
58
|
+
path = File.join(root_dir, output_file)
|
59
|
+
FileUtils.mkdir_p(File.dirname(path))
|
60
|
+
File.open(path, 'w') { |f| f.write(layout) }
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
data/lib/moonrope/param_set.rb
CHANGED
@@ -29,7 +29,7 @@ module Moonrope
|
|
29
29
|
#
|
30
30
|
def _value_for(key)
|
31
31
|
# Get the value from the params and defaults
|
32
|
-
value = (@params[key.to_s]
|
32
|
+
value = @params.has_key?(key.to_s) ? @params[key.to_s] : @defaults[key.to_s]
|
33
33
|
# Ensure that empty strings are actually nil.
|
34
34
|
value = nil if value.is_a?(String) && value.length == 0
|
35
35
|
# Return the value
|
@@ -39,6 +39,16 @@ module Moonrope
|
|
39
39
|
alias_method :[], :_value_for
|
40
40
|
alias_method :method_missing, :_value_for
|
41
41
|
|
42
|
+
#
|
43
|
+
# Set the value for a given param
|
44
|
+
#
|
45
|
+
# @param key [String]
|
46
|
+
# @param value [AnyObject]
|
47
|
+
#
|
48
|
+
def _set_value(name, value)
|
49
|
+
@params[name.to_s] = value
|
50
|
+
end
|
51
|
+
|
42
52
|
#
|
43
53
|
# Set the defaults for the param set
|
44
54
|
#
|
@@ -25,23 +25,6 @@ module Moonrope
|
|
25
25
|
#
|
26
26
|
def call(env)
|
27
27
|
if env['PATH_INFO'] =~ Moonrope::Request.path_regex
|
28
|
-
|
29
|
-
if @options[:reload_on_each_request]
|
30
|
-
@base.load
|
31
|
-
end
|
32
|
-
|
33
|
-
#
|
34
|
-
# Call the on request block if one has been defined for the base.
|
35
|
-
#
|
36
|
-
if @base.on_request.is_a?(Proc)
|
37
|
-
@base.on_request.call(@base, env)
|
38
|
-
end
|
39
|
-
|
40
|
-
#
|
41
|
-
# Create a new request object
|
42
|
-
#
|
43
|
-
request = @base.request(env, $1)
|
44
|
-
|
45
28
|
#
|
46
29
|
# Set some global headers which are always returned
|
47
30
|
#
|
@@ -65,6 +48,38 @@ module Moonrope
|
|
65
48
|
#
|
66
49
|
global_headers['Content-Type'] = 'application/json'
|
67
50
|
|
51
|
+
#
|
52
|
+
# Reload if needed
|
53
|
+
#
|
54
|
+
if @options[:reload_on_each_request]
|
55
|
+
@base = @base.copy
|
56
|
+
begin
|
57
|
+
@base.load
|
58
|
+
rescue => e
|
59
|
+
return generate_error_triplet(@base, nil, e, global_headers)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Create a new request object
|
65
|
+
#
|
66
|
+
request = base.request(env, $1)
|
67
|
+
|
68
|
+
#
|
69
|
+
# If force SSL is enabled, don't allow requests to proceed if they're
|
70
|
+
# not SSL
|
71
|
+
#
|
72
|
+
if base.force_ssl? && !request.ssl?
|
73
|
+
return [400, global_headers, [{:status => 'http-not-supported', :message => "Non-secure HTTP connections are not supported. Requests should be made using https:// rather than http://."}.to_json]]
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Call the on request block if one has been defined for the base.
|
78
|
+
#
|
79
|
+
if base.on_request.is_a?(Proc)
|
80
|
+
base.on_request.call(base, env)
|
81
|
+
end
|
82
|
+
|
68
83
|
#
|
69
84
|
# Check the request is valid
|
70
85
|
#
|
@@ -78,30 +93,22 @@ module Moonrope
|
|
78
93
|
begin
|
79
94
|
result = request.execute
|
80
95
|
json = result.to_json
|
81
|
-
global_headers['Content-Length'] = json.bytesize.to_s
|
82
|
-
[200, global_headers.merge(result.headers), [result.to_json]]
|
83
|
-
rescue JSON::ParserError => e
|
84
|
-
[400, global_headers, [{:status => 'invalid-json', :details => e.message}.to_json]]
|
85
|
-
rescue => e
|
86
|
-
Moonrope.logger.info e.class
|
87
|
-
Moonrope.logger.info e.message
|
88
|
-
Moonrope.logger.info e.backtrace.join("\n")
|
89
|
-
|
90
|
-
response = {:status => 'internal-server-error'}
|
91
96
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
end
|
97
|
+
global_headers['Content-Length'] = json.bytesize.to_s
|
98
|
+
headers = global_headers.merge(result.headers)
|
99
|
+
Moonrope.logger.info "[#{Time.now.utc.strftime("%Y-%m-%d %H:%M:%S")}] controller=#{request.controller.name} action=#{request.action.name} status=#{result.status} time=#{result.time} ip=#{request.ip} size=#{json.bytesize}"
|
96
100
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
response[:backtrace] = e.backtrace[0,6]
|
101
|
+
base.request_callbacks.each do |callback|
|
102
|
+
# Call each request callback and provide the request, the result
|
103
|
+
# and the raw that's being returned to the user.
|
104
|
+
callback.call(request, result, json, headers)
|
102
105
|
end
|
103
106
|
|
104
|
-
[
|
107
|
+
[200, headers, [json]]
|
108
|
+
rescue JSON::ParserError => e
|
109
|
+
[400, global_headers, [{:status => 'invalid-json', :details => e.message}.to_json]]
|
110
|
+
rescue => e
|
111
|
+
generate_error_triplet(base, request, e, global_headers)
|
105
112
|
end
|
106
113
|
|
107
114
|
else
|
@@ -113,5 +120,27 @@ module Moonrope
|
|
113
120
|
end
|
114
121
|
end
|
115
122
|
|
123
|
+
def generate_error_triplet(base, request, exception, headers = {})
|
124
|
+
Moonrope.logger.info exception.class
|
125
|
+
Moonrope.logger.info exception.message
|
126
|
+
Moonrope.logger.info exception.backtrace.join("\n")
|
127
|
+
|
128
|
+
response = {:status => 'internal-server-error'}
|
129
|
+
|
130
|
+
# Call any request errors which have been registered on the base
|
131
|
+
base.request_error_callbacks.each do |callback|
|
132
|
+
callback.call(request, exception)
|
133
|
+
end
|
134
|
+
|
135
|
+
# If in development, return more details about the exception which was raised.
|
136
|
+
if base.environment == 'development'
|
137
|
+
response[:error] = exception.class.to_s
|
138
|
+
response[:message] = exception.message
|
139
|
+
response[:backtrace] = exception.backtrace[0,6]
|
140
|
+
end
|
141
|
+
|
142
|
+
[500, headers, [response.to_json]]
|
143
|
+
end
|
144
|
+
|
116
145
|
end
|
117
146
|
end
|
data/lib/moonrope/railtie.rb
CHANGED
@@ -1,19 +1,21 @@
|
|
1
|
+
require 'moonrope/doc_server'
|
2
|
+
|
1
3
|
module Moonrope
|
2
4
|
class Railtie < Rails::Railtie
|
3
5
|
|
4
6
|
initializer 'moonrope.initialize' do |app|
|
5
7
|
|
6
8
|
# Initialize a new moonrope base.
|
7
|
-
|
9
|
+
Moonrope::Base.instance = Moonrope::Base.load(Rails.root.join('api'))
|
8
10
|
|
9
11
|
# Set the logger
|
10
12
|
Moonrope.logger = Rails.logger
|
11
13
|
|
12
14
|
# Set the environment to match the Rails environment
|
13
|
-
|
15
|
+
Moonrope::Base.instance.environment = Rails.env.to_s
|
14
16
|
|
15
17
|
# Ensure all request use UTC
|
16
|
-
|
18
|
+
Moonrope::Base.instance.on_request = Proc.new do |base, env|
|
17
19
|
Time.zone = 'UTC'
|
18
20
|
end
|
19
21
|
|
@@ -22,29 +24,44 @@ module Moonrope
|
|
22
24
|
Moonrope::Request.path_regex = app.config.moonrope_request_path_regex
|
23
25
|
end
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
-
|
27
|
+
ActiveSupport.on_load(:active_record) do
|
28
|
+
|
29
|
+
# Catch ActiveRecord::RecordNotFound exception as a standard not-found error
|
30
|
+
Moonrope::Base.instance.register_external_error ActiveRecord::RecordNotFound do |exception, result|
|
28
31
|
result.status = 'not-found'
|
29
32
|
result.data = {:message => exception.message}
|
30
33
|
end
|
31
34
|
|
32
|
-
#
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
end
|
35
|
+
# Catch ActiveRecord::DeleteRestrictionError and raise an a DeleteRestrictionError
|
36
|
+
Moonrope::Base.instance.register_external_error ActiveRecord::DeleteRestrictionError do |exception, result|
|
37
|
+
result.status = 'error'
|
38
|
+
result.data = {:code => "DeleteRestrictionError", :message => "Object could not be deleted due to dependency"}
|
39
|
+
if exception.message =~ /([\w\-]+)\z/
|
40
|
+
result.data[:dependency] = $1
|
39
41
|
end
|
40
42
|
end
|
43
|
+
|
44
|
+
# Catch ActiveRecord::RecordInvalid and raise an a ValidationError
|
45
|
+
Moonrope::Base.instance.register_external_error ActiveRecord::RecordInvalid do |exception, result|
|
46
|
+
result.status = 'error'
|
47
|
+
errors = exception.record.errors.respond_to?(:to_api_hash) ? exception.record.errors.to_api_hash : exception.record.errors
|
48
|
+
result.data = {:code => "ValidationError", :message => "Object could not be saved due to a validation error", :errors => errors}
|
49
|
+
end
|
50
|
+
|
41
51
|
end
|
42
52
|
|
53
|
+
# Insert the documentation middleware
|
54
|
+
app.middleware.use(
|
55
|
+
Moonrope::DocServer,
|
56
|
+
Moonrope::Base.instance,
|
57
|
+
:reload_on_each_request => !app.config.cache_classes
|
58
|
+
)
|
59
|
+
|
43
60
|
# Insert the Moonrope middleware into the application's middleware
|
44
61
|
# stack (at the bottom).
|
45
62
|
app.middleware.use(
|
46
63
|
Moonrope::RackMiddleware,
|
47
|
-
|
64
|
+
Moonrope::Base.instance,
|
48
65
|
:reload_on_each_request => !app.config.cache_classes
|
49
66
|
)
|
50
67
|
|
data/lib/moonrope/request.rb
CHANGED
@@ -3,10 +3,16 @@ module Moonrope
|
|
3
3
|
|
4
4
|
class << self
|
5
5
|
attr_accessor :path_regex
|
6
|
+
attr_accessor :path_prefix
|
6
7
|
|
7
8
|
# @return [Regex] the regex which should be matched for all API requests
|
8
9
|
def path_regex
|
9
|
-
@path_regex ||= /\A
|
10
|
+
@path_regex ||= /\A\/#{path_prefix}([\w\/\-\.]+)?/
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [Regex] the initial path of the prefix to be matched
|
14
|
+
def path_prefix
|
15
|
+
@path_prefix ||= /api\//
|
10
16
|
end
|
11
17
|
end
|
12
18
|
|
@@ -17,7 +23,7 @@ module Moonrope
|
|
17
23
|
# @return [String] the name of the action which was requested
|
18
24
|
attr_reader :action_name
|
19
25
|
# @return [Object] the authenticated user
|
20
|
-
attr_reader :
|
26
|
+
attr_reader :identity
|
21
27
|
|
22
28
|
#
|
23
29
|
# Initialize a new Moonrope::Request
|
@@ -80,25 +86,30 @@ module Moonrope
|
|
80
86
|
# @return [Moonrope::ActionResult]
|
81
87
|
#
|
82
88
|
def execute
|
83
|
-
eval_env = EvalEnvironment.new(@base, self)
|
84
|
-
|
89
|
+
eval_env = EvalEnvironment.new(@base, self, action)
|
90
|
+
|
91
|
+
if action.authenticator_to_use.is_a?(Moonrope::Authenticator)
|
85
92
|
result = action.convert_errors_to_action_result do
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
93
|
+
if block = action.authenticator_to_use.lookup
|
94
|
+
@identity = eval_env.instance_eval(&block)
|
95
|
+
end
|
96
|
+
|
97
|
+
unless action.check_access(eval_env) == true
|
98
|
+
if rule = action.authenticator_to_use.rules[action.access_rule_to_use]
|
99
|
+
eval_env.structured_error(rule[:error_code], rule[:description], :action => action.name, :controller => controller.name)
|
100
|
+
else
|
101
|
+
eval_env.structured_error("NotPermitted", "You are not permitted to access #{controller.name}/#{action.name}", :action => action.name, :controller => controller.name)
|
92
102
|
end
|
93
103
|
end
|
94
104
|
end
|
95
105
|
|
96
106
|
if result.is_a?(Moonrope::ActionResult)
|
97
|
-
# If we already have a result, we should return it and no longer execute
|
98
|
-
# this request.
|
99
107
|
return result
|
100
108
|
end
|
109
|
+
elsif action.authenticator_to_use == :not_found
|
110
|
+
raise Moonrope::Errors::MissingAuthenticator, "Wanted to use authenticator that was not defined."
|
101
111
|
end
|
112
|
+
|
102
113
|
action.execute(eval_env)
|
103
114
|
end
|
104
115
|
|
@@ -109,7 +120,7 @@ module Moonrope
|
|
109
120
|
#
|
110
121
|
def params
|
111
122
|
@params ||= begin
|
112
|
-
if @env['CONTENT_TYPE']
|
123
|
+
if @env['CONTENT_TYPE'] && @env['CONTENT_TYPE'] =~ /\Aapplication\/json(;|\z)/i
|
113
124
|
Moonrope::ParamSet.new(rack_request.body.read)
|
114
125
|
else
|
115
126
|
Moonrope::ParamSet.new(rack_request.params['params'])
|
@@ -132,7 +143,7 @@ module Moonrope
|
|
132
143
|
# @return [Boolean]
|
133
144
|
#
|
134
145
|
def anonymous?
|
135
|
-
|
146
|
+
identity.nil?
|
136
147
|
end
|
137
148
|
|
138
149
|
#
|
@@ -141,7 +152,24 @@ module Moonrope
|
|
141
152
|
# @return [Boolean]
|
142
153
|
#
|
143
154
|
def authenticated?
|
144
|
-
!(
|
155
|
+
!(identity.nil? || identity == false)
|
156
|
+
end
|
157
|
+
|
158
|
+
#
|
159
|
+
# Return the remote IP
|
160
|
+
#
|
161
|
+
# @return [String]
|
162
|
+
#
|
163
|
+
def ip
|
164
|
+
rack_request.ip
|
165
|
+
end
|
166
|
+
|
167
|
+
#
|
168
|
+
# Is this request on SSL?
|
169
|
+
#
|
170
|
+
# @return [Boolean]
|
171
|
+
def ssl?
|
172
|
+
rack_request.ssl?
|
145
173
|
end
|
146
174
|
|
147
175
|
private
|