moonrope 1.2.5 → 1.3.0
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 +4 -4
- data/lib/moonrope/action.rb +29 -29
- data/lib/moonrope/action_result.rb +9 -9
- data/lib/moonrope/base.rb +27 -27
- data/lib/moonrope/before_action.rb +5 -5
- data/lib/moonrope/controller.rb +6 -6
- data/lib/moonrope/dsl/action_dsl.rb +18 -13
- data/lib/moonrope/dsl/base_dsl.rb +7 -7
- data/lib/moonrope/dsl/controller_dsl.rb +8 -8
- data/lib/moonrope/dsl/structure_dsl.rb +23 -14
- data/lib/moonrope/errors.rb +12 -12
- data/lib/moonrope/eval_environment.rb +22 -22
- data/lib/moonrope/eval_helpers.rb +3 -3
- data/lib/moonrope/helper.rb +4 -4
- data/lib/moonrope/param_set.rb +6 -6
- data/lib/moonrope/rack_middleware.rb +16 -16
- data/lib/moonrope/railtie.rb +10 -10
- data/lib/moonrope/request.rb +20 -20
- data/lib/moonrope/structure.rb +28 -28
- data/lib/moonrope/structure_attribute.rb +5 -6
- data/lib/moonrope/version.rb +2 -2
- metadata +2 -2
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
module Moonrope
|
|
2
2
|
module DSL
|
|
3
3
|
class StructureDSL
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
# @return [Moonrope::Structure] the associated structure
|
|
6
6
|
attr_reader :structure
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
# @return [Array] groups which should applied
|
|
9
9
|
attr_accessor :groups
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
# @return [Array] conditions which should applied
|
|
12
12
|
attr_accessor :conditions
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
# @return [Hash] options
|
|
15
15
|
attr_accessor :options
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
#
|
|
18
18
|
# Initialize a new StructureDSL
|
|
19
19
|
#
|
|
@@ -25,27 +25,36 @@ module Moonrope
|
|
|
25
25
|
@groups = []
|
|
26
26
|
@conditions = []
|
|
27
27
|
end
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
def scope(options = {}, &block)
|
|
30
30
|
scope_dsl = self.class.new(@structure)
|
|
31
31
|
scope_dsl.options = options
|
|
32
32
|
scope_dsl.instance_eval(&block)
|
|
33
33
|
end
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
def group(name, &block)
|
|
36
36
|
scope_dsl = self.class.new(@structure)
|
|
37
37
|
scope_dsl.groups = [@groups, name].flatten
|
|
38
38
|
scope_dsl.instance_eval(&block)
|
|
39
39
|
end
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
def condition(condition, &block)
|
|
42
42
|
scope_dsl = self.class.new(@structure)
|
|
43
43
|
scope_dsl.conditions = [@conditions, condition].flatten
|
|
44
44
|
scope_dsl.instance_eval(&block)
|
|
45
45
|
end
|
|
46
|
-
|
|
47
|
-
def attribute(type, name,
|
|
48
|
-
|
|
46
|
+
|
|
47
|
+
def attribute(type, name, options_or_description = {}, options_if_description = {})
|
|
48
|
+
|
|
49
|
+
if options_or_description.is_a?(String)
|
|
50
|
+
options_if_description[:description] = options_or_description
|
|
51
|
+
options = options_if_description
|
|
52
|
+
else
|
|
53
|
+
options = options_or_description
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
attribute = StructureAttribute.new(type, name)
|
|
57
|
+
attribute.description = options[:description]
|
|
49
58
|
attribute.structure = options[:structure]
|
|
50
59
|
attribute.structure_opts = options[:structure_opts]
|
|
51
60
|
attribute.value_type = options[:type]
|
|
@@ -56,7 +65,7 @@ module Moonrope
|
|
|
56
65
|
attribute.conditions = @conditions
|
|
57
66
|
@structure.attributes[type] << attribute
|
|
58
67
|
end
|
|
59
|
-
|
|
68
|
+
|
|
60
69
|
def basic(*args, &block)
|
|
61
70
|
if block_given?
|
|
62
71
|
@structure.basic = block
|
|
@@ -72,7 +81,7 @@ module Moonrope
|
|
|
72
81
|
attribute(:full, *args)
|
|
73
82
|
end
|
|
74
83
|
end
|
|
75
|
-
|
|
84
|
+
|
|
76
85
|
def expansion(name, *args, &block)
|
|
77
86
|
if block_given?
|
|
78
87
|
@structure.expansions[name] = block
|
|
@@ -80,7 +89,7 @@ module Moonrope
|
|
|
80
89
|
attribute(:expansion, name, *args)
|
|
81
90
|
end
|
|
82
91
|
end
|
|
83
|
-
|
|
92
|
+
|
|
84
93
|
end
|
|
85
94
|
end
|
|
86
95
|
end
|
data/lib/moonrope/errors.rb
CHANGED
|
@@ -1,54 +1,54 @@
|
|
|
1
1
|
module Moonrope
|
|
2
2
|
module Errors
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
class Error < StandardError
|
|
5
5
|
end
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
class HelperAlreadyDefined < Error; end
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
class RequestError < Error
|
|
10
10
|
attr_reader :options
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
def initialize(options)
|
|
13
13
|
@options = options
|
|
14
14
|
end
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
def status
|
|
17
17
|
@options.is_a?(Hash) ? @options[:status] || 'error' : 'error'
|
|
18
18
|
end
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
def data
|
|
21
21
|
{:message => @options}
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
class AccessDenied < RequestError
|
|
26
26
|
def status
|
|
27
27
|
'access-denied'
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
class NotFound < RequestError
|
|
32
32
|
def status
|
|
33
33
|
'not-found'
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
class ValidationError < RequestError
|
|
38
38
|
def status
|
|
39
39
|
'validation-error'
|
|
40
40
|
end
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
def data
|
|
43
43
|
{:errors => @options}
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
class ParameterError < RequestError
|
|
48
48
|
def status
|
|
49
49
|
'parameter-error'
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
end
|
|
54
54
|
end
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
module Moonrope
|
|
2
2
|
class EvalEnvironment
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
include Moonrope::EvalHelpers
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
# @return [Moonrope::Base] the base object
|
|
7
7
|
attr_reader :base
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
# @return [Moonrope::Request] the associated request
|
|
10
10
|
attr_reader :request
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
# @return [Hash] the headers
|
|
13
13
|
attr_reader :headers
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
# @return [Hash] the flags
|
|
16
16
|
attr_reader :flags
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
# @return [Hash] the default params to be merged with request params
|
|
19
19
|
attr_accessor :default_params
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
# @return [Moonrope::Action] the action which invoked this environment
|
|
22
22
|
attr_accessor :action
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
#
|
|
25
25
|
# Initialize a new EvalEnvironment
|
|
26
26
|
#
|
|
@@ -35,21 +35,21 @@ module Moonrope
|
|
|
35
35
|
@default_params = {}
|
|
36
36
|
reset
|
|
37
37
|
end
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
#
|
|
40
40
|
# @return [Integer] the requested API version
|
|
41
41
|
#
|
|
42
42
|
def version
|
|
43
43
|
request ? request.version : 1
|
|
44
44
|
end
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
#
|
|
47
47
|
# @return [Object] the authenticated object
|
|
48
48
|
#
|
|
49
49
|
def auth
|
|
50
50
|
request ? request.authenticated_user : nil
|
|
51
51
|
end
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
#
|
|
54
54
|
# @return [Hash] all parameters sent for this request including defaults
|
|
55
55
|
#
|
|
@@ -60,7 +60,7 @@ module Moonrope
|
|
|
60
60
|
params
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
|
-
|
|
63
|
+
|
|
64
64
|
#
|
|
65
65
|
# Set a header which should be returned to the client.
|
|
66
66
|
#
|
|
@@ -71,7 +71,7 @@ module Moonrope
|
|
|
71
71
|
def set_header(name, value)
|
|
72
72
|
@headers[name.to_s] = value
|
|
73
73
|
end
|
|
74
|
-
|
|
74
|
+
|
|
75
75
|
#
|
|
76
76
|
# Set a flag which should be returned to the client.
|
|
77
77
|
#
|
|
@@ -82,17 +82,17 @@ module Moonrope
|
|
|
82
82
|
def set_flag(name, value)
|
|
83
83
|
@flags[name] = value
|
|
84
84
|
end
|
|
85
|
-
|
|
86
|
-
#
|
|
85
|
+
|
|
86
|
+
#
|
|
87
87
|
# Clear all flags & headers from this environment.
|
|
88
|
-
#
|
|
88
|
+
#
|
|
89
89
|
# @return [void]
|
|
90
90
|
#
|
|
91
91
|
def reset
|
|
92
92
|
@flags = {}
|
|
93
93
|
@headers = {}
|
|
94
94
|
end
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
#
|
|
97
97
|
# Attempts to find an return an accessor from the has
|
|
98
98
|
#
|
|
@@ -109,10 +109,10 @@ module Moonrope
|
|
|
109
109
|
super
|
|
110
110
|
end
|
|
111
111
|
end
|
|
112
|
-
|
|
112
|
+
|
|
113
113
|
#
|
|
114
114
|
# Generate a new structure from the core DSL for the given
|
|
115
|
-
# object and return a hash or nil if the structure doesn't
|
|
115
|
+
# object and return a hash or nil if the structure doesn't
|
|
116
116
|
# exist.
|
|
117
117
|
#
|
|
118
118
|
# @param structure_name [Moonrope::Structure or Symbol] the structure to be used
|
|
@@ -131,7 +131,7 @@ module Moonrope
|
|
|
131
131
|
nil
|
|
132
132
|
end
|
|
133
133
|
end
|
|
134
|
-
|
|
134
|
+
|
|
135
135
|
#
|
|
136
136
|
# Return a Moonrope::Structure object for the provided name
|
|
137
137
|
#
|
|
@@ -145,7 +145,7 @@ module Moonrope
|
|
|
145
145
|
false
|
|
146
146
|
end
|
|
147
147
|
end
|
|
148
|
-
|
|
148
|
+
|
|
149
149
|
#
|
|
150
150
|
# Return whether or not a given structure name is valid?
|
|
151
151
|
#
|
|
@@ -154,6 +154,6 @@ module Moonrope
|
|
|
154
154
|
def has_structure_for?(structure_name)
|
|
155
155
|
self.structure_for(structure_name).is_a?(Moonrope::Structure)
|
|
156
156
|
end
|
|
157
|
-
|
|
157
|
+
|
|
158
158
|
end
|
|
159
159
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module Moonrope
|
|
2
2
|
module EvalHelpers
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
#
|
|
5
5
|
# Raise an error.
|
|
6
6
|
#
|
|
@@ -17,7 +17,7 @@ module Moonrope
|
|
|
17
17
|
raise Moonrope::Errors::RequestError, message
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
#
|
|
22
22
|
# Return paginated information
|
|
23
23
|
#
|
|
@@ -30,6 +30,6 @@ module Moonrope
|
|
|
30
30
|
block.call(result)
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
end
|
|
35
35
|
end
|
data/lib/moonrope/helper.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module Moonrope
|
|
2
2
|
class Helper
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
# @return [Symbol] the name of the helper
|
|
5
5
|
attr_reader :name
|
|
6
6
|
|
|
@@ -9,10 +9,10 @@ module Moonrope
|
|
|
9
9
|
|
|
10
10
|
# @return [Proc] the proc to execute
|
|
11
11
|
attr_reader :block
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
# @return [Hash] options for this helper
|
|
14
14
|
attr_reader :options
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
#
|
|
17
17
|
# Initialize a new helper
|
|
18
18
|
#
|
|
@@ -26,6 +26,6 @@ module Moonrope
|
|
|
26
26
|
@options = options
|
|
27
27
|
@block = block
|
|
28
28
|
end
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
end
|
|
31
31
|
end
|
data/lib/moonrope/param_set.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module Moonrope
|
|
2
2
|
class ParamSet
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
#
|
|
5
5
|
# Initialize a new ParamSet
|
|
6
6
|
#
|
|
@@ -12,7 +12,7 @@ module Moonrope
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
#
|
|
15
|
-
# Return the value for the given key
|
|
15
|
+
# Return the value for the given key
|
|
16
16
|
#
|
|
17
17
|
# @param key [String] the key to lookup
|
|
18
18
|
# @return [Object] the value
|
|
@@ -25,10 +25,10 @@ module Moonrope
|
|
|
25
25
|
# Return the value
|
|
26
26
|
value
|
|
27
27
|
end
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
alias_method :[], :_value_for
|
|
30
30
|
alias_method :method_missing, :_value_for
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
#
|
|
33
33
|
# Set the defaults for the param set
|
|
34
34
|
#
|
|
@@ -39,7 +39,7 @@ module Moonrope
|
|
|
39
39
|
@defaults = defaults
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
#
|
|
44
44
|
# Does the specified key exist?
|
|
45
45
|
#
|
|
@@ -49,6 +49,6 @@ module Moonrope
|
|
|
49
49
|
def has?(key)
|
|
50
50
|
@params.keys.include?(key.to_s) || @defaults.keys.include?(key.to_s)
|
|
51
51
|
end
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
end
|
|
54
54
|
end
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
module Moonrope
|
|
2
2
|
class RackMiddleware
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
#
|
|
5
5
|
# Initialize a new Moonrope::Rack server
|
|
6
6
|
#
|
|
7
7
|
# @param app [Object] the next Rack application in the stack
|
|
8
8
|
# @param base [Moonrope::Base] the base API to serve
|
|
9
9
|
# @param options [Hash] a hash of options
|
|
10
|
-
#
|
|
10
|
+
#
|
|
11
11
|
#
|
|
12
12
|
def initialize(app, base, options = {})
|
|
13
13
|
@app = app
|
|
14
14
|
@base = base
|
|
15
15
|
@options = options
|
|
16
16
|
end
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
attr_reader :base
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
#
|
|
21
21
|
# Make a new request
|
|
22
22
|
#
|
|
@@ -25,23 +25,23 @@ module Moonrope
|
|
|
25
25
|
#
|
|
26
26
|
def call(env)
|
|
27
27
|
if env['PATH_INFO'] =~ Moonrope::Request.path_regex
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
if @options[:reload_on_each_request]
|
|
30
30
|
@base.load
|
|
31
31
|
end
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
#
|
|
34
34
|
# Call the on request block if one has been defined for the base.
|
|
35
35
|
#
|
|
36
36
|
if @base.on_request.is_a?(Proc)
|
|
37
37
|
@base.on_request.call(@base, env)
|
|
38
38
|
end
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
#
|
|
41
41
|
# Create a new request object
|
|
42
42
|
#
|
|
43
43
|
request = @base.request(env, $1)
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
#
|
|
46
46
|
# Set some global headers which are always returned
|
|
47
47
|
#
|
|
@@ -52,26 +52,26 @@ module Moonrope
|
|
|
52
52
|
global_headers['Access-Control-Allow-Headers'] = env['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']
|
|
53
53
|
end
|
|
54
54
|
global_headers['Access-Control-Allow-Methods'] = '*'
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
#
|
|
57
57
|
# Options always returns a 200
|
|
58
58
|
#
|
|
59
59
|
if env['REQUEST_METHOD'] == 'OPTIONS'
|
|
60
60
|
return [200, global_headers, ['OK']]
|
|
61
61
|
end
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
#
|
|
64
64
|
# Responses are now in JSON
|
|
65
65
|
#
|
|
66
66
|
global_headers['Content-Type'] = 'application/json'
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
#
|
|
69
69
|
# Check the request is valid
|
|
70
70
|
#
|
|
71
71
|
unless request.valid?
|
|
72
72
|
return [400, global_headers, [{:status => 'invalid-controller-or-action'}.to_json]]
|
|
73
73
|
end
|
|
74
|
-
|
|
74
|
+
|
|
75
75
|
#
|
|
76
76
|
# Execute the request
|
|
77
77
|
#
|
|
@@ -86,16 +86,16 @@ module Moonrope
|
|
|
86
86
|
Moonrope.logger.info e.class
|
|
87
87
|
Moonrope.logger.info e.message
|
|
88
88
|
Moonrope.logger.info e.backtrace.join("\n")
|
|
89
|
-
|
|
89
|
+
|
|
90
90
|
response = {:status => 'internal-server-error'}
|
|
91
|
-
|
|
91
|
+
|
|
92
92
|
# If in development, return more details about the exception which was raised.
|
|
93
93
|
if @base.environment == 'development'
|
|
94
94
|
response[:error] = e.class.to_s
|
|
95
95
|
response[:message] = e.message
|
|
96
96
|
response[:backtrace] = e.backtrace[0,6]
|
|
97
97
|
end
|
|
98
|
-
|
|
98
|
+
|
|
99
99
|
[500, global_headers, [response.to_json]]
|
|
100
100
|
end
|
|
101
101
|
|
|
@@ -107,6 +107,6 @@ module Moonrope
|
|
|
107
107
|
end
|
|
108
108
|
end
|
|
109
109
|
end
|
|
110
|
-
|
|
110
|
+
|
|
111
111
|
end
|
|
112
112
|
end
|
data/lib/moonrope/railtie.rb
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
module Moonrope
|
|
2
2
|
class Railtie < Rails::Railtie
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
initializer 'moonrope.initialize' do |app|
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
# Initialize a new moonrope base.
|
|
7
7
|
app.config.moonrope = Moonrope::Base.load(Rails.root.join('api'))
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
# Set the logger
|
|
10
10
|
Moonrope.logger = Rails.logger
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
# Set the environment to match the Rails environment
|
|
13
13
|
app.config.moonrope.environment = Rails.env.to_s
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
# Ensure all request use UTC
|
|
16
16
|
app.config.moonrope.on_request = Proc.new do |base, env|
|
|
17
17
|
Time.zone = 'UTC'
|
|
@@ -21,14 +21,14 @@ module Moonrope
|
|
|
21
21
|
if app.config.respond_to?(:moonrope_request_path_regex) && app.config.moonrope_request_path_regex.is_a?(Regexp)
|
|
22
22
|
Moonrope::Request.path_regex = app.config.moonrope_request_path_regex
|
|
23
23
|
end
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
# Catch ActiveRecord::RecordNotFound exception as a standard not-found error
|
|
26
26
|
if defined?(ActiveRecord)
|
|
27
27
|
app.config.moonrope.register_external_error ActiveRecord::RecordNotFound do |exception, result|
|
|
28
28
|
result.status = 'not-found'
|
|
29
29
|
result.data = {:message => exception.message}
|
|
30
30
|
end
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
# Add a helper for auto setting parameters
|
|
33
33
|
app.config.moonrope.dsl.instance_eval do
|
|
34
34
|
helper :auto_set_params_for, :unloadable => false do |object|
|
|
@@ -39,7 +39,7 @@ module Moonrope
|
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
# Insert the Moonrope middleware into the application's middleware
|
|
44
44
|
# stack (at the bottom).
|
|
45
45
|
app.middleware.use(
|
|
@@ -47,8 +47,8 @@ module Moonrope
|
|
|
47
47
|
app.config.moonrope,
|
|
48
48
|
:reload_on_each_request => !app.config.cache_classes
|
|
49
49
|
)
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
end
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
end
|
|
54
54
|
end
|