moonrope 1.4.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- 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/html/assets/lock.svg +3 -0
- data/html/assets/reset.css +101 -0
- data/html/assets/style.css +348 -0
- data/html/assets/tool.svg +4 -0
- data/html/assets/try.js +151 -0
- data/html/authenticators/default.html +191 -0
- data/html/controllers/meta/version.html +144 -0
- data/html/controllers/meta.html +73 -0
- data/html/controllers/users/create.html +341 -0
- data/html/controllers/users/list.html +348 -0
- data/html/controllers/users/show.html +261 -0
- data/html/controllers/users/update.html +387 -0
- data/html/controllers/users.html +93 -0
- data/html/index.html +166 -0
- data/html/moonrope.txt +0 -0
- data/html/structures/pet.html +176 -0
- data/html/structures/user.html +338 -0
- data/lib/moonrope/action.rb +165 -37
- data/lib/moonrope/authenticator.rb +39 -0
- data/lib/moonrope/base.rb +24 -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 +31 -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 +27 -2
- data/lib/moonrope/errors.rb +3 -0
- data/lib/moonrope/eval_environment.rb +82 -3
- data/lib/moonrope/eval_helpers/filter_helper.rb +82 -0
- data/lib/moonrope/eval_helpers.rb +28 -5
- 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 +1 -1
- data/lib/moonrope/railtie.rb +31 -14
- data/lib/moonrope/request.rb +25 -14
- data/lib/moonrope/structure.rb +74 -11
- data/lib/moonrope/structure_attribute.rb +15 -0
- data/lib/moonrope/version.rb +1 -1
- data/lib/moonrope.rb +5 -4
- 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 +151 -0
- data/templates/basic/authenticator.erb +51 -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 +109 -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 +335 -0
- metadata +82 -48
data/lib/moonrope/controller.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
require 'moonrope/dsl/controller_dsl'
|
2
|
+
|
1
3
|
module Moonrope
|
2
4
|
class Controller
|
3
5
|
|
4
|
-
attr_accessor :name, :actions, :
|
6
|
+
attr_accessor :name, :actions, :befores, :friendly_name, :description, :doc, :authenticator, :access_rule, :shared_actions
|
5
7
|
attr_reader :base, :dsl
|
6
8
|
|
7
9
|
#
|
@@ -15,7 +17,7 @@ module Moonrope
|
|
15
17
|
@base = base
|
16
18
|
@name = name
|
17
19
|
@actions = {}
|
18
|
-
@
|
20
|
+
@shared_actions = {}
|
19
21
|
@befores = []
|
20
22
|
@dsl = Moonrope::DSL::ControllerDSL.new(self)
|
21
23
|
@dsl.instance_eval(&block) if block_given?
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Moonrope
|
2
|
+
class DocContext
|
3
|
+
|
4
|
+
attr_reader :vars
|
5
|
+
|
6
|
+
def initialize(generator, options = {})
|
7
|
+
@generator = generator
|
8
|
+
@vars = options.delete(:vars) || {}
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def set_page_title(title)
|
13
|
+
@vars[:page_title] = title
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_active_nav(nav)
|
17
|
+
@vars[:active_nav] = nav
|
18
|
+
end
|
19
|
+
|
20
|
+
def base
|
21
|
+
@generator.base
|
22
|
+
end
|
23
|
+
|
24
|
+
def host
|
25
|
+
@generator.host
|
26
|
+
end
|
27
|
+
|
28
|
+
def prefix
|
29
|
+
@generator.prefix
|
30
|
+
end
|
31
|
+
|
32
|
+
def version
|
33
|
+
@generator.version
|
34
|
+
end
|
35
|
+
|
36
|
+
def method_missing(name)
|
37
|
+
if @vars.has_key?(name.to_sym)
|
38
|
+
@vars[name.to_sym]
|
39
|
+
else
|
40
|
+
super
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def git_version
|
45
|
+
ENV["VDT_VERSION"] ||
|
46
|
+
(`git rev-parse HEAD`.strip rescue nil)
|
47
|
+
end
|
48
|
+
|
49
|
+
def asset_path(file)
|
50
|
+
path("assets/" + file)
|
51
|
+
end
|
52
|
+
|
53
|
+
def full_prefix
|
54
|
+
"#{host}/#{prefix}/#{version}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def path(file)
|
58
|
+
depth = ((@options[:output_file] || '').split('/').size - 1).times.map { "../" }.join
|
59
|
+
if file == :root
|
60
|
+
file = depth + (@options[:welcome_file] || "welcome")
|
61
|
+
else
|
62
|
+
file = depth + file
|
63
|
+
end
|
64
|
+
|
65
|
+
if @options[:html_extensions] && !(file =~ /\.[a-z]+\z/)
|
66
|
+
file = "#{file}.html"
|
67
|
+
end
|
68
|
+
|
69
|
+
file
|
70
|
+
end
|
71
|
+
|
72
|
+
def render(template_file)
|
73
|
+
ERB.new(File.read(template_file), nil, '-').result(binding)
|
74
|
+
end
|
75
|
+
|
76
|
+
def partial(name, attributes = {})
|
77
|
+
erb = self.class.new(@generator, @options.merge(:vars => attributes))
|
78
|
+
erb.render(File.join(@generator.template_root_path, "_#{name}.erb"))
|
79
|
+
end
|
80
|
+
|
81
|
+
def friendly_type(type)
|
82
|
+
if type.is_a?(Symbol)
|
83
|
+
type.to_s.capitalize
|
84
|
+
else
|
85
|
+
type.to_s
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def humanize(string)
|
90
|
+
string.to_s.gsub(/\_/, ' ')
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'moonrope/doc_context'
|
2
|
+
|
3
|
+
module Moonrope
|
4
|
+
class DocServer
|
5
|
+
|
6
|
+
CONTENT_TYPES = {
|
7
|
+
'css' => 'text/css',
|
8
|
+
'js' => 'text/javascript',
|
9
|
+
'svg' => 'image/svg+xml'
|
10
|
+
}
|
11
|
+
|
12
|
+
class << self
|
13
|
+
#
|
14
|
+
# Set the default path regex which should be matched for requests for
|
15
|
+
# API docmentation. By default, this is /api/docs/.
|
16
|
+
#
|
17
|
+
def path_regex
|
18
|
+
@path_regex ||= /\A\/#{Moonrope::Request.path_prefix}docs\/([\w\.]+)\/?([\w\/\-\.]+)?/
|
19
|
+
end
|
20
|
+
attr_writer :path_regex
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(app, base, options = {})
|
24
|
+
@app = app
|
25
|
+
@base = base
|
26
|
+
@options = options
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader :base
|
30
|
+
|
31
|
+
class Generator
|
32
|
+
def initialize(base, options = {})
|
33
|
+
@base = base
|
34
|
+
@options = options
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_reader :base
|
38
|
+
|
39
|
+
def template_root_path
|
40
|
+
File.expand_path("../../../templates/basic", __FILE__)
|
41
|
+
end
|
42
|
+
|
43
|
+
def host
|
44
|
+
@options[:host]
|
45
|
+
end
|
46
|
+
|
47
|
+
def prefix
|
48
|
+
@options[:prefix]
|
49
|
+
end
|
50
|
+
|
51
|
+
def version
|
52
|
+
@options[:version]
|
53
|
+
end
|
54
|
+
|
55
|
+
def generate_file(output_file, template_file, variables = {})
|
56
|
+
# Generate the page for the requested template with the given variables
|
57
|
+
file = DocContext.new(self, :output_file => output_file, :vars => variables)
|
58
|
+
file_string = file.render(File.join(template_root_path, "#{template_file}.erb"))
|
59
|
+
# Generate the final page within the layout
|
60
|
+
DocContext.new(self, :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"))
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def call(env)
|
65
|
+
if env['PATH_INFO'] =~ self.class.path_regex
|
66
|
+
version = $1
|
67
|
+
doc_path = $2
|
68
|
+
request = Rack::Request.new(env)
|
69
|
+
generator = Generator.new(@base, :host => "#{request.scheme}://#{request.host_with_port}", :version => version, :prefix => env['PATH_INFO'].split('/')[1])
|
70
|
+
|
71
|
+
if @options[:reload_on_each_request]
|
72
|
+
@base.load
|
73
|
+
end
|
74
|
+
|
75
|
+
file = nil
|
76
|
+
content_type = nil
|
77
|
+
|
78
|
+
case doc_path
|
79
|
+
when nil, ""
|
80
|
+
return [302, {'Location' => "#{env['PATH_INFO']}/welcome"}, ['']]
|
81
|
+
when /\Awelcome\z/, /\Aindex\.html\z/
|
82
|
+
file = generator.generate_file(doc_path, 'index')
|
83
|
+
when /\Acontrollers\/(\w+)(\.html)?\z/
|
84
|
+
if controller = @base.controller($1.to_sym)
|
85
|
+
file = generator.generate_file(doc_path, 'controller', :controller => controller)
|
86
|
+
end
|
87
|
+
when /\Acontrollers\/(\w+)\/(\w+)(\.html)?\z/
|
88
|
+
if controller = @base.controller($1.to_sym)
|
89
|
+
if action = controller.action($2.to_sym)
|
90
|
+
file = generator.generate_file(doc_path, 'action', :controller => controller, :action => action)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
when /\Astructures\/(\w+)(\.html)?\z/
|
94
|
+
if structure = @base.structure($1.to_sym)
|
95
|
+
file = generator.generate_file(doc_path, 'structure', :structure => structure)
|
96
|
+
end
|
97
|
+
when /\Aauthenticators\/(\w+)(\.html)?\z/
|
98
|
+
if authenticator = @base.authenticators[$1.to_sym]
|
99
|
+
file = generator.generate_file(doc_path, 'authenticator', :authenticator => authenticator)
|
100
|
+
end
|
101
|
+
when /\Aassets\/([\w]+)\.([a-z]+)\z/
|
102
|
+
path = File.join(generator.template_root_path, 'assets', "#{$1}.#{$2}")
|
103
|
+
if File.exist?(path)
|
104
|
+
file = File.read(path)
|
105
|
+
content_type = CONTENT_TYPES[$2] || 'text/plain'
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
if file
|
110
|
+
[200, {
|
111
|
+
'Content-Type' => content_type || 'text/html',
|
112
|
+
'Content-Length' => file.bytesize.to_s},
|
113
|
+
[file]]
|
114
|
+
else
|
115
|
+
[404, {}, ['Not found']]
|
116
|
+
end
|
117
|
+
else
|
118
|
+
return @app.call(env)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'moonrope/errors'
|
2
|
+
require 'moonrope/dsl/filterable_dsl'
|
3
|
+
|
1
4
|
module Moonrope
|
2
5
|
module DSL
|
3
6
|
class ActionDSL
|
@@ -11,6 +14,18 @@ module Moonrope
|
|
11
14
|
@action = action
|
12
15
|
end
|
13
16
|
|
17
|
+
#
|
18
|
+
# Set the title for the action
|
19
|
+
#
|
20
|
+
# title "List all users"
|
21
|
+
#
|
22
|
+
# @param value [String]
|
23
|
+
# @return [void]
|
24
|
+
#
|
25
|
+
def title(value)
|
26
|
+
@action.title = value
|
27
|
+
end
|
28
|
+
|
14
29
|
#
|
15
30
|
# Set the description for the action
|
16
31
|
#
|
@@ -23,6 +38,14 @@ module Moonrope
|
|
23
38
|
@action.description = value
|
24
39
|
end
|
25
40
|
|
41
|
+
|
42
|
+
#
|
43
|
+
# Set this action so that it isn't documented
|
44
|
+
#
|
45
|
+
def no_doc!
|
46
|
+
@action.doc = false
|
47
|
+
end
|
48
|
+
|
26
49
|
#
|
27
50
|
# Add a new param to the action's param set.
|
28
51
|
#
|
@@ -33,27 +56,94 @@ module Moonrope
|
|
33
56
|
# @param options_if_description [Hash] a hash of additional options if a description was provided
|
34
57
|
# @return [void]
|
35
58
|
#
|
36
|
-
def param(name, description_or_options = {}, options_if_description = {})
|
59
|
+
def param(name, description_or_options = {}, options_if_description = {}, &block)
|
37
60
|
if description_or_options.is_a?(String)
|
38
61
|
options = options_if_description.merge(:description => description_or_options)
|
39
62
|
else
|
40
63
|
options = description_or_options
|
41
64
|
end
|
65
|
+
|
66
|
+
options[:from_structure] ||= @from_structure if @from_structure
|
67
|
+
|
68
|
+
if structure = options[:from_structure]
|
69
|
+
if @action.controller && structure = @action.controller.base.structure(structure)
|
70
|
+
if attribute = structure.attribute(name)
|
71
|
+
options[:description] ||= attribute.description
|
72
|
+
options[:type] ||= attribute.value_type
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
options[:apply] = block if block_given?
|
78
|
+
options[:from_shared_action] = @within_shared_action.dup if @within_shared_action
|
42
79
|
@action.params[name] = options
|
43
80
|
end
|
44
81
|
|
45
82
|
#
|
46
|
-
#
|
83
|
+
# Specifies that all params within this block should be marked as being from
|
84
|
+
# a given structure
|
47
85
|
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
86
|
+
# from_structure :user do
|
87
|
+
# param :username
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# @param name [Symbol] the name of the structure
|
51
91
|
#
|
52
|
-
|
92
|
+
def from_structure(name, &block)
|
93
|
+
@from_structure = name
|
94
|
+
self.instance_eval(&block)
|
95
|
+
ensure
|
96
|
+
@from_structure = nil
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Add a new error to the actions' errors
|
101
|
+
#
|
102
|
+
# error "NoUnitFound", "The unit with given {{id}} could not be found"
|
103
|
+
#
|
104
|
+
# @param name [String] the name of the error
|
105
|
+
# @param description [String] a description of the error
|
53
106
|
# @return [void]
|
54
107
|
#
|
55
|
-
def
|
56
|
-
@action.
|
108
|
+
def error(name, description, options = {})
|
109
|
+
@action.errors[name] = options.merge(:description => description, :from_share => @within_share)
|
110
|
+
end
|
111
|
+
|
112
|
+
#
|
113
|
+
# Sets the type of return value that is expected from a successful call
|
114
|
+
# to this API action.
|
115
|
+
#
|
116
|
+
# returns :array, :structure => :user
|
117
|
+
#
|
118
|
+
# @param type [Symbol] the type of object that will be returend
|
119
|
+
# @param options [Hash] further options about the returned value
|
120
|
+
# @return [void]
|
121
|
+
#
|
122
|
+
def returns(type, options = {})
|
123
|
+
@action.returns = options.merge(:type => type)
|
124
|
+
end
|
125
|
+
|
126
|
+
#
|
127
|
+
# Sets the name of the authenticator to use for this action
|
128
|
+
#
|
129
|
+
# @param name [Symbol] the name of the authenticator
|
130
|
+
#
|
131
|
+
def authenticator(name)
|
132
|
+
@action.authenticator = name
|
133
|
+
end
|
134
|
+
|
135
|
+
#
|
136
|
+
# Sets the name of the access rule to use for this action
|
137
|
+
#
|
138
|
+
# @param name [Symbol] the name of the authenticator
|
139
|
+
#
|
140
|
+
def access_rule(name)
|
141
|
+
if name.is_a?(Hash)
|
142
|
+
authenticator name.first[0]
|
143
|
+
access_rule name.first[1]
|
144
|
+
else
|
145
|
+
@action.access_rule = name
|
146
|
+
end
|
57
147
|
end
|
58
148
|
|
59
149
|
#
|
@@ -67,7 +157,67 @@ module Moonrope
|
|
67
157
|
# @return [void]
|
68
158
|
#
|
69
159
|
def action(&block)
|
70
|
-
@action.
|
160
|
+
@action.actions << block
|
161
|
+
end
|
162
|
+
|
163
|
+
#
|
164
|
+
# Specify that this action will be returning paginated data. Sets up the
|
165
|
+
# parameters for the action as appropriate.
|
166
|
+
#
|
167
|
+
def paginated(options = {})
|
168
|
+
@action.traits << :paginated
|
169
|
+
param :page, "The page number", :type => Integer, :required => true, :default => options[:page] || 1
|
170
|
+
param :per_page, "The number of items to return per page", :type => Integer, :required => true, :default => options[:per_page] || 30
|
171
|
+
end
|
172
|
+
|
173
|
+
#
|
174
|
+
# Specify that this action will return data sorted by user provided data.
|
175
|
+
#
|
176
|
+
def sortable(*fields)
|
177
|
+
if fields.empty?
|
178
|
+
raise Moonrope::Errors::Error, "You must specify at least one field when calling 'sortable'"
|
179
|
+
else
|
180
|
+
if fields.first.is_a?(Hash)
|
181
|
+
default_order = fields.first.first[1].to_s
|
182
|
+
fields[0] = fields.first.first[0]
|
183
|
+
else
|
184
|
+
default_order = 'asc'
|
185
|
+
end
|
186
|
+
@action.traits << :sortable
|
187
|
+
param :sort_by, "The field to sort by", :type => String, :required => true, :default => fields[0].to_s, :options => fields.map(&:to_s)
|
188
|
+
param :order, "The direction to order units by", :type => String, :required => true, :default => default_order, :options => ["asc", "desc"]
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
#
|
193
|
+
# Specify that this action will return data which can be filtered by specifying
|
194
|
+
# certain parameters on a filter parameter
|
195
|
+
#
|
196
|
+
def filterable(&block)
|
197
|
+
if @action.errors['FilterError'].nil?
|
198
|
+
error 'FilterError', "An error has occurred while processing filters for this action", :attributes => {:issue_code => "A more specific issue code", :issue_message => "A more specific message about the issue"}
|
199
|
+
end
|
200
|
+
|
201
|
+
if @action.params[:filters].nil?
|
202
|
+
param :filters, "A hash of filters to apply to results", :type => Hash, :default => {}
|
203
|
+
end
|
204
|
+
dsl = FilterableDSL.new(@action)
|
205
|
+
dsl.instance_eval(&block)
|
206
|
+
end
|
207
|
+
|
208
|
+
#
|
209
|
+
# Include any block from the controller shares
|
210
|
+
#
|
211
|
+
def use(name, options = {})
|
212
|
+
if block = (@action.controller.shared_actions[name] || @action.controller.base.shared_actions[name])
|
213
|
+
@within_shared_action ||= []
|
214
|
+
@within_shared_action << name
|
215
|
+
self.instance_exec(options, &block)
|
216
|
+
else
|
217
|
+
raise Moonrope::Errors::InvalidSharedAction, "Invalid share name #{name}"
|
218
|
+
end
|
219
|
+
ensure
|
220
|
+
@within_shared_action.delete(name) if @within_shared_action
|
71
221
|
end
|
72
222
|
|
73
223
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Moonrope
|
2
|
+
module DSL
|
3
|
+
class AuthenticatorDSL
|
4
|
+
|
5
|
+
def initialize(authenticator)
|
6
|
+
@authenticator = authenticator
|
7
|
+
end
|
8
|
+
|
9
|
+
def description(value)
|
10
|
+
@authenticator.description = value
|
11
|
+
end
|
12
|
+
|
13
|
+
def header(name, description = nil, options = {})
|
14
|
+
@authenticator.headers[name] = options.merge(:name => name, :description => description)
|
15
|
+
end
|
16
|
+
|
17
|
+
def error(name, description = nil, options = {})
|
18
|
+
@authenticator.errors[name] = options.merge(:name => name, :description => description)
|
19
|
+
end
|
20
|
+
|
21
|
+
def lookup(&block)
|
22
|
+
@authenticator.lookup = block
|
23
|
+
end
|
24
|
+
|
25
|
+
def rule(name, error_code, description = nil, &block)
|
26
|
+
@authenticator.rules[name] = {:name => name, :error_code => error_code, :description => description, :block => block}
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -1,3 +1,7 @@
|
|
1
|
+
require 'moonrope/structure'
|
2
|
+
require 'moonrope/controller'
|
3
|
+
require 'moonrope/authenticator'
|
4
|
+
|
1
5
|
module Moonrope
|
2
6
|
module DSL
|
3
7
|
class BaseDSL
|
@@ -47,24 +51,6 @@ module Moonrope
|
|
47
51
|
controller
|
48
52
|
end
|
49
53
|
|
50
|
-
#
|
51
|
-
# Set the authenticator for the API.
|
52
|
-
#
|
53
|
-
# @yield stores the block as the authenticator
|
54
|
-
#
|
55
|
-
def authenticator(&block)
|
56
|
-
@base.authenticator = block
|
57
|
-
end
|
58
|
-
|
59
|
-
#
|
60
|
-
# Set the default access check block.
|
61
|
-
#
|
62
|
-
# @yield stores the block as the access check
|
63
|
-
#
|
64
|
-
def default_access(value = nil, &block)
|
65
|
-
@base.default_access = block_given? ? block : value
|
66
|
-
end
|
67
|
-
|
68
54
|
#
|
69
55
|
# Define a new helper in the global namespace
|
70
56
|
#
|
@@ -81,6 +67,23 @@ module Moonrope
|
|
81
67
|
helper_instance
|
82
68
|
end
|
83
69
|
|
70
|
+
#
|
71
|
+
# Define a new authenticator
|
72
|
+
#
|
73
|
+
def authenticator(name, &block)
|
74
|
+
authenticator = Moonrope::Authenticator.new(name)
|
75
|
+
dsl = Moonrope::DSL::AuthenticatorDSL.new(authenticator)
|
76
|
+
dsl.instance_eval(&block) if block_given?
|
77
|
+
@base.authenticators[name] = authenticator
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Define a new global shared action
|
82
|
+
#
|
83
|
+
def shared_action(name, &block)
|
84
|
+
@base.shared_actions[name] = block
|
85
|
+
end
|
86
|
+
|
84
87
|
end
|
85
88
|
end
|
86
89
|
end
|
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'moonrope/action'
|
2
|
+
require 'moonrope/before_action'
|
3
|
+
|
1
4
|
module Moonrope
|
2
5
|
module DSL
|
3
6
|
class ControllerDSL
|
@@ -14,6 +17,31 @@ module Moonrope
|
|
14
17
|
# @return [Moonrope::Controller] the associated controller
|
15
18
|
attr_reader :controller
|
16
19
|
|
20
|
+
#
|
21
|
+
# Stop this controller frmo being documented
|
22
|
+
#
|
23
|
+
def no_doc!
|
24
|
+
@controller.doc = false
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Set the friendly name for the controller
|
29
|
+
#
|
30
|
+
# @param name [String]
|
31
|
+
#
|
32
|
+
def friendly_name(string)
|
33
|
+
@controller.friendly_name = string
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Set the description for the controller
|
38
|
+
#
|
39
|
+
# @param description [String]
|
40
|
+
#
|
41
|
+
def description(description)
|
42
|
+
@controller.description = description
|
43
|
+
end
|
44
|
+
|
17
45
|
#
|
18
46
|
# Defines a new action within the controller.
|
19
47
|
#
|
@@ -28,6 +56,29 @@ module Moonrope
|
|
28
56
|
action
|
29
57
|
end
|
30
58
|
|
59
|
+
#
|
60
|
+
# Set the name of the authenticator to use for all actions in this controller
|
61
|
+
#
|
62
|
+
# @param name [Symbol]
|
63
|
+
#
|
64
|
+
def authenticator(name)
|
65
|
+
@controller.authenticator = name
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# Set the name of the access rule to use for all actions in this controller
|
70
|
+
#
|
71
|
+
# @param name [Symbol]
|
72
|
+
#
|
73
|
+
def access_rule(name)
|
74
|
+
if name.is_a?(Hash)
|
75
|
+
authenticator name.first[0]
|
76
|
+
access_rule name.first[1]
|
77
|
+
else
|
78
|
+
@controller.access_rule = name
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
31
82
|
#
|
32
83
|
# Defines a new before action within the controller.
|
33
84
|
#
|
@@ -43,14 +94,6 @@ module Moonrope
|
|
43
94
|
before_action
|
44
95
|
end
|
45
96
|
|
46
|
-
#
|
47
|
-
# Defines the access required for controller methods which do not
|
48
|
-
# define their own access.
|
49
|
-
#
|
50
|
-
def access(value = nil, &block)
|
51
|
-
@controller.access = block_given? ? block : value
|
52
|
-
end
|
53
|
-
|
54
97
|
#
|
55
98
|
# Defines a new helper for this controller.
|
56
99
|
#
|
@@ -61,9 +104,17 @@ module Moonrope
|
|
61
104
|
if @controller.base.helper(name, @controller)
|
62
105
|
raise Moonrope::Errors::HelperAlreadyDefined, "Helper has already been defined with name `#{name}`"
|
63
106
|
end
|
64
|
-
|
65
107
|
@controller.base.helpers << Moonrope::Helper.new(name, @controller, options, &block)
|
66
108
|
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Define a shared action which can be used by any action
|
112
|
+
#
|
113
|
+
# @param name[Symbol] the name of the shared action
|
114
|
+
#
|
115
|
+
def shared_action(name, &block)
|
116
|
+
@controller.shared_actions[name] = block
|
117
|
+
end
|
67
118
|
end
|
68
119
|
end
|
69
120
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Moonrope
|
2
|
+
module DSL
|
3
|
+
class FilterableDSL
|
4
|
+
|
5
|
+
def initialize(action)
|
6
|
+
@action = action
|
7
|
+
end
|
8
|
+
|
9
|
+
def attribute(name, options = {}, &block)
|
10
|
+
if options[:type] == Integer || options[:type] == Float
|
11
|
+
# Numbers
|
12
|
+
options[:operators] ||= [:eq, :not_eq, :gt, :gte, :lt, :lte, :in, :not_in]
|
13
|
+
elsif options[:type] == String
|
14
|
+
# Strings
|
15
|
+
options[:operators] ||= [:eq, :not_eq, :starts_with, :ends_with, :in, :not_in]
|
16
|
+
elsif options[:type] == :timestamp
|
17
|
+
# Times
|
18
|
+
options[:operators] ||= [:eq, :not_eq, :gt, :gte, :lt, :lte]
|
19
|
+
else
|
20
|
+
# Everything else
|
21
|
+
options[:operators] ||= [:eq, :not_eq]
|
22
|
+
end
|
23
|
+
@action.filters[name] = options.merge(:name => name, :block => block)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|