flame 4.18.1 → 5.0.0.rc6

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 (54) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +921 -0
  3. data/LICENSE.txt +19 -0
  4. data/README.md +135 -0
  5. data/lib/flame.rb +12 -4
  6. data/lib/flame/application.rb +93 -40
  7. data/lib/flame/config.rb +73 -0
  8. data/lib/flame/controller.rb +62 -98
  9. data/lib/flame/controller/actions.rb +122 -0
  10. data/lib/flame/controller/cookies.rb +44 -0
  11. data/lib/flame/controller/path_to.rb +63 -0
  12. data/lib/flame/dispatcher.rb +44 -73
  13. data/lib/flame/dispatcher/request.rb +33 -4
  14. data/lib/flame/dispatcher/routes.rb +66 -0
  15. data/lib/flame/dispatcher/static.rb +26 -15
  16. data/lib/flame/errors/argument_not_assigned_error.rb +7 -6
  17. data/lib/flame/errors/config_file_not_found_error.rb +17 -0
  18. data/lib/flame/errors/controller_not_found_error.rb +19 -0
  19. data/lib/flame/errors/route_arguments_order_error.rb +9 -8
  20. data/lib/flame/errors/route_extra_arguments_error.rb +18 -18
  21. data/lib/flame/errors/route_not_found_error.rb +8 -7
  22. data/lib/flame/errors/template_not_found_error.rb +6 -6
  23. data/lib/flame/path.rb +141 -55
  24. data/lib/flame/render.rb +46 -15
  25. data/lib/flame/router.rb +41 -127
  26. data/lib/flame/router/controller_finder.rb +56 -0
  27. data/lib/flame/router/route.rb +16 -54
  28. data/lib/flame/router/routes.rb +136 -0
  29. data/lib/flame/router/routes_refine.rb +144 -0
  30. data/lib/flame/router/routes_refine/mounting.rb +57 -0
  31. data/lib/flame/validators.rb +21 -11
  32. data/lib/flame/version.rb +1 -1
  33. metadata +139 -84
  34. data/bin/flame +0 -71
  35. data/lib/flame/application/config.rb +0 -43
  36. data/lib/flame/dispatcher/cookies.rb +0 -31
  37. data/template/.gitignore +0 -11
  38. data/template/Gemfile +0 -15
  39. data/template/Rakefile.erb +0 -64
  40. data/template/app.rb.erb +0 -7
  41. data/template/config.ru.erb +0 -20
  42. data/template/config/config.rb.erb +0 -14
  43. data/template/config/database.example.yml +0 -5
  44. data/template/config/sequel.rb.erb +0 -15
  45. data/template/config/thin.example.yml +0 -18
  46. data/template/controllers/_base_controller.rb.erb +0 -13
  47. data/template/db/.keep +0 -0
  48. data/template/helpers/.keep +0 -0
  49. data/template/lib/.keep +0 -0
  50. data/template/locales/en.yml +0 -0
  51. data/template/models/.keep +0 -0
  52. data/template/public/.keep +0 -0
  53. data/template/server +0 -49
  54. data/template/views/.keep +0 -0
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flame
4
+ class Dispatcher
5
+ ## Module for working with routes
6
+ module Routes
7
+ private
8
+
9
+ ## Find route and try execute it
10
+ def try_route
11
+ http_method = request.http_method
12
+ http_method = :GET if http_method == :HEAD
13
+ return unless available_endpoint
14
+
15
+ route = available_endpoint[http_method]
16
+ return unless route || available_endpoint.allow
17
+
18
+ halt(405, nil, 'Allow' => available_endpoint.allow) unless route
19
+ status 200
20
+ execute_route route
21
+ true
22
+ end
23
+
24
+ ## Execute route
25
+ ## @param route [Flame::Route] route that must be executed
26
+ def execute_route(route, action = route.action)
27
+ params.merge!(
28
+ router.path_of(route).extract_arguments(request.path)
29
+ )
30
+ # route.execute(self)
31
+ @current_controller = route.controller.new(self)
32
+ @current_controller.send(:execute, action)
33
+ rescue StandardError, SyntaxError => e
34
+ # p 'rescue from dispatcher'
35
+ dump_error(e)
36
+ status 500
37
+ @current_controller&.send(:server_error, e)
38
+ # p 're raise error from dispatcher'
39
+ # raise e
40
+ end
41
+
42
+ ## Generate a default body of nearest route
43
+ def default_body_of_nearest_route
44
+ ## Return nil if must be no body for current HTTP status
45
+ return if Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status)
46
+
47
+ ## Find the nearest route by the parts of requested path
48
+ route = router.find_nearest_route(request.path)
49
+ ## Return standard `default_body` if the route not found
50
+ return default_body unless route
51
+
52
+ return not_found_body(route) if response.not_found?
53
+
54
+ controller =
55
+ (@current_controller if defined?(@current_controller)) || route.controller.new(self)
56
+ controller.send :default_body
57
+ end
58
+
59
+ def not_found_body(route)
60
+ ## Execute `not_found` method as action for the founded route
61
+ execute_route(route, :not_found)
62
+ body
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,9 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'cgi'
4
+
3
5
  module Flame
4
6
  class Dispatcher
5
7
  ## Module for working with static files
6
8
  module Static
9
+ ## Find static file by path
10
+ ## @param filename [String] relative path to the static file
11
+ ## @param dir [String]
12
+ ## absolute local path of the directory with static files
13
+ ## @return [Flame::Dispatcher::Static::StaticFile] instance of static file
7
14
  def find_static(filename = request.path_info, dir: config[:public_dir])
8
15
  StaticFile.new(filename, dir)
9
16
  end
@@ -11,52 +18,56 @@ module Flame
11
18
  private
12
19
 
13
20
  ## Find static files and try return it
14
- def try_static(*args)
15
- file = find_static(*args)
21
+ def try_static(*args, **kwargs)
22
+ file = find_static(*args, **kwargs)
16
23
  return nil unless file.exist?
24
+
25
+ halt 400 unless file.within_directory
17
26
  return_static(file)
18
27
  end
19
28
 
20
29
  def return_static(file)
21
- halt 304 if file.newer? request.env['HTTP_IF_MODIFIED_SINCE']
30
+ response[Rack::CACHE_CONTROL] = 'public, max-age=31536000' # one year
31
+ halt 304 unless file.newer? request.env['HTTP_IF_MODIFIED_SINCE']
22
32
  response.content_type = file.extname
23
- response[Rack::CACHE_CONTROL] = 'no-cache'
24
33
  response['Last-Modified'] = file.mtime.httpdate
25
34
  body file.content
26
35
  end
27
36
 
28
37
  ## Class for static files with helpers methods
29
38
  class StaticFile
39
+ attr_reader :extname, :within_directory
40
+
30
41
  def initialize(filename, dir)
31
42
  @filename = filename.to_s
32
- @path = File.join dir, URI.decode(@filename)
43
+ @directory = File.expand_path dir
44
+ @file_path = File.expand_path File.join dir, CGI.unescape(@filename)
45
+ @extname = File.extname(@file_path)
46
+ @within_directory = @file_path.start_with? @directory
33
47
  end
34
48
 
35
49
  def exist?
36
- File.exist?(@path) && File.file?(@path)
50
+ File.exist?(@file_path) && File.file?(@file_path)
37
51
  end
38
52
 
39
53
  def mtime
40
- File.mtime(@path)
41
- end
42
-
43
- def extname
44
- File.extname(@path)
54
+ File.mtime(@file_path)
45
55
  end
46
56
 
47
57
  def newer?(http_since)
48
- http_since && Time.httpdate(http_since).to_i >= mtime.to_i
58
+ !http_since || mtime.to_i > Time.httpdate(http_since).to_i
49
59
  end
50
60
 
51
61
  def content
52
- File.read(@path)
62
+ File.read(@file_path)
53
63
  end
54
64
 
55
65
  def path(with_version: false)
56
- path = @filename
57
- with_version ? "#{path}?v=#{mtime.to_i}" : path
66
+ with_version ? "#{@filename}?v=#{mtime.to_i}" : @filename
58
67
  end
59
68
  end
69
+
70
+ private_constant :StaticFile
60
71
  end
61
72
  end
62
73
  end
@@ -4,13 +4,14 @@ module Flame
4
4
  module Errors
5
5
  ## Error for Flame::Dispatcher.path_to
6
6
  class ArgumentNotAssignedError < StandardError
7
+ ## Create a new instance of error
8
+ ## @param path [Flame::Path, String] path without argument
9
+ ## @param argument [Flame::Path::Part, String, Symbol]
10
+ ## not assigned argument
7
11
  def initialize(path, argument)
8
- @path = path
9
- @argument = argument
10
- end
11
-
12
- def message
13
- "Argument '#{@argument}' for path '#{@path}' is not assigned"
12
+ super(
13
+ "Argument '#{argument}' for path '#{path}' is not assigned"
14
+ )
14
15
  end
15
16
  end
16
17
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flame
4
+ module Errors
5
+ ## Error for not found config file in Config
6
+ class ConfigFileNotFoundError < StandardError
7
+ ## Create a new instance of error
8
+ ## @param file_name [String]
9
+ ## file name mask by which file was not found
10
+ ## @param directory [String] directory in which file was not found
11
+ def initialize(file_name, directory)
12
+ directory = directory.sub(%r{^/+}, '').sub(%r{/+$}, '')
13
+ super "Config file '#{file_name}' not found in '#{directory}/'"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flame
4
+ module Errors
5
+ ## Error for not found controller by name in namespace
6
+ class ControllerNotFoundError < StandardError
7
+ ## Create a new instance of error
8
+ ## @param controller_name [Symbol, String]
9
+ ## name of controller which not found
10
+ ## @param namespace [Module]
11
+ ## namespace for which controller not found
12
+ def initialize(controller_name, namespace)
13
+ super(
14
+ "Controller '#{controller_name}' not found for '#{namespace}'"
15
+ )
16
+ end
17
+ end
18
+ end
19
+ end
@@ -4,15 +4,16 @@ module Flame
4
4
  module Errors
5
5
  ## Error for Route initialization
6
6
  class RouteArgumentsOrderError < StandardError
7
+ ## Create a new instance of error
8
+ ## @param path [Flame::Path, String] path with wrong arguments order
9
+ ## @param wrong_ordered_arguments [Array<Symbol>]
10
+ ## two wrong ordered arguments
7
11
  def initialize(path, wrong_ordered_arguments)
8
- @path = path
9
- @wrong_ordered_arguments = wrong_ordered_arguments
10
- end
11
-
12
- def message
13
- "Path '#{@path}' should have" \
14
- " '#{@wrong_ordered_arguments.first}' argument before" \
15
- " '#{@wrong_ordered_arguments.last}'"
12
+ super(
13
+ "Path '#{path}' should have" \
14
+ " '#{wrong_ordered_arguments.first}' argument before" \
15
+ " '#{wrong_ordered_arguments.last}'"
16
+ )
16
17
  end
17
18
  end
18
19
  end
@@ -4,29 +4,29 @@ module Flame
4
4
  module Errors
5
5
  ## Error for Route initialization
6
6
  class RouteExtraArgumentsError < StandardError
7
+ ## Create a new instance of error
8
+ ## @param ctrl [Flame::Controller] controller
9
+ ## @param action [Symbol] action
10
+ ## @param path [Flame::Path, String] path
11
+ ## @param extra [Hash] extra arguments
12
+ ## @option extra [Symbol] :type required or optional
13
+ ## @option extra [Symbol] :place extra arguments in controller or path
14
+ ## @option extra [Array<Symbol>] :args extra arguments
7
15
  def initialize(ctrl, action, path, extra)
8
- @ctrl = ctrl
9
- @action = action
10
- @path = path
11
- @extra = extra
12
- @extra[:type_name] = {
13
- req: 'required',
14
- opt: 'optional'
15
- }[@extra[:type]]
16
- end
16
+ extra[:type_name] = { req: 'required', opt: 'optional' }[extra[:type]]
17
17
 
18
- def message
19
- case @extra[:place]
20
- when :ctrl
18
+ entity = {
21
19
  ## Error if path has no arguments, that controller's method has
22
20
  ## NOTE: It isn't using because `Flame::Path#adopt`
23
- "Path '#{@path}' has no #{@extra[:type_name]}" \
24
- " arguments #{@extra[:args].inspect}"
25
- when :path
21
+ ctrl: "Path '#{path}'",
26
22
  ## Error if path has more arguments, than controller's method
27
- "Action '#{@ctrl}##{@action}' has no #{@extra[:type_name]}" \
28
- " arguments #{@extra[:args].inspect}"
29
- end
23
+ path: "Action '#{ctrl}##{action}'"
24
+ }[extra[:place]]
25
+
26
+ super(
27
+ "#{entity} has no " \
28
+ "#{extra[:type_name]} arguments #{extra[:args].inspect}"
29
+ )
30
30
  end
31
31
  end
32
32
  end
@@ -4,14 +4,15 @@ module Flame
4
4
  module Errors
5
5
  ## Error for Flame::Dispatcher.path_to
6
6
  class RouteNotFoundError < StandardError
7
- def initialize(ctrl, method)
8
- @ctrl = ctrl
9
- @method = method
10
- end
11
-
12
- def message
13
- "Route with controller '#{@ctrl}' and method '#{@method}'" \
7
+ ## Create a new instance of error
8
+ ## @param controller [Flame::Controller]
9
+ ## controller with which route not found
10
+ ## @param action [Symbol] action with which route not found
11
+ def initialize(controller, action)
12
+ super(
13
+ "Route with controller '#{controller}' and action '#{action}'" \
14
14
  ' not found in application routes'
15
+ )
15
16
  end
16
17
  end
17
18
  end
@@ -4,14 +4,14 @@ module Flame
4
4
  module Errors
5
5
  ## Error for not found template file in Render
6
6
  class TemplateNotFoundError < StandardError
7
+ ## Create a new instance of error
8
+ ## @param controller [Flame::Controller]
9
+ ## controller from which template not found
10
+ ## @param path [String, Symbol] path of not founded template
7
11
  def initialize(controller, path)
8
- @controller = controller
9
- @controller = @controller.class unless @controller.is_a? Class
10
- @path = path
11
- end
12
+ controller = controller.class unless controller.is_a? Class
12
13
 
13
- def message
14
- "Template '#{@path}' not found for '#{@controller}'"
14
+ super "Template '#{path}' not found for '#{controller}'"
15
15
  end
16
16
  end
17
17
  end
data/lib/flame/path.rb CHANGED
@@ -1,26 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'forwardable'
4
+ require 'memery'
4
5
 
5
6
  require_relative 'errors/argument_not_assigned_error'
6
7
 
7
8
  module Flame
8
9
  ## Class for working with paths
9
10
  class Path
11
+ include Memery
12
+
13
+ extend Forwardable
14
+ def_delegators :to_s, :include?
15
+
16
+ ## Merge parts of path to one path
17
+ ## @param parts [Array<String, Flame::Path>] parts of expected path
18
+ ## @return [Flame::Path] path from parts
10
19
  def self.merge(*parts)
11
- parts.join('/').gsub(%r{\/{2,}}, '/')
20
+ parts.join('/').gsub(%r|/{2,}|, '/')
12
21
  end
13
22
 
23
+ ## Create a new instance
24
+ ## @param paths [String, Flame::Path, Array<String, Flame::Path>]
25
+ ## paths as parts for new path
14
26
  def initialize(*paths)
15
27
  @path = self.class.merge(*paths)
16
28
  freeze
17
29
  end
18
30
 
19
- def parts
20
- @parts ||= @path.to_s.split('/').reject(&:empty?)
21
- .map! { |part| PathPart.new(part) }
31
+ ## Return parts of path, splitted by slash (`/`)
32
+ ## @return [Array<Flame::Path::Part>] array of path parts
33
+ memoize def parts
34
+ @path.to_s.split('/').reject(&:empty?)
35
+ .map! { |part| self.class::Part.new(part) }
22
36
  end
23
37
 
38
+ ## Freeze all strings in object
24
39
  def freeze
25
40
  @path.freeze
26
41
  parts.each(&:freeze)
@@ -28,143 +43,214 @@ module Flame
28
43
  super
29
44
  end
30
45
 
46
+ ## Create new instance from self and other by concatinating
47
+ ## @param other [Flame::Path, String] other path which will be concatinated
48
+ ## @return [Flame::Path] result of concatinating
49
+ def +(other)
50
+ self.class.new(self, other)
51
+ end
52
+
31
53
  ## Compare by parts count and the first arg position
54
+ ## @param other [Flame::Path] other path
55
+ ## @return [-1, 0, 1] result of comparing
32
56
  def <=>(other)
33
57
  self_parts, other_parts = [self, other].map(&:parts)
34
- parts_size = self_parts.size <=> other_parts.size
35
- return parts_size unless parts_size.zero?
36
- self_parts.zip(other_parts)
37
- .reduce(0) do |result, (self_part, other_part)|
38
- break -1 if self_part.arg? && !other_part.arg?
39
- break 1 if other_part.arg? && !self_part.arg?
40
- result
41
- end
58
+ by_parts_size = self_parts.size <=> other_parts.size
59
+ return by_parts_size unless by_parts_size.zero?
60
+
61
+ compare_by_args_in_parts self_parts.zip(other_parts)
42
62
  end
43
63
 
64
+ ## Compare with other path by parts
65
+ ## @param other [Flame::Path, String] other path
66
+ ## @return [true, false] equal or not
44
67
  def ==(other)
45
68
  other = self.class.new(other) if other.is_a? String
46
69
  parts == other.parts
47
70
  end
48
71
 
49
72
  ## Complete path for the action of controller
73
+ ## @param ctrl [Flame::Controller] to which controller adapt
74
+ ## @param action [Symbol] to which action of controller adapt
75
+ ## @return [Flame::Path] adapted path
50
76
  ## @todo Add :arg:type support (:id:num, :name:str, etc.)
51
77
  def adapt(ctrl, action)
52
78
  parameters = ctrl.instance_method(action).parameters
53
79
  parameters.map! do |parameter|
54
80
  parameter_type, parameter_name = parameter
55
- path_part = PathPart.new parameter_name, arg: parameter_type
81
+ path_part = self.class::Part.new parameter_name, arg: parameter_type
56
82
  path_part unless parts.include? path_part
57
83
  end
58
84
  self.class.new @path.empty? ? "/#{action}" : self, *parameters.compact
59
85
  end
60
86
 
61
- ## Can recieve other as String
62
- def match?(other)
63
- other = self.class.new(other) if other.is_a? String
64
- return false unless other_contain_required_parts?(other)
65
- result = [self, other].map { |path| path.parts.size }.max.times do |i|
66
- break false unless compare_parts parts[i], other.parts[i]
67
- end
68
- result = true if result
69
- result
70
- end
71
-
72
87
  ## Extract arguments from other path with values at arguments
73
88
  ## @param other_path [Flame::Path] other path with values at arguments
89
+ ## @return [Hash{Symbol => String}] hash of arguments from two paths
74
90
  def extract_arguments(other_path)
75
- parts.each_with_index.with_object({}) do |(part, i), args|
76
- other_part = other_path.parts[i].to_s
77
- next args unless part.arg?
78
- break args if part.opt_arg? && other_part.empty?
79
- args[
80
- part[(part.opt_arg? ? 2 : 1)..-1].to_sym
81
- ] = URI.decode(other_part)
82
- end
91
+ Extractor.new(parts, other_path.parts).run
83
92
  end
84
93
 
85
- ## Assign arguments to path for `Controller.path_to`
94
+ ## Assign arguments to path for `Controller#path_to`
86
95
  ## @param args [Hash] arguments for assigning
87
96
  def assign_arguments(args = {})
88
97
  result_parts = parts.map { |part| assign_argument(part, args) }.compact
89
98
  self.class.merge result_parts.unshift(nil)
90
99
  end
91
100
 
101
+ ## @return [String] path as String
92
102
  def to_s
93
103
  @path
94
104
  end
95
105
  alias to_str to_s
96
106
 
97
- private
98
-
99
- def other_contain_required_parts?(other)
100
- other_parts = other.parts
101
- req_path_parts = parts.reject(&:opt_arg?)
102
- fixed_path_parts = parts.reject(&:arg?)
103
- (fixed_path_parts - other_parts).empty? &&
104
- other_parts.count >= req_path_parts.count
107
+ ## Path parts as keys of nested Hashes
108
+ ## @return [Array(Flame::Router::Routes, Flame::Router::Routes)]
109
+ ## whole Routes (parent) and the endpoint (most nested Routes)
110
+ def to_routes_with_endpoint
111
+ endpoint =
112
+ parts.reduce(result = Flame::Router::Routes.new) do |hash, part|
113
+ hash[part] ||= Flame::Router::Routes.new
114
+ end
115
+ [result, endpoint]
105
116
  end
106
117
 
107
- def compare_parts(part, other_part)
108
- return unless part
109
- return if other_part.nil? && !part.opt_arg?
110
- # p other_part, part
111
- return true if part.arg?
112
- return true if other_part == part
113
- end
118
+ private
114
119
 
115
120
  ## Helpers for `assign_arguments`
116
121
  def assign_argument(part, args = {})
117
122
  ## Not argument
118
123
  return part unless part.arg?
119
124
  ## Not required argument
120
- return args[part[2..-1].to_sym] if part.opt_arg?
125
+ return args.delete(part[2..-1].to_sym) if part.opt_arg?
126
+
121
127
  ## Required argument
122
- param = args[part[1..-1].to_sym]
128
+ param = args.delete(part[1..-1].to_sym)
123
129
  ## Required argument is nil
124
130
  error = Errors::ArgumentNotAssignedError.new(@path, part)
125
131
  raise error if param.nil?
132
+
126
133
  ## All is ok
127
134
  param
128
135
  end
129
136
 
137
+ def compare_by_args_in_parts(self_and_other_parts)
138
+ result = 0
139
+
140
+ self_and_other_parts.each do |self_part, other_part|
141
+ if self_part.arg?
142
+ break result = -1 unless other_part.arg?
143
+ elsif other_part.arg?
144
+ break result = 1
145
+ end
146
+ end
147
+
148
+ result
149
+ end
150
+
151
+ ## Class for extracting arguments from other path
152
+ class Extractor
153
+ def initialize(parts, other_parts)
154
+ @parts = parts
155
+ @other_parts = other_parts
156
+
157
+ @index = 0
158
+ @other_index = 0
159
+
160
+ @args = {}
161
+ end
162
+
163
+ def run
164
+ @parts.each do |part|
165
+ next static_part_found unless part.arg?
166
+
167
+ break if part.opt_arg? && @other_parts.count <= @other_index
168
+
169
+ @args[part.to_sym] = extract
170
+ @index += 1
171
+ end
172
+
173
+ @args
174
+ end
175
+
176
+ private
177
+
178
+ def static_part_found
179
+ @index += 1
180
+ @other_index += 1
181
+ end
182
+
183
+ def extract
184
+ other_part = @other_parts[@other_index]
185
+
186
+ return if @parts[@index.next] == other_part
187
+
188
+ @other_index += 1
189
+ URI.decode_www_form_component(other_part)
190
+ end
191
+ end
192
+
193
+ private_constant :Extractor
194
+
130
195
  ## Class for one part of Path
131
- class PathPart
196
+ class Part
132
197
  extend Forwardable
133
198
 
134
- def_delegators :@part, :[], :hash
199
+ def_delegators :to_s, :[], :hash, :size, :empty?, :b, :inspect
135
200
 
136
201
  ARG_CHAR = ':'
137
202
  ARG_CHAR_OPT = '?'
138
203
 
204
+ ## Create new instance from String
205
+ ## @param part [String] path part as String
206
+ ## @param arg [Boolean] is this part an argument
139
207
  def initialize(part, arg: false)
140
208
  @part = "#{ARG_CHAR if arg}#{ARG_CHAR_OPT if arg == :opt}#{part}"
209
+ freeze
141
210
  end
142
211
 
212
+ ## Freeze object
143
213
  def freeze
144
214
  @part.freeze
145
215
  super
146
216
  end
147
217
 
218
+ ## Compare with another
219
+ ## @param other [Flame::Path::Part] other path part
220
+ ## @return [true, false] equal or not
148
221
  def ==(other)
149
222
  to_s == other.to_s
150
223
  end
151
224
 
152
225
  alias eql? ==
153
226
 
227
+ ## Convert path part to String
228
+ ## @return [String] path part as String
154
229
  def to_s
155
230
  @part
156
231
  end
232
+ alias to_str to_s
157
233
 
234
+ ## Is the path part an argument
235
+ ## @return [true, false] an argument or not
158
236
  def arg?
159
237
  @part.start_with? ARG_CHAR
160
238
  end
161
239
 
240
+ ## Is the path part an optional argument
241
+ ## @return [true, false] an optional argument or not
162
242
  def opt_arg?
163
- @part[1] == ARG_CHAR_OPT
243
+ arg? && @part[1] == ARG_CHAR_OPT
164
244
  end
165
245
 
166
- def clean
167
- @part.delete ARG_CHAR + ARG_CHAR_OPT
246
+ # def req_arg?
247
+ # arg? && !opt_arg?
248
+ # end
249
+
250
+ ## Path part as a Symbol without arguments characters
251
+ ## @return [Symbol] clean Symbol
252
+ def to_sym
253
+ @part.delete(ARG_CHAR + ARG_CHAR_OPT).to_sym
168
254
  end
169
255
  end
170
256
  end