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.
Files changed (78) hide show
  1. checksums.yaml +5 -5
  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/lib/moonrope.rb +5 -4
  18. data/lib/moonrope/action.rb +170 -40
  19. data/lib/moonrope/authenticator.rb +42 -0
  20. data/lib/moonrope/base.rb +67 -6
  21. data/lib/moonrope/controller.rb +4 -2
  22. data/lib/moonrope/doc_context.rb +94 -0
  23. data/lib/moonrope/doc_server.rb +123 -0
  24. data/lib/moonrope/dsl/action_dsl.rb +159 -9
  25. data/lib/moonrope/dsl/authenticator_dsl.rb +35 -0
  26. data/lib/moonrope/dsl/base_dsl.rb +21 -18
  27. data/lib/moonrope/dsl/controller_dsl.rb +60 -9
  28. data/lib/moonrope/dsl/filterable_dsl.rb +27 -0
  29. data/lib/moonrope/dsl/structure_dsl.rb +28 -2
  30. data/lib/moonrope/errors.rb +13 -0
  31. data/lib/moonrope/eval_environment.rb +82 -3
  32. data/lib/moonrope/eval_helpers.rb +47 -8
  33. data/lib/moonrope/eval_helpers/filter_helper.rb +82 -0
  34. data/lib/moonrope/guard.rb +35 -0
  35. data/lib/moonrope/html_generator.rb +65 -0
  36. data/lib/moonrope/param_set.rb +11 -1
  37. data/lib/moonrope/rack_middleware.rb +66 -37
  38. data/lib/moonrope/railtie.rb +31 -14
  39. data/lib/moonrope/request.rb +43 -15
  40. data/lib/moonrope/structure.rb +100 -18
  41. data/lib/moonrope/structure_attribute.rb +39 -0
  42. data/lib/moonrope/version.rb +1 -1
  43. data/moonrope.gemspec +21 -0
  44. data/spec/spec_helper.rb +32 -0
  45. data/spec/specs/action_spec.rb +455 -0
  46. data/spec/specs/base_spec.rb +29 -0
  47. data/spec/specs/controller_spec.rb +31 -0
  48. data/spec/specs/param_set_spec.rb +31 -0
  49. data/templates/basic/_action_form.erb +77 -0
  50. data/templates/basic/_errors_table.erb +32 -0
  51. data/templates/basic/_structure_attributes_list.erb +55 -0
  52. data/templates/basic/action.erb +168 -0
  53. data/templates/basic/assets/lock.svg +3 -0
  54. data/templates/basic/assets/reset.css +101 -0
  55. data/templates/basic/assets/style.css +348 -0
  56. data/templates/basic/assets/tool.svg +4 -0
  57. data/templates/basic/assets/try.js +157 -0
  58. data/templates/basic/authenticator.erb +52 -0
  59. data/templates/basic/controller.erb +20 -0
  60. data/templates/basic/index.erb +114 -0
  61. data/templates/basic/layout.erb +46 -0
  62. data/templates/basic/structure.erb +23 -0
  63. data/test/test_helper.rb +81 -0
  64. data/test/tests/action_access_test.rb +63 -0
  65. data/test/tests/actions_test.rb +524 -0
  66. data/test/tests/authenticators_test.rb +87 -0
  67. data/test/tests/base_test.rb +35 -0
  68. data/test/tests/controllers_test.rb +49 -0
  69. data/test/tests/eval_environment_test.rb +136 -0
  70. data/test/tests/evel_helpers_test.rb +60 -0
  71. data/test/tests/examples_test.rb +11 -0
  72. data/test/tests/helpers_test.rb +97 -0
  73. data/test/tests/param_set_test.rb +44 -0
  74. data/test/tests/rack_middleware_test.rb +131 -0
  75. data/test/tests/request_test.rb +232 -0
  76. data/test/tests/structures_param_extensions_test.rb +159 -0
  77. data/test/tests/structures_test.rb +398 -0
  78. 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
@@ -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] || @defaults[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
- # Call any request errors which have been registered on the base
93
- @base.request_error_callbacks.each do |callback|
94
- callback.call(request, e)
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
- # If in development, return more details about the exception which was raised.
98
- if @base.environment == 'development'
99
- response[:error] = e.class.to_s
100
- response[:message] = e.message
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
- [500, global_headers, [response.to_json]]
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
@@ -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
- app.config.moonrope = Moonrope::Base.load(Rails.root.join('api'))
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
- app.config.moonrope.environment = Rails.env.to_s
15
+ Moonrope::Base.instance.environment = Rails.env.to_s
14
16
 
15
17
  # Ensure all request use UTC
16
- app.config.moonrope.on_request = Proc.new do |base, env|
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
- # Catch ActiveRecord::RecordNotFound exception as a standard not-found error
26
- if defined?(ActiveRecord)
27
- app.config.moonrope.register_external_error ActiveRecord::RecordNotFound do |exception, result|
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
- # Add a helper for auto setting parameters
33
- app.config.moonrope.dsl.instance_eval do
34
- helper :auto_set_params_for, :unloadable => false do |object|
35
- current_action = object.new_record? ? :create_only : :update_only
36
- request.action.params.select { |k,v| v[:set] == true || v[:set] == current_action }.keys.each do |param|
37
- object.send("#{param}=", params[param]) if params.has?(param)
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
- app.config.moonrope,
64
+ Moonrope::Base.instance,
48
65
  :reload_on_each_request => !app.config.cache_classes
49
66
  )
50
67
 
@@ -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\/api\/([\w\/\-\.]+)?/
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 :authenticated_user
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
- if @base.authenticator
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
- @authenticated_user = eval_env.instance_eval(&@base.authenticator)
87
- # If we are authenticated, check whether the action permits access to
88
- # this user, if not raise an error.
89
- if authenticated?
90
- unless action.check_access(eval_env) == true
91
- raise Moonrope::Errors::AccessDenied, "Access to #{controller.name}/#{action.name} is not permitted."
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'] == 'application/json'
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
- authenticated_user.nil?
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
- !(authenticated_user.nil? || authenticated_user == false)
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