moonrope 1.3.3 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
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