cabbage_doc 0.0.1
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.
- checksums.yaml +7 -0
- data/lib/cabbage_doc/action.rb +86 -0
- data/lib/cabbage_doc/authentication.rb +54 -0
- data/lib/cabbage_doc/client.rb +27 -0
- data/lib/cabbage_doc/cloneable.rb +9 -0
- data/lib/cabbage_doc/collection.rb +67 -0
- data/lib/cabbage_doc/configuration.rb +52 -0
- data/lib/cabbage_doc/controller.rb +117 -0
- data/lib/cabbage_doc/customizer.rb +9 -0
- data/lib/cabbage_doc/example.rb +25 -0
- data/lib/cabbage_doc/pacto_helper.rb +27 -0
- data/lib/cabbage_doc/parameter.rb +72 -0
- data/lib/cabbage_doc/params.rb +59 -0
- data/lib/cabbage_doc/parser.rb +47 -0
- data/lib/cabbage_doc/path.rb +9 -0
- data/lib/cabbage_doc/processor.rb +41 -0
- data/lib/cabbage_doc/processors/contracts.rb +12 -0
- data/lib/cabbage_doc/processors/documentation.rb +15 -0
- data/lib/cabbage_doc/processors/rspec.rb +11 -0
- data/lib/cabbage_doc/request.rb +70 -0
- data/lib/cabbage_doc/response.rb +37 -0
- data/lib/cabbage_doc/singleton.rb +15 -0
- data/lib/cabbage_doc/task.rb +72 -0
- data/lib/cabbage_doc/version.rb +3 -0
- data/lib/cabbage_doc/web.rb +39 -0
- data/lib/cabbage_doc/web_helper.rb +25 -0
- data/lib/cabbage_doc.rb +34 -0
- data/web/public/css/application.css +184 -0
- data/web/public/js/application.js +113 -0
- data/web/public/js/jquery-1.12.3.min.js +5 -0
- data/web/views/action.haml +20 -0
- data/web/views/array.haml +13 -0
- data/web/views/authentication.haml +25 -0
- data/web/views/controller.haml +7 -0
- data/web/views/documentation.haml +3 -0
- data/web/views/enumeration.haml +9 -0
- data/web/views/index.haml +8 -0
- data/web/views/layout.haml +10 -0
- data/web/views/parameter.haml +12 -0
- data/web/views/response.haml +14 -0
- data/web/views/text.haml +1 -0
- data/web/views/welcome.haml +1 -0
- metadata +196 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 72c954cb92be482568747328289d2c65daf273a6
|
4
|
+
data.tar.gz: aafdefb0496f2a2152402c492b4cc8b4eb11ef77
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 16cd85c66b78ad28fa3c2791e7df1922cd33160754148f0cf42b4861d66705e521d3421ed5ead623643f9a046ea802713c81b8a3f6d2292ac8e9b78955f82897
|
7
|
+
data.tar.gz: 5afa6dec9856b545e4c0ba4d4ce691b623dd153cc8dc45f11bae3e66ac10ee567d81950e8398b52ece74eab5459cdee6a372b90deac2092ebf9b5f14097c7d17
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module CabbageDoc
|
2
|
+
class Action
|
3
|
+
include Parser
|
4
|
+
|
5
|
+
METHODS = %w(GET POST PUT DELETE).freeze
|
6
|
+
METHODS_REGEXP = METHODS.join('|').freeze
|
7
|
+
|
8
|
+
attr_reader :label, :name, :description, :path, :method, :parameters, :examples
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@parameters = []
|
12
|
+
@examples = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse(text)
|
16
|
+
@method, @path = parse_method_and_path(text)
|
17
|
+
return unless valid?
|
18
|
+
|
19
|
+
@name = parse_name(text)
|
20
|
+
@label = parse_label(text)
|
21
|
+
@description = parse_description(text)
|
22
|
+
|
23
|
+
@parameters, @examples = parse_parameters_and_examples(text)
|
24
|
+
|
25
|
+
valid?
|
26
|
+
end
|
27
|
+
|
28
|
+
def valid?
|
29
|
+
@method && @path
|
30
|
+
end
|
31
|
+
|
32
|
+
def param?(key)
|
33
|
+
@parameters.any? { |parameter| parameter.name =~ /#{key}/ }
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# FIXME: get rid of this and optimize direct regex matches
|
39
|
+
def text_to_lines(text)
|
40
|
+
text.gsub(/\r\n?/, "\n").split(/\n/)[0..-2].map do |line|
|
41
|
+
line.strip!
|
42
|
+
line.slice!(0)
|
43
|
+
line.strip!
|
44
|
+
line if line.size > 0
|
45
|
+
end.compact
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse_parameters_and_examples(text)
|
49
|
+
# FIXME: rewrite this to do a 'scan' with the right Regexp
|
50
|
+
parameters = []
|
51
|
+
examples = []
|
52
|
+
|
53
|
+
lines = text_to_lines(text)
|
54
|
+
|
55
|
+
lines.each do |line|
|
56
|
+
if parameter = Parameter.parse(line)
|
57
|
+
parameters << parameter
|
58
|
+
elsif example = Example.parse(line)
|
59
|
+
examples << example
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
[parameters, examples]
|
64
|
+
end
|
65
|
+
|
66
|
+
def parse_method_and_path(text)
|
67
|
+
m = text.match(/#\s*(#{METHODS_REGEXP}):\s*(.*?)$/)
|
68
|
+
[m[1].strip.upcase, m[2].strip] if m
|
69
|
+
end
|
70
|
+
|
71
|
+
def parse_name(text)
|
72
|
+
m = text.match(/def\s+(.*?)\s*#\s*#{MARKER}$/)
|
73
|
+
m[1].strip if m
|
74
|
+
end
|
75
|
+
|
76
|
+
def parse_label(text)
|
77
|
+
m = text.match(/#\s*Public:(.*?)$/)
|
78
|
+
m[1].strip if m
|
79
|
+
end
|
80
|
+
|
81
|
+
def parse_description(text)
|
82
|
+
m = text.match(/#\s*Description:(.*?)$/)
|
83
|
+
m[1].strip if m
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module CabbageDoc
|
2
|
+
class Authentication
|
3
|
+
class << self
|
4
|
+
def new(request = nil)
|
5
|
+
super().tap do |auth|
|
6
|
+
yield(auth) if block_given?
|
7
|
+
Configuration.instance.authentication.call(auth, request)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_accessor :type,
|
13
|
+
:username,
|
14
|
+
:password,
|
15
|
+
:token,
|
16
|
+
:domain,
|
17
|
+
:subdomain,
|
18
|
+
:subdomains,
|
19
|
+
:scheme,
|
20
|
+
:path,
|
21
|
+
:user_agent,
|
22
|
+
:configurable,
|
23
|
+
:verbose
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
Configuration.instance.tap do |config|
|
27
|
+
@domain = config.domain
|
28
|
+
@scheme = config.scheme
|
29
|
+
@path = config.path
|
30
|
+
@user_agent = config.title
|
31
|
+
end
|
32
|
+
|
33
|
+
@verbose = false
|
34
|
+
@subdomains = []
|
35
|
+
@configurable = []
|
36
|
+
@type = :basic
|
37
|
+
end
|
38
|
+
|
39
|
+
def valid?
|
40
|
+
case type
|
41
|
+
when :basic
|
42
|
+
username && password
|
43
|
+
when :token
|
44
|
+
!token.nil?
|
45
|
+
else
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def configurable?
|
51
|
+
@configurable.any?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module CabbageDoc
|
2
|
+
class Client
|
3
|
+
include HTTParty
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def new(auth)
|
7
|
+
Class.new(self) do |klass|
|
8
|
+
klass.headers "User-Agent" => auth.user_agent
|
9
|
+
|
10
|
+
if auth.subdomain
|
11
|
+
klass.base_uri "#{auth.scheme}://#{auth.subdomain}.#{auth.domain}/#{auth.path}"
|
12
|
+
else
|
13
|
+
klass.base_uri "#{auth.scheme}://#{auth.domain}/#{auth.path}"
|
14
|
+
end
|
15
|
+
|
16
|
+
if auth.type == :basic
|
17
|
+
klass.basic_auth auth.username, auth.password
|
18
|
+
elsif auth.token
|
19
|
+
klass.headers "Authorization" => "#{auth.type.to_s.capitalize} #{auth.token}"
|
20
|
+
end
|
21
|
+
|
22
|
+
debug_output $stdout if auth.verbose
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module CabbageDoc
|
4
|
+
class Collection
|
5
|
+
include Singleton
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
FILENAME = "controllers.yml".freeze
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@_controllers = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def <<(controller)
|
15
|
+
@_controllers << controller
|
16
|
+
end
|
17
|
+
|
18
|
+
def find_action(method, path)
|
19
|
+
action = nil
|
20
|
+
|
21
|
+
@_controllers.each do |controller|
|
22
|
+
action = controller.find_action(method, path)
|
23
|
+
break if action
|
24
|
+
end
|
25
|
+
|
26
|
+
action
|
27
|
+
end
|
28
|
+
|
29
|
+
def each
|
30
|
+
@_controllers.each do |controller|
|
31
|
+
yield controller
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def parse!(filename)
|
36
|
+
text = File.read(filename) rescue nil
|
37
|
+
return false unless text
|
38
|
+
|
39
|
+
controller = Controller.parse(text)
|
40
|
+
return false unless controller
|
41
|
+
|
42
|
+
controllers = controller.eval(text)
|
43
|
+
|
44
|
+
@_controllers.concat(controllers)
|
45
|
+
|
46
|
+
controllers.any?
|
47
|
+
end
|
48
|
+
|
49
|
+
def clear!
|
50
|
+
@_controllers = []
|
51
|
+
end
|
52
|
+
|
53
|
+
def load!
|
54
|
+
@_controllers = YAML.load(File.read(filename)) rescue [] unless @_controllers.any?
|
55
|
+
end
|
56
|
+
|
57
|
+
def save!
|
58
|
+
open(filename, 'w') { |f| f.write(YAML.dump(@_controllers)) } rescue nil
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def filename
|
64
|
+
@_filename ||= Path.join(Configuration.instance.root, FILENAME)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module CabbageDoc
|
2
|
+
class Configuration
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
DEFAULTS = {
|
6
|
+
version: 'v1',
|
7
|
+
path: 'api/v1',
|
8
|
+
title: 'Cabbage Doc',
|
9
|
+
scheme: 'https',
|
10
|
+
verbose: false
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
OPTIONAL_ATTRIBUTES = %i(welcome path scheme version title verbose authentication).freeze
|
14
|
+
REQUIRED_ATTRIBUTES = %i(domain controllers root).freeze
|
15
|
+
ATTRIBUTES = (OPTIONAL_ATTRIBUTES + REQUIRED_ATTRIBUTES).freeze
|
16
|
+
CALLABLE_ATTRIBUTES = %i(controllers authentication).freeze
|
17
|
+
|
18
|
+
attr_accessor *ATTRIBUTES
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
DEFAULTS.each do |attr, value|
|
22
|
+
send(:"#{attr}=", value)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def validate!
|
27
|
+
validate_required!
|
28
|
+
validate_callable!
|
29
|
+
validate_root!
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def validate_required!
|
35
|
+
REQUIRED_ATTRIBUTES.each do |attr|
|
36
|
+
raise ArgumentError, "#{attr} is required" unless send(attr)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def validate_callable!
|
41
|
+
CALLABLE_ATTRIBUTES.each do |attr|
|
42
|
+
if (value = send(attr)) && !value.respond_to?(:call)
|
43
|
+
raise ArgumentError, "#{attr} is not callable"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def validate_root!
|
49
|
+
raise ArgumentError, "#{root} directory doesn't exist" unless Dir.exists?(root)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module CabbageDoc
|
2
|
+
class Controller
|
3
|
+
include Parser
|
4
|
+
include Cloneable
|
5
|
+
|
6
|
+
attr_reader :label, :klass, :name, :path, :actions
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@actions = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse(text)
|
13
|
+
@label, @path, @klass = parse_label_path_and_class(text)
|
14
|
+
return false unless @label && @klass
|
15
|
+
|
16
|
+
@name = compose_name(klass)
|
17
|
+
|
18
|
+
@actions = parse_actions(text) unless template?
|
19
|
+
|
20
|
+
valid?
|
21
|
+
end
|
22
|
+
|
23
|
+
def valid?
|
24
|
+
@name && (actions? || template?)
|
25
|
+
end
|
26
|
+
|
27
|
+
def find_action(method, path)
|
28
|
+
@actions.detect do |action|
|
29
|
+
action.method == method && action.path == path
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def eval(text)
|
34
|
+
return [self] unless template?
|
35
|
+
|
36
|
+
templates = []
|
37
|
+
|
38
|
+
templates += parse_templates(@path)
|
39
|
+
templates += parse_templates(@label)
|
40
|
+
|
41
|
+
return [] unless templates.any?
|
42
|
+
|
43
|
+
count = templates.first[:values].count
|
44
|
+
|
45
|
+
(1..count).map do |i|
|
46
|
+
template_text = text.dup
|
47
|
+
|
48
|
+
templates.each do |template|
|
49
|
+
template_text.gsub!(template[:text], template[:values].shift.to_s)
|
50
|
+
end
|
51
|
+
|
52
|
+
self.class.parse(template_text)
|
53
|
+
end.compact
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def compose_label(metadata, klass)
|
59
|
+
metadata[:label] || klass.sub(/Controller$/, '')
|
60
|
+
end
|
61
|
+
|
62
|
+
def compose_path(metadata, klass)
|
63
|
+
Path.join('/', Configuration.instance.path, metadata[:path] || compose_name(klass))
|
64
|
+
end
|
65
|
+
|
66
|
+
def compose_name(klass)
|
67
|
+
compose_label({}, klass).downcase
|
68
|
+
end
|
69
|
+
|
70
|
+
def parse_label_path_and_class(text)
|
71
|
+
klass = parse_class(text)
|
72
|
+
return unless klass
|
73
|
+
|
74
|
+
metadata = parse_metadata(text)
|
75
|
+
|
76
|
+
[compose_label(metadata, klass), compose_path(metadata, klass), klass]
|
77
|
+
end
|
78
|
+
|
79
|
+
def parse_actions(text)
|
80
|
+
actions = []
|
81
|
+
|
82
|
+
text.scan(/(#\s*Public:\s*.*?(#{Action::METHODS_REGEXP}):.*?def\s+.*?\s*#\s*#{MARKER})/m) do
|
83
|
+
actions << Action.parse($1.strip)
|
84
|
+
end
|
85
|
+
|
86
|
+
actions.compact
|
87
|
+
end
|
88
|
+
|
89
|
+
def parse_class(text)
|
90
|
+
m = text.match(/class\s+(.*?)\s+?#\s+?#{MARKER}$/)
|
91
|
+
m[1].strip.split('<').first.strip if m
|
92
|
+
end
|
93
|
+
|
94
|
+
def parse_metadata(text)
|
95
|
+
m = text.match(/(#\s*Public:\s*.*?class\s+.*?\s*#\s*#{MARKER})/m)
|
96
|
+
return {} unless m
|
97
|
+
|
98
|
+
metadata = m[1].strip
|
99
|
+
|
100
|
+
{}.tap do |hash|
|
101
|
+
m = metadata.match(/#\s*Public:(.*?)$/)
|
102
|
+
hash[:label] = m[1].strip if m
|
103
|
+
|
104
|
+
m = metadata.match(/#\s*PATH:\s*\/(.*?)$/)
|
105
|
+
hash[:path] = m[1].strip if m
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def actions?
|
110
|
+
@actions.any?
|
111
|
+
end
|
112
|
+
|
113
|
+
def template?
|
114
|
+
@path =~ /\/{.*?,.*?}/
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module CabbageDoc
|
2
|
+
class Example
|
3
|
+
include Parser
|
4
|
+
|
5
|
+
attr_reader :label, :params
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@params = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse(text)
|
12
|
+
m = text.match(/^(.*?)\s+-\s+(\(.*?\))$/)
|
13
|
+
return false unless m
|
14
|
+
|
15
|
+
@label = m[1].strip
|
16
|
+
@params = parse_option(m[2].strip)
|
17
|
+
|
18
|
+
valid?
|
19
|
+
end
|
20
|
+
|
21
|
+
def valid?
|
22
|
+
!@label.nil?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module CabbageDoc
|
2
|
+
module PactoHelper
|
3
|
+
def pacto_enable!
|
4
|
+
WebMock.allow_net_connect!
|
5
|
+
|
6
|
+
Pacto.configure do |config|
|
7
|
+
config.contracts_path = Configuration.instance.root
|
8
|
+
end
|
9
|
+
|
10
|
+
Pacto.generate!
|
11
|
+
end
|
12
|
+
|
13
|
+
def pacto_disable!
|
14
|
+
Pacto.stop_generating!
|
15
|
+
end
|
16
|
+
|
17
|
+
def pacto_available?
|
18
|
+
require 'forwardable'
|
19
|
+
require 'pacto'
|
20
|
+
|
21
|
+
defined?(Pacto)
|
22
|
+
rescue LoadError => e
|
23
|
+
puts "WARNING: Pacto is not available."
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module CabbageDoc
|
2
|
+
class Parameter
|
3
|
+
include Parser
|
4
|
+
|
5
|
+
TYPES = %i(numeric decimal integer string id enumeration array date time timestamp hash)
|
6
|
+
|
7
|
+
attr_reader :label, :name, :type, :type_label, :default, :values, :required
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@values = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse(text)
|
14
|
+
m = text.match(/^(.*?\s+\(.*?\).*?)\s+-\s+(.*?)$/)
|
15
|
+
return false unless m
|
16
|
+
|
17
|
+
@name, @type_label, @required = parse_name_type_required(m[1].strip)
|
18
|
+
@type = @type_label.downcase.to_sym if @type_label
|
19
|
+
|
20
|
+
@required = !!@required
|
21
|
+
|
22
|
+
@label, @default, @values = parse_label_default_values(m[2].strip)
|
23
|
+
@values ||= []
|
24
|
+
|
25
|
+
valid?
|
26
|
+
end
|
27
|
+
|
28
|
+
def valid?
|
29
|
+
@type && TYPES.include?(@type)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def parse_label_default_values(text)
|
35
|
+
m = text.match(/^(.*?)\s*(\(.*?\))?$/)
|
36
|
+
return unless m
|
37
|
+
|
38
|
+
index, options = parse_options(text)
|
39
|
+
|
40
|
+
arr = [text[0..index-1].strip]
|
41
|
+
|
42
|
+
if options.any?
|
43
|
+
arr << options[:default]
|
44
|
+
arr << Array(options[:values])
|
45
|
+
end
|
46
|
+
|
47
|
+
arr
|
48
|
+
end
|
49
|
+
|
50
|
+
def parse_name_type_required(text)
|
51
|
+
text.split(/\s+/).map(&:strip).map do |component|
|
52
|
+
if component =~ /^\((.*?)\)$/
|
53
|
+
$1
|
54
|
+
elsif component =~ /\[required\]/i
|
55
|
+
true
|
56
|
+
else
|
57
|
+
component
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def parse_options(text)
|
63
|
+
m = text.match(/:.*?\)$/)
|
64
|
+
return [0, {}] unless m
|
65
|
+
|
66
|
+
index = text.rindex('(')
|
67
|
+
return [0, {}] unless index
|
68
|
+
|
69
|
+
[index, parse_option(text[index..-1].strip)]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module CabbageDoc
|
2
|
+
class Params
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize(params, collection)
|
6
|
+
@_params = convert(params, collection)
|
7
|
+
end
|
8
|
+
|
9
|
+
def each
|
10
|
+
@_params.each do |k, v|
|
11
|
+
yield(k, v)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def delete(key)
|
16
|
+
@_params.delete(key)
|
17
|
+
end
|
18
|
+
|
19
|
+
def find(key)
|
20
|
+
@_params[key]
|
21
|
+
end
|
22
|
+
|
23
|
+
def valid?
|
24
|
+
@_params.any?
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_hash
|
28
|
+
@_params
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_query
|
32
|
+
@_params.map do |k, v|
|
33
|
+
if v.is_a?(Array)
|
34
|
+
v.map { |vv| "#{k}[]=#{CGI.escape(vv)}" }
|
35
|
+
else
|
36
|
+
"#{k}=#{CGI.escape(v)}"
|
37
|
+
end
|
38
|
+
end.flatten.join('&')
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def convert(params, collection)
|
44
|
+
method = params['method']
|
45
|
+
action = params['action']
|
46
|
+
|
47
|
+
return {} unless action && method
|
48
|
+
|
49
|
+
action = collection.find_action(method, action)
|
50
|
+
return {} unless action
|
51
|
+
|
52
|
+
{}.tap do |hash|
|
53
|
+
params.each do |k, v|
|
54
|
+
hash[k] = v if action.param?(k.sub(/\[\]$/, ''))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module CabbageDoc
|
2
|
+
module Parser
|
3
|
+
class << self
|
4
|
+
def included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def parse(text)
|
11
|
+
instance = new
|
12
|
+
instance if instance.parse(text)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse(text)
|
17
|
+
raise NotImplementedError
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse_option(text)
|
21
|
+
m = text.match(/^\((.*?)\)$/)
|
22
|
+
return {} unless m
|
23
|
+
|
24
|
+
{}.tap do |hash|
|
25
|
+
m[1].split(/,/).map(&:strip).each do |o|
|
26
|
+
k, v = o.split(':').map(&:strip)
|
27
|
+
next unless k && v
|
28
|
+
|
29
|
+
v = v.split('|').map(&:strip)
|
30
|
+
v = v.first if v.size == 1
|
31
|
+
|
32
|
+
hash[k.to_sym] = v
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse_templates(text)
|
38
|
+
templates = []
|
39
|
+
|
40
|
+
text.scan(/(\{(.*?)\})/) do
|
41
|
+
templates << { text: $1, values: $2.split(/,/).map(&:strip) }
|
42
|
+
end
|
43
|
+
|
44
|
+
templates
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|