shaf 2.1.0 → 3.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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/lib/shaf/app.rb +27 -9
  4. data/lib/shaf/command/base.rb +4 -0
  5. data/lib/shaf/command/new.rb +5 -1
  6. data/lib/shaf/command/server.rb +5 -1
  7. data/lib/shaf/command/templates/config/settings.yml.erb +9 -8
  8. data/lib/shaf/extensions/resource_uris.rb +37 -155
  9. data/lib/shaf/extensions/symbolic_routes.rb +5 -18
  10. data/lib/shaf/formable/builder.rb +58 -19
  11. data/lib/shaf/formable/form.rb +13 -10
  12. data/lib/shaf/formable.rb +10 -23
  13. data/lib/shaf/generator/base.rb +82 -0
  14. data/lib/shaf/generator/controller.rb +19 -35
  15. data/lib/shaf/generator/forms.rb +10 -14
  16. data/lib/shaf/generator/migration/add_column.rb +0 -4
  17. data/lib/shaf/generator/migration/add_index.rb +0 -4
  18. data/lib/shaf/generator/migration/base.rb +8 -0
  19. data/lib/shaf/generator/migration/create_table.rb +0 -4
  20. data/lib/shaf/generator/migration/drop_column.rb +0 -4
  21. data/lib/shaf/generator/migration/rename_column.rb +0 -4
  22. data/lib/shaf/generator/model.rb +29 -14
  23. data/lib/shaf/generator/policy.rb +8 -14
  24. data/lib/shaf/generator/profile.rb +9 -14
  25. data/lib/shaf/generator/scaffold.rb +6 -9
  26. data/lib/shaf/generator/serializer.rb +31 -30
  27. data/lib/shaf/generator/templates/api/controller.rb.erb +13 -13
  28. data/lib/shaf/generator/templates/api/forms.rb.erb +2 -2
  29. data/lib/shaf/generator/templates/api/model.rb.erb +1 -1
  30. data/lib/shaf/generator/templates/api/profile.rb.erb +1 -1
  31. data/lib/shaf/generator/templates/api/serializer.rb.erb +1 -1
  32. data/lib/shaf/generator/templates/spec/integration_spec.rb.erb +14 -14
  33. data/lib/shaf/helpers/paginate.rb +1 -1
  34. data/lib/shaf/link_relations.rb +77 -0
  35. data/lib/shaf/profile.rb +8 -8
  36. data/lib/shaf/registrable_factory.rb +62 -32
  37. data/lib/shaf/responder/problem_json.rb +1 -1
  38. data/lib/shaf/router.rb +65 -12
  39. data/lib/shaf/spec/integration_spec.rb +1 -1
  40. data/lib/shaf/tasks.rb +0 -1
  41. data/lib/shaf/upgrade/package.rb +5 -7
  42. data/lib/shaf/utils.rb +2 -2
  43. data/lib/shaf/version.rb +1 -1
  44. data/lib/shaf/yard/link_object.rb +2 -3
  45. data/templates/Rakefile +0 -6
  46. data/templates/api/controllers/base_controller.rb +0 -2
  47. data/templates/api/serializers/root_serializer.rb +0 -11
  48. data/templates/config/initializers/middleware.rb +6 -0
  49. data/templates/spec/spec_helper.rb +4 -1
  50. data/upgrades/3.0.0.tar.gz +0 -0
  51. data/yard_templates/api_doc/profile_attribute/html/attribute.erb +2 -1
  52. data/yard_templates/api_doc/resource_attribute/html/attribute.erb +2 -1
  53. data/yard_templates/api_doc/sidebar/html/profile_list.erb +2 -1
  54. data/yard_templates/api_doc/sidebar/html/serializer_list.erb +2 -1
  55. data.tar.gz.sig +0 -0
  56. metadata +34 -36
  57. metadata.gz.sig +0 -0
  58. data/lib/shaf/api_doc/comment.rb +0 -27
  59. data/lib/shaf/api_doc/document.rb +0 -137
  60. data/lib/shaf/api_doc/link_relations.rb +0 -77
  61. data/lib/shaf/middleware.rb +0 -1
  62. data/lib/shaf/tasks/api_doc_task.rb +0 -146
@@ -11,19 +11,8 @@ module Shaf
11
11
  create_profile
12
12
  end
13
13
 
14
- def name
15
- n = args.first || ""
16
- return n unless n.empty?
17
- raise Command::ArgumentError,
18
- "Please provide a model name when using the serializer generator!"
19
- end
20
-
21
- def plural_name
22
- Utils.pluralize(name)
23
- end
24
-
25
- def model_class_name
26
- Utils.model_name(name)
14
+ def serializer_class_name
15
+ "#{model_class_name}Serializer"
27
16
  end
28
17
 
29
18
  def policy_class_name
@@ -38,21 +27,31 @@ module Shaf
38
27
  'spec/serializer_spec.rb'
39
28
  end
40
29
 
41
- def target
42
- "api/serializers/#{name}_serializer.rb"
30
+ def target_dir
31
+ 'api/serializers'
32
+ end
33
+
34
+ def target_name
35
+ "#{name}_serializer.rb"
43
36
  end
44
37
 
45
38
  def spec_target
46
- "spec/serializers/#{name}_serializer_spec.rb"
39
+ target(directory: 'spec/serializers', name: "#{name}_serializer_spec.rb")
40
+ end
41
+
42
+ def policy_file
43
+ File.join(['policies', namespace, "#{name}_policy"].compact)
47
44
  end
48
45
 
49
46
  def create_serializer
50
47
  content = render(template, opts)
48
+ content = wrap_in_module(content, module_name)
51
49
  write_output(target, content)
52
50
  end
53
51
 
54
52
  def create_serializer_spec
55
53
  content = render(spec_template, opts)
54
+ content = wrap_in_module(content, module_name, search: "describe #{serializer_class_name}")
56
55
  write_output(spec_target, content)
57
56
  end
58
57
 
@@ -74,16 +73,16 @@ module Shaf
74
73
 
75
74
  def profile_with_doc
76
75
  doc = <<~DOC
77
- # Adds a link to the '#{name}' profile and a curie. By default the
76
+ # Adds a link to the '#{resource_name}' profile and a curie. By default the
78
77
  # curie prefix is 'doc', use the `curie_prefix` keyword argument to
79
78
  # change this.
80
79
  # Note: the target of the profile link and the curie will be set to
81
- # `profile_uri('#{name}')` resp. `doc_curie_uri('#{name}')`. To
80
+ # `profile_uri('#{resource_name}')` resp. `doc_curie_uri('#{resource_name}')`. To
82
81
  # create links for external profiles or curies, delete the next line
83
82
  # and use `::link` and/or `::curie` instead.
84
83
  DOC
85
84
 
86
- doc.split("\n") << %Q(profile #{Utils.symbol_string(name)})
85
+ doc.split("\n") << %Q(profile #{Utils.symbol_string(resource_name)})
87
86
  end
88
87
 
89
88
  def links_with_doc
@@ -98,7 +97,7 @@ module Shaf
98
97
  def collection_link
99
98
  link(
100
99
  rel: "collection",
101
- uri_helper: "#{plural_name}_uri",
100
+ uri_helper: "#{collection_name}_uri",
102
101
  kwargs: {embed_depth: 0}
103
102
  )
104
103
  end
@@ -106,21 +105,21 @@ module Shaf
106
105
  def self_link
107
106
  link(
108
107
  rel: "self",
109
- uri_helper: "#{name}_uri(resource)"
108
+ uri_helper: "#{resource_name}_uri(resource)"
110
109
  )
111
110
  end
112
111
 
113
112
  def edit_link
114
113
  link(
115
114
  rel: "edit-form",
116
- uri_helper: "edit_#{name}_uri(resource)"
115
+ uri_helper: "edit_#{resource_name}_uri(resource)"
117
116
  )
118
117
  end
119
118
 
120
119
  def delete_link
121
120
  link(
122
121
  rel: "delete",
123
- uri_helper: "#{name}_uri(resource)",
122
+ uri_helper: "#{resource_name}_uri(resource)",
124
123
  kwargs: {curie: :doc}
125
124
  )
126
125
  end
@@ -128,7 +127,7 @@ module Shaf
128
127
  def create_link
129
128
  link(
130
129
  rel: "create-form",
131
- uri_helper: "new_#{name}_uri"
130
+ uri_helper: "new_#{resource_name}_uri"
132
131
  )
133
132
  end
134
133
 
@@ -147,9 +146,9 @@ module Shaf
147
146
  def collection_with_doc
148
147
  <<~EOS.split("\n")
149
148
  collection of: '#{plural_name}' do
150
- curie(:doc) { doc_curie_uri('#{name}') }
149
+ curie(:doc) { doc_curie_uri('#{resource_name}') }
151
150
 
152
- link :self, #{plural_name}_uri
151
+ link :self, #{collection_name}_uri
153
152
  link :up, root_uri
154
153
 
155
154
  #{create_link.join("\n ")}
@@ -160,10 +159,12 @@ module Shaf
160
159
  def opts
161
160
  {
162
161
  name: name,
163
- class_name: "#{model_class_name}Serializer",
162
+ resource_name: resource_name,
163
+ collection_name: collection_name,
164
+ class_name: serializer_class_name,
164
165
  model_class_name: model_class_name,
165
166
  policy_class_name: policy_class_name,
166
- policy_name: "#{name}_policy",
167
+ policy_file: policy_file,
167
168
  attribute_names: attribute_names,
168
169
  link_relations: link_relations,
169
170
  profile_with_doc: profile_with_doc,
@@ -174,12 +175,12 @@ module Shaf
174
175
  end
175
176
 
176
177
  def create_policy
177
- policy_args = ["policy", name, *attribute_names]
178
+ policy_args = ["policy", name_arg, *attribute_names]
178
179
  Generator::Factory.create(*policy_args, **options).call
179
180
  end
180
181
 
181
182
  def create_profile
182
- profile_args = ["profile", name, *attributes]
183
+ profile_args = ["profile", name_arg, *attributes]
183
184
  Generator::Factory.create(*profile_args, **options).call
184
185
  end
185
186
  end
@@ -4,45 +4,45 @@ class <%= controller_class_name %> < BaseController
4
4
 
5
5
  authorize_with <%= policy_class_name %>
6
6
 
7
- resource_uris_for :<%= name %>
7
+ resource_uris_for :<%= name %><%= namespace ? ", namespace: '#{namespace}'" : "" %>
8
8
 
9
- get :<%= plural_name %>_path do
9
+ get :<%= collection_name %>_path do
10
10
  authorize! :read
11
11
  collection = paginate(<%= model_class_name %>.order(:created_at).reverse)
12
12
  respond_with_collection collection, serializer: <%= serializer_class_name %>
13
13
  end
14
14
 
15
- get :new_<%= name %>_path do
15
+ get :new_<%= resource_name %>_path do
16
16
  authorize! :read
17
17
  cache_control(:private, http_cache_max_age: :short)
18
18
  respond_with create_form
19
19
  end
20
20
 
21
- post :<%= plural_name %>_path do
21
+ post :<%= collection_name %>_path do
22
22
  authorize! :write
23
23
  <%= name %> = <%= model_class_name %>.create(<%= name %>_params)
24
- headers('Location' => <%= name %>_uri(<%= name %>))
24
+ headers('Location' => <%= resource_name %>_uri(<%= name %>))
25
25
  respond_with <%= name %>, status: 201
26
26
  end
27
27
 
28
- get :<%= name %>_path do
28
+ get :<%= resource_name %>_path do
29
29
  authorize! :read
30
30
  respond_with <%= name %>
31
31
  end
32
32
 
33
- get :edit_<%= name %>_path do
33
+ get :edit_<%= resource_name %>_path do
34
34
  authorize! :write
35
35
  cache_control(:private, http_cache_max_age: :short)
36
36
  respond_with edit_form
37
37
  end
38
38
 
39
- put :<%= name %>_path do
39
+ put :<%= resource_name %>_path do
40
40
  authorize! :write
41
41
  <%= name %>.update(<%= name %>_params)
42
42
  respond_with <%= name %>
43
43
  end
44
44
 
45
- delete :<%= name %>_path do
45
+ delete :<%= resource_name %>_path do
46
46
  authorize! :write
47
47
  <%= name %>.destroy
48
48
  status 204
@@ -62,15 +62,15 @@ class <%= controller_class_name %> < BaseController
62
62
 
63
63
  def create_form
64
64
  <%= model_class_name %>.create_form.tap do |form|
65
- form.self_link = new_<%= name %>_uri
66
- form.href = <%= plural_name %>_uri
65
+ form.self_link = new_<%= resource_name %>_uri
66
+ form.href = <%= collection_name %>_uri
67
67
  end
68
68
  end
69
69
 
70
70
  def edit_form
71
71
  <%= name %>.edit_form.tap do |form|
72
- form.self_link = edit_<%= name %>_uri(<%= name %>)
73
- form.href = <%= name %>_uri(<%= name %>)
72
+ form.self_link = edit_<%= resource_name %>_uri(<%= name %>)
73
+ form.href = <%= resource_name %>_uri(<%= name %>)
74
74
  end
75
75
  end
76
76
  end
@@ -4,12 +4,12 @@ class <%= class_name %>
4
4
  forms_for(<%= model_class_name %>) do
5
5
  <%= fields.join("\n ") %>
6
6
 
7
- create do
7
+ create_form do
8
8
  title 'Create <%= model_class_name %>'
9
9
  name 'create-<%= model_name %>'
10
10
  end
11
11
 
12
- edit do
12
+ edit_form do
13
13
  instance_accessor
14
14
  title 'Update <%= model_class_name %>'
15
15
  name 'update-<%= model_name %>'
@@ -1,4 +1,4 @@
1
- class <%= class_name %> < Sequel::Model
1
+ class <%= class_name %> < Sequel::Model(:<%= table_name %>)
2
2
 
3
3
  end
4
4
 
@@ -1,6 +1,6 @@
1
1
  module Profiles
2
2
  class <%= profile_class_name %> < Shaf::Profile
3
- name '<%= profile_name %>'
3
+ name '<%= resource_name %>'
4
4
 
5
5
  doc 'FIXME: replace this with a description of the <%= profile_name %> profile'
6
6
 
@@ -1,5 +1,5 @@
1
1
  require 'serializers/base_serializer'
2
- require 'policies/<%= policy_name %>'
2
+ require '<%= policy_file %>'
3
3
 
4
4
  class <%= class_name %> < BaseSerializer
5
5
 
@@ -4,10 +4,10 @@ describe <%= model_class_name %>, type: :integration do
4
4
 
5
5
  it "returns a <%= name %>" do
6
6
  <%= name %> = <%= model_class_name %>.create
7
- get <%= name %>_uri(<%= name %>)
7
+ get <%= resource_name %>_uri(<%= name %>)
8
8
  _(status).must_equal 200
9
9
  _(link_rels).must_include(:self)
10
- _(links[:self][:href]).must_equal <%= name %>_uri(<%= name %>)
10
+ _(links[:self][:href]).must_equal <%= resource_name %>_uri(<%= name %>)
11
11
  <% params.each do |param| -%>
12
12
  _(attributes).must_include(:'<%= param[0] %>')
13
13
  <% end -%>
@@ -16,10 +16,10 @@ describe <%= model_class_name %>, type: :integration do
16
16
  it "lists all <%= plural_name %>" do
17
17
  2.times { <%= model_class_name %>.create }
18
18
 
19
- get <%= plural_name %>_uri
19
+ get <%= collection_name %>_uri
20
20
  _(status).must_equal 200
21
21
  _(link_rels).must_include(:self)
22
- _(links[:self][:href]).must_include <%= plural_name %>_uri
22
+ _(links[:self][:href]).must_include <%= collection_name %>_uri
23
23
  _(embedded(:'<%= plural_name %>').size).must_equal 2
24
24
 
25
25
  each_embedded :'<%= plural_name %>' do
@@ -31,12 +31,12 @@ describe <%= model_class_name %>, type: :integration do
31
31
  end
32
32
  <% if params.size > 0 %>
33
33
  it "can create <%= plural_name %>" do
34
- get <%= plural_name %>_uri
34
+ get <%= collection_name %>_uri
35
35
 
36
36
  _(link_rels).must_include(:'create-form')
37
37
  follow_rel :'create-form'
38
- _(links[:self][:href]).must_equal new_<%= name %>_uri
39
- _(attributes[:href]).must_equal <%= plural_name %>_uri
38
+ _(links[:self][:href]).must_equal new_<%= resource_name %>_uri
39
+ _(attributes[:href]).must_equal <%= collection_name %>_uri
40
40
  _(attributes[:method]).must_equal "POST"
41
41
  _(attributes[:name]).must_equal "create-<%= name %>"
42
42
  _(attributes[:title]).must_equal "Create <%= model_class_name %>"
@@ -49,9 +49,9 @@ describe <%= model_class_name %>, type: :integration do
49
49
  _(link_rels).must_include(:self)
50
50
  _(headers["Location"]).must_equal links[:self][:href]
51
51
 
52
- get <%= plural_name %>_uri
52
+ get <%= collection_name %>_uri
53
53
  _(status).must_equal 200
54
- _(links[:self][:href]).must_include <%= plural_name %>_uri
54
+ _(links[:self][:href]).must_include <%= collection_name %>_uri
55
55
  _(embedded(:'<%= plural_name %>').size).must_equal 1
56
56
 
57
57
  embedded :'<%= plural_name %>' do
@@ -68,14 +68,14 @@ describe <%= model_class_name %>, type: :integration do
68
68
 
69
69
  it "<%= plural_name %> can be updated" do
70
70
  <%= name %> = <%= model_class_name %>.create
71
- get <%= name %>_uri(<%= name %>)
71
+ get <%= resource_name %>_uri(<%= name %>)
72
72
  _(status).must_equal 200
73
73
 
74
74
  _(link_rels).must_include(:'edit-form')
75
75
  follow_rel :'edit-form'
76
76
 
77
- _(links[:self][:href]).must_equal edit_<%= name %>_uri(<%= name %>)
78
- _(attributes[:href]).must_equal <%= name %>_uri(<%= name %>)
77
+ _(links[:self][:href]).must_equal edit_<%= resource_name %>_uri(<%= name %>)
78
+ _(attributes[:href]).must_equal <%= resource_name %>_uri(<%= name %>)
79
79
  _(attributes[:method]).must_equal "PUT"
80
80
  _(attributes[:name]).must_equal "update-<%= name %>"
81
81
  _(attributes[:title]).must_equal "Update <%= model_class_name %>"
@@ -91,14 +91,14 @@ describe <%= model_class_name %>, type: :integration do
91
91
 
92
92
  it "<%= plural_name %> can be deleted" do
93
93
  <%= name %> = <%= model_class_name %>.create
94
- get <%= name %>_uri(<%= name %>)
94
+ get <%= resource_name %>_uri(<%= name %>)
95
95
  _(status).must_equal 200
96
96
  _(link_rels).must_include(:'doc:delete')
97
97
 
98
98
  follow_rel(:'doc:delete', method: :delete)
99
99
  _(status).must_equal 204
100
100
 
101
- get <%= name %>_uri(<%= name %>)
101
+ get <%= resource_name %>_uri(<%= name %>)
102
102
  _(status).must_equal 404
103
103
  end
104
104
 
@@ -12,7 +12,7 @@ module Shaf
12
12
  unless collection.respond_to? :paginate
13
13
  log.warn "Trying to paginate a collection that doesn't " \
14
14
  "support pagination: #{collection}"
15
- return
15
+ return collection
16
16
  end
17
17
 
18
18
  per_page = params[:per_page].to_i if params[:per_page]
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'tempfile'
5
+ require 'uri'
6
+ require 'csv'
7
+
8
+ module Shaf
9
+ class LinkRelations
10
+ class LinkRelation
11
+ attr_reader :name, :description, :reference, :notes
12
+
13
+ def initialize(name, description, reference, notes)
14
+ @name = name.to_sym
15
+ @description = description.freeze
16
+ @reference = reference.freeze
17
+ @notes = notes.freeze
18
+ end
19
+ end
20
+
21
+ class << self
22
+ IANA_URL = URI('https://www.iana.org/assignments/link-relations/link-relations-1.csv')
23
+
24
+ def all
25
+ relations.values
26
+ end
27
+
28
+ def get(key)
29
+ load_iana
30
+ relations[key.to_sym]
31
+ end
32
+
33
+ def add(link_relation)
34
+ relations[link_relation.name.to_sym] = link_relation
35
+ end
36
+
37
+ private
38
+
39
+ def load_iana
40
+ return if @loaded
41
+
42
+ iana_csv.each do |name, desc, ref, notes|
43
+ next if name == 'Relation Name'
44
+ add LinkRelation.new(name, desc, ref, notes)
45
+ end
46
+
47
+ @loaded = true
48
+ end
49
+
50
+ def relations
51
+ @relations ||= {}
52
+ end
53
+
54
+ def tmp_file_name
55
+ File.join(Dir.tmpdir, 'shaf_iana_link_relations')
56
+ end
57
+
58
+ def iana_csv
59
+ CSV.new(iana_links)
60
+ end
61
+
62
+ def iana_links
63
+ return File.read(tmp_file_name) if File.readable? tmp_file_name
64
+
65
+ response = Net::HTTP.get_response(IANA_URL)
66
+
67
+ if response.code.to_i == 200
68
+ response.body.tap do |content|
69
+ File.open(tmp_file_name, 'w') { |file| file.write(content) }
70
+ end
71
+ else
72
+ Utils.iana_link_relations
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
data/lib/shaf/profile.rb CHANGED
@@ -5,6 +5,14 @@ require 'shaf/extensions/resource_uris'
5
5
 
6
6
  module Shaf
7
7
  class Profile
8
+ module NormalizeName
9
+ private def normalize(name)
10
+ name.to_s.downcase.tr('-', '_')
11
+ end
12
+ end
13
+
14
+ extend NormalizeName
15
+ include NormalizeName
8
16
  include Shaf::UriHelper
9
17
 
10
18
  class << self
@@ -98,18 +106,10 @@ module Shaf
98
106
  def evaluator
99
107
  Evaluator.new(parent: self)
100
108
  end
101
-
102
- def normalize(name)
103
- name.to_s.downcase.tr('-', '_')
104
- end
105
109
  end
106
110
 
107
111
  def name
108
112
  normalize(self.class.name)
109
113
  end
110
-
111
- def normalize(str)
112
- self.class.normalize(str)
113
- end
114
114
  end
115
115
  end
@@ -1,15 +1,58 @@
1
1
  module Shaf
2
2
  module RegistrableFactory
3
-
4
3
  class NotFoundError < StandardError; end
4
+ class NoIdentifiersError < StandardError
5
+
6
+ def initialize(clazz)
7
+ super <<~ERR
8
+ #{clazz} does not have an @identifiers ivar.
9
+ Did you perhaps forget to call `#{clazz}.identifier`?
10
+ ERR
11
+ end
12
+ end
13
+
14
+ class Entry
15
+ attr_reader :clazz
16
+
17
+ def initialize(clazz)
18
+ @clazz = clazz
19
+ end
20
+
21
+ def match?(strings)
22
+ raise NoIdentifiersError, clazz unless identifiers
23
+ return false if strings.size < identifiers.size
24
+ identifiers.zip(strings).all? { |pattern, str| matching_identifier? str, pattern }
25
+ end
26
+
27
+ def identifier_count
28
+ identifiers&.size || 0
29
+ end
30
+
31
+ def usage
32
+ clazz.instance_variable_get(:@usage)
33
+ end
34
+
35
+ private
36
+
37
+ def identifiers
38
+ clazz.identified_by
39
+ end
40
+
41
+ def matching_identifier?(str, pattern)
42
+ return false if pattern.nil? || str.nil? || str.empty?
43
+ pattern = pattern.to_s if pattern.is_a? Symbol
44
+ return str == pattern if pattern.is_a? String
45
+ !!str.match(pattern)
46
+ end
47
+ end
5
48
 
6
49
  def all
7
- reg.dup
50
+ reg.map(&:clazz)
8
51
  end
9
52
 
10
- def each
53
+ def each(&block)
11
54
  return all.each unless block_given?
12
- all.each { |c| yield c }
55
+ all.each(&block)
13
56
  end
14
57
 
15
58
  def size
@@ -17,34 +60,31 @@ module Shaf
17
60
  end
18
61
 
19
62
  def register(clazz)
20
- reg << clazz
63
+ reg << Entry.new(clazz)
21
64
  end
22
65
 
23
66
  def unregister(*str)
24
67
  return if str.empty? || !str.all?
25
- reg.delete_if { |clazz| matching_class? str, clazz }
68
+ reg.delete_if { |entry| entry.match? str }
26
69
  end
27
70
 
28
71
  def lookup(*str)
29
- return if str.empty? || !str.all?
30
- reg.select { |clazz| matching_class? str, clazz }
31
- .sort_by(&method(:identifier_count))
32
- .last
72
+ lookup_entry(*str)&.clazz
33
73
  end
34
74
 
35
75
  def usage
36
76
  reg.compact.map do |entry|
37
- usage = entry.instance_variable_get(:@usage)
77
+ usage = entry.usage
38
78
  usage.respond_to?(:call) ? usage.call : usage
39
79
  end
40
80
  end
41
81
 
42
82
  def create(*params, **options)
43
- clazz = lookup(*params)
44
- raise NotFoundError.new(%Q(Command '#{ARGV}' is not supported)) unless clazz
83
+ entry = lookup_entry(*params)
84
+ raise NotFoundError.new(%Q(Command '#{ARGV}' is not supported)) unless entry
45
85
 
46
- args = init_args(clazz, params)
47
- clazz.new(*args, **options)
86
+ args = init_args(entry, params)
87
+ entry.clazz.new(*args, **options)
48
88
  end
49
89
 
50
90
  private
@@ -53,25 +93,15 @@ module Shaf
53
93
  @reg ||= []
54
94
  end
55
95
 
56
- def matching_class?(strings, clazz)
57
- identifiers = clazz.instance_variable_get(:@identifiers)
58
- return false if strings.size < identifiers.size
59
- identifiers.zip(strings).all? { |pattern, str| matching_identifier? str, pattern }
60
- end
61
-
62
- def matching_identifier?(str, pattern)
63
- return false if pattern.nil? || str.nil? || str.empty?
64
- pattern = pattern.to_s if pattern.is_a? Symbol
65
- return str == pattern if pattern.is_a? String
66
- str.match(pattern) || false
67
- end
68
-
69
- def identifier_count(clazz)
70
- clazz.instance_variable_get(:@identifiers)&.size || 0
96
+ def lookup_entry(*str)
97
+ return if str.empty? || !str.all?
98
+ reg.select { |entry| entry.match? str }
99
+ .sort_by { |entry| entry.identifier_count }
100
+ .last
71
101
  end
72
102
 
73
- def init_args(clazz, params)
74
- first_non_id = identifier_count(clazz)
103
+ def init_args(entry, params)
104
+ first_non_id = entry.identifier_count
75
105
  params[first_non_id..-1]
76
106
  end
77
107
  end
@@ -16,7 +16,7 @@ module Shaf
16
16
 
17
17
  def hash
18
18
  {
19
- status: controller.status,
19
+ status: status,
20
20
  type: code,
21
21
  title: title,
22
22
  detail: resource.message,