shaf 1.6.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/iana_link_relations.csv.gz +0 -0
- data/lib/shaf/alps/attribute_serializer.rb +41 -0
- data/lib/shaf/alps/json_serializer.rb +50 -0
- data/lib/shaf/alps/relation_serializer.rb +70 -0
- data/lib/shaf/app.rb +32 -7
- data/lib/shaf/authenticator/base.rb +161 -0
- data/lib/shaf/authenticator/basic_auth.rb +25 -0
- data/lib/shaf/authenticator/challenge.rb +32 -0
- data/lib/shaf/authenticator/parameter.rb +31 -0
- data/lib/shaf/authenticator/request.rb +17 -0
- data/lib/shaf/authenticator.rb +56 -0
- data/lib/shaf/command/base.rb +4 -0
- data/lib/shaf/command/console.rb +1 -1
- data/lib/shaf/command/generate.rb +5 -2
- data/lib/shaf/command/new.rb +24 -7
- data/lib/shaf/command/server.rb +5 -1
- data/lib/shaf/command/templates/Gemfile.erb +1 -0
- data/{templates/config/settings.yml → lib/shaf/command/templates/config/settings.yml.erb} +9 -12
- data/lib/shaf/errors.rb +11 -0
- data/lib/shaf/extensions/api_routes.rb +60 -0
- data/lib/shaf/extensions/authorize.rb +11 -9
- data/lib/shaf/extensions/log.rb +1 -1
- data/lib/shaf/extensions/resource_uris.rb +95 -137
- data/lib/shaf/extensions/symbolic_routes.rb +9 -19
- data/lib/shaf/extensions.rb +3 -3
- data/lib/shaf/formable/builder.rb +58 -19
- data/lib/shaf/formable/form.rb +14 -11
- data/lib/shaf/formable.rb +10 -24
- data/lib/shaf/generator/base.rb +84 -3
- data/lib/shaf/generator/controller.rb +30 -42
- data/lib/shaf/generator/doc.rb +17 -0
- data/lib/shaf/generator/forms.rb +11 -14
- data/lib/shaf/generator/helper.rb +2 -1
- data/lib/shaf/generator/migration/add_column.rb +0 -4
- data/lib/shaf/generator/migration/add_index.rb +0 -4
- data/lib/shaf/generator/migration/base.rb +15 -3
- data/lib/shaf/generator/migration/create_table.rb +0 -4
- data/lib/shaf/generator/migration/drop_column.rb +0 -4
- data/lib/shaf/generator/migration/rename_column.rb +0 -4
- data/lib/shaf/generator/migration/type.rb +4 -26
- data/lib/shaf/generator/migration/types.rb +45 -16
- data/lib/shaf/generator/model.rb +29 -15
- data/lib/shaf/generator/policy.rb +8 -14
- data/lib/shaf/generator/profile.rb +47 -0
- data/lib/shaf/generator/scaffold.rb +6 -9
- data/lib/shaf/generator/serializer.rb +64 -98
- data/lib/shaf/generator/templates/api/controller.rb.erb +13 -13
- data/lib/shaf/generator/templates/api/forms.rb.erb +2 -2
- data/lib/shaf/generator/templates/api/model.rb.erb +1 -1
- data/lib/shaf/generator/templates/api/policy.rb.erb +2 -2
- data/lib/shaf/generator/templates/api/profile.rb.erb +16 -0
- data/lib/shaf/generator/templates/api/serializer.rb.erb +3 -3
- data/lib/shaf/generator/templates/spec/integration_spec.rb.erb +15 -16
- data/lib/shaf/generator/templates/spec/serializer_spec.rb.erb +5 -5
- data/lib/shaf/generator.rb +2 -0
- data/lib/shaf/helpers/authentication.rb +79 -0
- data/lib/shaf/helpers/paginate.rb +1 -1
- data/lib/shaf/helpers/payload.rb +14 -34
- data/lib/shaf/helpers/vary.rb +8 -0
- data/lib/shaf/helpers.rb +4 -0
- data/lib/shaf/logger.rb +12 -0
- data/lib/shaf/parser/base.rb +44 -0
- data/lib/shaf/parser/form_data.rb +15 -0
- data/lib/shaf/parser/json.rb +26 -0
- data/lib/shaf/parser.rb +65 -0
- data/lib/shaf/profile/attribute.rb +29 -0
- data/lib/shaf/profile/evaluator.rb +46 -0
- data/lib/shaf/profile/relation.rb +29 -0
- data/lib/shaf/profile/unique_id.rb +58 -0
- data/lib/shaf/profile.rb +115 -0
- data/lib/shaf/profiles/shaf_basic.rb +20 -0
- data/lib/shaf/profiles/shaf_error.rb +49 -0
- data/lib/shaf/profiles/shaf_form.rb +110 -0
- data/lib/shaf/profiles.rb +46 -0
- data/lib/shaf/registrable_factory.rb +62 -32
- data/lib/shaf/responder/alps_json.rb +25 -0
- data/lib/shaf/responder/base.rb +20 -17
- data/lib/shaf/responder/hal.rb +66 -5
- data/lib/shaf/responder/html.rb +43 -16
- data/lib/shaf/responder/problem_json.rb +2 -2
- data/lib/shaf/responder.rb +41 -2
- data/lib/shaf/router.rb +65 -12
- data/lib/shaf/serializer.rb +35 -0
- data/lib/shaf/settings.rb +25 -12
- data/lib/shaf/spec/authenticator.rb +13 -0
- data/lib/shaf/spec/base.rb +1 -1
- data/lib/shaf/spec/http_method_utils.rb +1 -1
- data/lib/shaf/spec/integration_spec.rb +26 -14
- data/lib/shaf/spec/payload_utils.rb +2 -2
- data/lib/shaf/spec.rb +1 -0
- data/lib/shaf/supported_http_methods.rb +15 -0
- data/lib/shaf/tasks/routes_task.rb +14 -17
- data/lib/shaf/tasks.rb +0 -1
- data/lib/shaf/upgrade/manifest.rb +11 -2
- data/lib/shaf/upgrade/package.rb +73 -45
- data/lib/shaf/upgrade/version.rb +11 -10
- data/lib/shaf/utils.rb +19 -5
- data/lib/shaf/version.rb +3 -1
- data/lib/shaf/yard/attribute_method_handler.rb +19 -0
- data/lib/shaf/yard/attribute_object.rb +30 -0
- data/lib/shaf/yard/base_method_handler.rb +30 -0
- data/lib/shaf/yard/link_method_handler.rb +39 -0
- data/lib/shaf/yard/link_object.rb +60 -0
- data/lib/shaf/yard/nested_attributes.rb +37 -0
- data/lib/shaf/yard/parser.rb +64 -0
- data/lib/shaf/yard/profile_method_handler.rb +51 -0
- data/lib/shaf/yard/profile_object.rb +21 -0
- data/lib/shaf/yard/resource_object.rb +55 -0
- data/lib/shaf/yard/serializer_handler.rb +27 -0
- data/lib/shaf/yard.rb +34 -0
- data/lib/shaf.rb +6 -0
- data/templates/Rakefile +0 -6
- data/templates/api/controllers/base_controller.rb +0 -12
- data/templates/api/controllers/docs_controller.rb +5 -3
- data/templates/api/controllers/root_controller.rb +7 -1
- data/templates/api/policies/base_policy.rb +2 -0
- data/templates/api/serializers/base_serializer.rb +1 -3
- data/templates/api/serializers/error_serializer.rb +1 -5
- data/templates/api/serializers/form_serializer.rb +1 -5
- data/templates/api/serializers/root_serializer.rb +0 -11
- data/templates/api/serializers/validation_error_serializer.rb +1 -5
- data/templates/config/bootstrap.rb +1 -2
- data/templates/config/directories.rb +52 -44
- data/templates/config/helpers.rb +1 -1
- data/templates/config/initializers/authentication.rb +18 -0
- data/templates/config/initializers/db_migrations.rb +2 -2
- data/templates/config/initializers/logging.rb +2 -2
- data/templates/config/initializers/middleware.rb +6 -0
- data/templates/config/initializers.rb +52 -8
- data/templates/config.ru +1 -1
- data/templates/spec/spec_helper.rb +5 -0
- data/upgrades/0.5.0.tar.gz +0 -0
- data/upgrades/1.0.4.tar.gz +0 -0
- data/upgrades/1.1.0.tar.gz +0 -0
- data/upgrades/1.6.1.tar.gz +0 -0
- data/upgrades/2.0.0.tar.gz +0 -0
- data/upgrades/3.0.0.tar.gz +0 -0
- data/yard_templates/api_doc/doc_index/html/body.erb +3 -0
- data/yard_templates/api_doc/doc_index/setup.rb +8 -0
- data/yard_templates/api_doc/html/css/api-doc.css +222 -0
- data/yard_templates/api_doc/html/favicon.ico +0 -0
- data/yard_templates/api_doc/html/js/switch_tab.js +17 -0
- data/yard_templates/api_doc/html/setup.rb +59 -0
- data/yard_templates/api_doc/layout/html/footer.erb +3 -0
- data/yard_templates/api_doc/layout/html/header.erb +7 -0
- data/yard_templates/api_doc/layout/html/layout.erb +13 -0
- data/yard_templates/api_doc/layout/setup.rb +24 -0
- data/yard_templates/api_doc/profile/html/attributes.erb +10 -0
- data/yard_templates/api_doc/profile/html/profile.erb +6 -0
- data/yard_templates/api_doc/profile/html/relations.erb +10 -0
- data/yard_templates/api_doc/profile/setup.rb +38 -0
- data/yard_templates/api_doc/profile_attribute/html/attribute.erb +23 -0
- data/yard_templates/api_doc/profile_attribute/setup.rb +21 -0
- data/yard_templates/api_doc/profile_relation/html/relation.erb +37 -0
- data/yard_templates/api_doc/profile_relation/setup.rb +41 -0
- data/yard_templates/api_doc/resource/html/attributes.erb +10 -0
- data/yard_templates/api_doc/resource/html/profile.erb +14 -0
- data/yard_templates/api_doc/resource/html/relations.erb +10 -0
- data/yard_templates/api_doc/resource/html/resource.erb +5 -0
- data/yard_templates/api_doc/resource/setup.rb +56 -0
- data/yard_templates/api_doc/resource_attribute/html/attribute.erb +23 -0
- data/yard_templates/api_doc/resource_attribute/setup.rb +20 -0
- data/yard_templates/api_doc/resource_relation/html/relation.erb +47 -0
- data/yard_templates/api_doc/resource_relation/setup.rb +80 -0
- data/yard_templates/api_doc/setup.rb +31 -0
- data/yard_templates/api_doc/sidebar/html/profile_list.erb +8 -0
- data/yard_templates/api_doc/sidebar/html/search.erb +7 -0
- data/yard_templates/api_doc/sidebar/html/serializer_list.erb +8 -0
- data/yard_templates/api_doc/sidebar/html/sidebar.erb +13 -0
- data/yard_templates/api_doc/sidebar/setup.rb +56 -0
- data.tar.gz.sig +2 -2
- metadata +143 -38
- metadata.gz.sig +0 -0
- data/lib/shaf/api_doc/comment.rb +0 -27
- data/lib/shaf/api_doc/document.rb +0 -137
- data/lib/shaf/extensions/current_user.rb +0 -48
- data/lib/shaf/middleware.rb +0 -1
- data/lib/shaf/responder/hal_serializable.rb +0 -64
- data/lib/shaf/tasks/api_doc_task.rb +0 -125
@@ -1,27 +1,24 @@
|
|
1
1
|
---
|
2
2
|
default: &default
|
3
|
-
|
4
|
-
views_folder: frontend/views
|
3
|
+
default_authentication_realm: api
|
5
4
|
documents_dir: doc/api
|
6
|
-
migrations_dir: db/migrations
|
7
5
|
fixtures_dir: spec/fixtures
|
8
|
-
|
6
|
+
hostname: localhost
|
9
7
|
http_cache: on
|
10
8
|
http_cache_max_age_long: 86400 # 60 * 60 * 24 = 1 day
|
11
9
|
http_cache_max_age_short: 3600 # 60 * 60 = 1 hour
|
12
|
-
|
10
|
+
migrations_dir: db/migrations
|
11
|
+
paginate_per_page: 25
|
12
|
+
port: <%= default_port %>
|
13
|
+
project_name: <%= project_name %>
|
13
14
|
protocol: http
|
14
|
-
|
15
|
-
|
16
|
-
form_profile_name: shaf-form
|
17
|
-
form_profile_uri: https://gist.githubusercontent.com/sammyhenningsson/39c8aafeaf60192b082762cbf3e08d57/raw/shaf-form.md
|
18
|
-
error_profile_name: shaf-error
|
19
|
-
error_profile_uri: https://gist.githubusercontent.com/sammyhenningsson/049d10e2b8978059cde104fc5d6c2d52/raw/shaf-error.md
|
15
|
+
public_folder: frontend/assets
|
16
|
+
views_folder: frontend/views
|
20
17
|
|
21
18
|
production:
|
22
19
|
<<: *default
|
23
|
-
port: <%= ENV.fetch('PORT', 443) %>
|
24
20
|
base_uri: https://my.public.shaf.api.com
|
21
|
+
port: <%= ENV.fetch('PORT', 443) %>
|
25
22
|
|
26
23
|
development:
|
27
24
|
<<: *default
|
data/lib/shaf/errors.rb
CHANGED
@@ -64,6 +64,17 @@ module Shaf
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
+
class NotAcceptableError < ServerError
|
68
|
+
def http_status
|
69
|
+
406
|
70
|
+
end
|
71
|
+
|
72
|
+
def initialize(msg = nil)
|
73
|
+
msg ||= 'Resource found, but a suitable representation could not be generated'
|
74
|
+
super(msg, code: 'NOT_ACCEPTABLE', title: 'Content negotiation failed')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
67
78
|
class ConflictError < ServerError
|
68
79
|
def http_status
|
69
80
|
409
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Shaf
|
4
|
+
module ApiRoutes
|
5
|
+
class Registry
|
6
|
+
class << self
|
7
|
+
def register(controller, method, symbol)
|
8
|
+
routes[controller][symbol] << method.to_s.upcase
|
9
|
+
end
|
10
|
+
|
11
|
+
def controllers
|
12
|
+
routes.keys.sort_by(&:to_s)
|
13
|
+
end
|
14
|
+
|
15
|
+
def routes_for(controller)
|
16
|
+
sorted = routes[controller].keys.sort_by(&:to_s)
|
17
|
+
sorted.each do |symbol|
|
18
|
+
yield route_info(controller, symbol)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def routes
|
25
|
+
@routes ||= Hash.new do |hash, key|
|
26
|
+
# Group routes with conditionals together (`Set.new`). Like:
|
27
|
+
# get(:foobar_path, agent: /ios/) { "ios specific" }
|
28
|
+
# get(:foobar_path, agent: /android/) { "android specific" }
|
29
|
+
hash[key] = Hash.new { |h, k| h[k] = Set.new }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def route_info(controller, symbol)
|
34
|
+
methods = routes[controller][symbol].to_a
|
35
|
+
template_method = :"#{symbol}_template"
|
36
|
+
|
37
|
+
if controller.respond_to? template_method
|
38
|
+
template = controller.public_send(template_method)
|
39
|
+
else
|
40
|
+
template = symbol
|
41
|
+
symbol = '-'
|
42
|
+
end
|
43
|
+
|
44
|
+
[methods, template, symbol]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
Shaf::SUPPORTED_HTTP_METHODS.each do |method|
|
50
|
+
define_method method do |path, **options, &block|
|
51
|
+
path_str = path.to_s
|
52
|
+
path_str.sub!(/_uri/, '_path')
|
53
|
+
path_str = "#{path_str}_path" unless path_str.end_with? '_path'
|
54
|
+
path_str.sub!(/_path/, '_collection_path') if options[:collection]
|
55
|
+
Registry.register(self, method, path_str.to_sym)
|
56
|
+
super(path, **options, &block)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -8,22 +8,25 @@ module Shaf
|
|
8
8
|
|
9
9
|
attr_reader :policy_class
|
10
10
|
|
11
|
-
def authorize_with(policy_class)
|
12
|
-
@policy_class = policy_class
|
13
|
-
end
|
14
|
-
|
15
11
|
def self.registered(app)
|
16
12
|
app.helpers Helpers
|
17
13
|
end
|
14
|
+
|
15
|
+
def authorize_with(policy_class)
|
16
|
+
@policy_class = policy_class
|
17
|
+
end
|
18
18
|
end
|
19
19
|
|
20
20
|
module Helpers
|
21
21
|
def authorize(action, resource = nil)
|
22
|
-
policy(resource)
|
22
|
+
policy = policy(resource)
|
23
|
+
raise Authorize::NoPolicyError unless policy
|
24
|
+
|
23
25
|
method = __method_for(action)
|
24
|
-
return
|
26
|
+
return policy.public_send(method) if policy.respond_to? method
|
27
|
+
|
25
28
|
raise Authorize::MissingPolicyAction,
|
26
|
-
"#{
|
29
|
+
"#{policy.class} does not implement method #{method}"
|
27
30
|
end
|
28
31
|
|
29
32
|
def authorize!(action, resource = nil)
|
@@ -33,9 +36,8 @@ module Shaf
|
|
33
36
|
private
|
34
37
|
|
35
38
|
def policy(resource)
|
36
|
-
return @policy if defined?(@policy) && @policy
|
37
39
|
user = current_user if respond_to? :current_user
|
38
|
-
|
40
|
+
self.class.policy_class&.new(user, resource)
|
39
41
|
end
|
40
42
|
|
41
43
|
def __method_for(action)
|
data/lib/shaf/extensions/log.rb
CHANGED
@@ -11,15 +11,25 @@ module Shaf
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
+
class << self
|
15
|
+
def resource_uris_for(name, **kwargs)
|
16
|
+
CreateUriMethods.new(name, **kwargs).call
|
17
|
+
end
|
18
|
+
|
19
|
+
def register_uri(name, uri)
|
20
|
+
MethodBuilder.new(name, uri).call
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
14
24
|
def resource_uris_for(name, **kwargs)
|
15
|
-
result =
|
25
|
+
result = ResourceUris.resource_uris_for(name, **kwargs)
|
16
26
|
UriHelperMethods.add_path_helpers(self, result)
|
17
27
|
|
18
28
|
include UriHelper unless self < UriHelper
|
19
29
|
end
|
20
30
|
|
21
31
|
def register_uri(name, uri)
|
22
|
-
result =
|
32
|
+
result = ResourceUris.register_uri(name, uri)
|
23
33
|
UriHelperMethods.add_path_helpers(self, result)
|
24
34
|
|
25
35
|
include UriHelper unless self < UriHelper
|
@@ -39,23 +49,23 @@ module Shaf
|
|
39
49
|
end
|
40
50
|
|
41
51
|
def add_path_helpers(clazz, methods)
|
42
|
-
|
43
|
-
@path_helpers[clazz] ||= []
|
44
|
-
@path_helpers[clazz].concat Array(methods)
|
52
|
+
path_helpers[clazz].concat Array(methods)
|
45
53
|
end
|
46
54
|
|
47
55
|
def path_helpers_for(clazz = nil)
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
56
|
+
return path_helpers unless clazz
|
57
|
+
path_helpers[clazz]
|
58
|
+
end
|
59
|
+
|
60
|
+
def path_helpers
|
61
|
+
@path_helpers ||= Hash.new { |hash, key| hash[key] = [] }
|
52
62
|
end
|
53
63
|
|
54
64
|
# For cleaning up after tests
|
55
65
|
def remove_all
|
56
66
|
helpers = instance_methods - [:path_helpers]
|
57
67
|
remove_method(*helpers)
|
58
|
-
@path_helpers = {}
|
68
|
+
@path_helpers = Hash.new { |hash, key| hash[key] = [] }
|
59
69
|
end
|
60
70
|
end
|
61
71
|
|
@@ -96,22 +106,20 @@ module Shaf
|
|
96
106
|
# edit_book_uri_template => /books/:id/edit
|
97
107
|
#
|
98
108
|
class CreateUriMethods
|
99
|
-
def initialize(name, base: nil,
|
109
|
+
def initialize(name, plural_name: nil, base: nil, namespace: nil, only: nil, except: nil)
|
100
110
|
@name = name.to_s
|
101
|
-
@base = base&.sub(%r(/\Z), '') || ''
|
102
111
|
@plural_name = plural_name&.to_s || Utils::pluralize(name.to_s)
|
112
|
+
@base = base&.sub(%r(/\Z), '') || ''
|
113
|
+
@base = "/#{namespace}" if namespace && !base
|
114
|
+
@namespace = namespace
|
103
115
|
@only = only
|
104
116
|
@except = except
|
105
117
|
@added_path_methods = []
|
106
118
|
end
|
107
119
|
|
108
120
|
def call
|
109
|
-
|
110
|
-
|
111
|
-
else
|
112
|
-
register_collection_helper
|
113
|
-
register_resource_helper
|
114
|
-
end
|
121
|
+
register_collection_helper
|
122
|
+
register_resource_helper
|
115
123
|
register_new_resource_helper
|
116
124
|
register_edit_resource_helper
|
117
125
|
@added_path_methods
|
@@ -119,48 +127,38 @@ module Shaf
|
|
119
127
|
|
120
128
|
private
|
121
129
|
|
122
|
-
attr_reader :name, :base, :
|
130
|
+
attr_reader :name, :plural_name, :base, :namespace, :only, :except
|
123
131
|
|
124
132
|
def register_collection_helper
|
125
133
|
return if skip? :collection
|
126
134
|
|
135
|
+
method = method_name(plural_name, name == plural_name)
|
127
136
|
template_uri = "#{base}/#{plural_name}".freeze
|
128
|
-
register(
|
137
|
+
register(method, template_uri)
|
129
138
|
end
|
130
139
|
|
131
140
|
def register_resource_helper
|
132
141
|
return if skip? :resource
|
133
142
|
|
143
|
+
method = method_name(name)
|
134
144
|
template_uri = "#{base}/#{plural_name}/:id".freeze
|
135
|
-
register(
|
136
|
-
end
|
137
|
-
|
138
|
-
# If a resource has the same singular and plural names, then this method
|
139
|
-
# should be used. It will return the resource uri when a resource is given
|
140
|
-
# as argument and the resources uri when no arguments are provided.
|
141
|
-
def register_resource_helper_by_arg
|
142
|
-
return register_resource_helper if skip? :collection
|
143
|
-
return register_collection_helper if skip? :new
|
144
|
-
|
145
|
-
resource_template_uri = "#{base}/#{plural_name}/:id"
|
146
|
-
collection_template_uri = "#{base}/#{plural_name}"
|
147
|
-
|
148
|
-
builder = MethodBuilder.new(name, resource_template_uri, alt_uri: collection_template_uri)
|
149
|
-
@added_path_methods << builder.call
|
145
|
+
register(method, template_uri)
|
150
146
|
end
|
151
147
|
|
152
148
|
def register_new_resource_helper
|
153
149
|
return if skip? :new
|
154
150
|
|
151
|
+
method = method_name(name)
|
155
152
|
template_uri = "#{base}/#{name}/form".freeze
|
156
|
-
register("new_#{
|
153
|
+
register("new_#{method}", template_uri)
|
157
154
|
end
|
158
155
|
|
159
156
|
def register_edit_resource_helper
|
160
157
|
return if skip? :edit
|
161
158
|
|
159
|
+
method = method_name(name)
|
162
160
|
template_uri = "#{base}/#{plural_name}/:id/edit".freeze
|
163
|
-
register("edit_#{
|
161
|
+
register("edit_#{method}", template_uri)
|
164
162
|
end
|
165
163
|
|
166
164
|
def register(name, template_uri)
|
@@ -177,35 +175,44 @@ module Shaf
|
|
177
175
|
false
|
178
176
|
end
|
179
177
|
end
|
178
|
+
|
179
|
+
def method_name(name, collection = false)
|
180
|
+
collection_str = "collection" if collection
|
181
|
+
[namespace, name, collection_str].compact.join('_')
|
182
|
+
end
|
180
183
|
end
|
181
184
|
|
182
185
|
class MethodBuilder
|
183
186
|
def self.query_string(query)
|
184
|
-
return
|
185
|
-
|
187
|
+
return '' unless query&.any?
|
188
|
+
|
189
|
+
fragment_id = query.delete(:fragment_id)
|
190
|
+
fragment_str = "##{fragment_id}" if fragment_id
|
191
|
+
|
192
|
+
query_str = query.map { |a| a.join('=') }.join('&')
|
193
|
+
query_str = "?#{query_str}" unless query_str.empty?
|
194
|
+
|
195
|
+
[query_str, fragment_str].join
|
186
196
|
end
|
187
197
|
|
188
|
-
def initialize(name, uri
|
198
|
+
def initialize(name, uri)
|
189
199
|
@name = name
|
190
|
-
@uri = uri
|
191
|
-
@alt_uri = alt_uri
|
200
|
+
@uri = uri.dup.freeze
|
192
201
|
end
|
193
202
|
|
194
203
|
def call
|
195
204
|
if UriHelper.respond_to? uri_method_name
|
196
205
|
exception = ResourceUris::UriHelperMethodAlreadyExistError
|
197
|
-
raise exception.new(
|
206
|
+
raise exception.new(name, uri_method_name)
|
198
207
|
end
|
199
208
|
|
200
|
-
|
201
|
-
build_methods
|
202
|
-
else
|
203
|
-
build_methods_with_optional_arg
|
204
|
-
end
|
209
|
+
build_methods
|
205
210
|
end
|
206
211
|
|
207
212
|
private
|
208
213
|
|
214
|
+
attr_reader :name, :uri
|
215
|
+
|
209
216
|
def build_methods
|
210
217
|
UriHelperMethods.eval_method uri_method_string
|
211
218
|
UriHelperMethods.eval_method path_method_string
|
@@ -215,21 +222,12 @@ module Shaf
|
|
215
222
|
path_method_name.to_sym
|
216
223
|
end
|
217
224
|
|
218
|
-
def build_methods_with_optional_arg
|
219
|
-
UriHelperMethods.eval_method uri_method_with_optional_arg_string
|
220
|
-
UriHelperMethods.eval_method path_method_with_optional_arg_string
|
221
|
-
UriHelperMethods.register(template_method_name, &template_proc)
|
222
|
-
UriHelperMethods.register(legacy_template_method_name, &template_proc)
|
223
|
-
UriHelperMethods.register(path_matcher_name, &path_matcher_proc)
|
224
|
-
path_method_name.to_sym
|
225
|
-
end
|
226
|
-
|
227
225
|
def uri_method_name
|
228
|
-
"#{
|
226
|
+
"#{name}_uri".freeze
|
229
227
|
end
|
230
228
|
|
231
229
|
def path_method_name
|
232
|
-
"#{
|
230
|
+
"#{name}_path".freeze
|
233
231
|
end
|
234
232
|
|
235
233
|
def path_matcher_name
|
@@ -244,81 +242,45 @@ module Shaf
|
|
244
242
|
"#{uri_method_name}_template".freeze
|
245
243
|
end
|
246
244
|
|
247
|
-
def uri_signature(optional_args: 0)
|
248
|
-
signature(uri_method_name, optional_args: optional_args)
|
245
|
+
def uri_signature(uri: @uri, optional_args: 0)
|
246
|
+
signature(uri_method_name, uri, optional_args: optional_args)
|
249
247
|
end
|
250
248
|
|
251
|
-
def path_signature(optional_args: 0)
|
252
|
-
signature(path_method_name, optional_args: optional_args)
|
249
|
+
def path_signature(uri: @uri, optional_args: 0)
|
250
|
+
signature(path_method_name, uri, optional_args: optional_args)
|
253
251
|
end
|
254
252
|
|
255
|
-
def signature(method_name, optional_args: 0)
|
256
|
-
|
253
|
+
def signature(method_name, uri, optional_args: 0)
|
254
|
+
args = extract_symbols(uri).size.times.map { |i| "arg#{i}" }
|
255
|
+
sym_count = args.size
|
257
256
|
|
258
|
-
|
259
|
-
|
257
|
+
optional_args.times { |i| args << "arg#{sym_count + i} = nil" }
|
258
|
+
args << '**query'
|
260
259
|
|
261
|
-
args
|
262
|
-
symbols.each_with_index do |arg, i|
|
263
|
-
if i < sym_count - optional_args
|
264
|
-
args << "arg#{i}"
|
265
|
-
else
|
266
|
-
args << "arg#{i} = nil"
|
267
|
-
end
|
268
|
-
end
|
269
|
-
s << (args.empty? ? "**query)" : "#{args.join(', ')}, **query)")
|
260
|
+
"#{method_name}(#{args.join(', ')})"
|
270
261
|
end
|
271
262
|
|
272
263
|
def uri_method_string
|
273
264
|
base_uri = UriHelper.base_uri
|
274
|
-
<<~
|
265
|
+
<<~RUBY
|
275
266
|
def #{uri_signature}
|
276
267
|
query_str = Shaf::MethodBuilder.query_string(query)
|
277
|
-
\"#{base_uri}#{interpolated_uri_string(
|
268
|
+
\"#{base_uri}#{interpolated_uri_string(uri)}\#{query_str}\".freeze
|
278
269
|
end
|
279
|
-
|
270
|
+
RUBY
|
280
271
|
end
|
281
272
|
|
282
273
|
def path_method_string
|
283
|
-
<<~
|
274
|
+
<<~RUBY
|
284
275
|
def #{path_signature}
|
285
276
|
query_str = Shaf::MethodBuilder.query_string(query)
|
286
|
-
\"#{interpolated_uri_string(
|
277
|
+
\"#{interpolated_uri_string(uri)}\#{query_str}\".freeze
|
287
278
|
end
|
288
|
-
|
289
|
-
end
|
290
|
-
|
291
|
-
def uri_method_with_optional_arg_string
|
292
|
-
base_uri = UriHelper.base_uri
|
293
|
-
arg_no = extract_symbols.size - 1
|
294
|
-
<<~Ruby
|
295
|
-
def #{uri_signature(optional_args: 1)}
|
296
|
-
query_str = Shaf::MethodBuilder.query_string(query)
|
297
|
-
if arg#{arg_no}.nil?
|
298
|
-
\"#{base_uri}#{interpolated_uri_string(@alt_uri)}\#{query_str}\".freeze
|
299
|
-
else
|
300
|
-
\"#{base_uri}#{interpolated_uri_string(@uri)}\#{query_str}\".freeze
|
301
|
-
end
|
302
|
-
end
|
303
|
-
Ruby
|
304
|
-
end
|
305
|
-
|
306
|
-
def path_method_with_optional_arg_string
|
307
|
-
arg_no = extract_symbols.size - 1
|
308
|
-
<<~Ruby
|
309
|
-
def #{path_signature(optional_args: 1)}
|
310
|
-
query_str = Shaf::MethodBuilder.query_string(query)
|
311
|
-
if arg#{arg_no}.nil?
|
312
|
-
\"#{interpolated_uri_string(@alt_uri)}\#{query_str}\".freeze
|
313
|
-
else
|
314
|
-
\"#{interpolated_uri_string(@uri)}\#{query_str}\".freeze
|
315
|
-
end
|
316
|
-
end
|
317
|
-
Ruby
|
279
|
+
RUBY
|
318
280
|
end
|
319
281
|
|
320
282
|
def extract_symbols(uri = @uri)
|
321
|
-
uri.split('/').grep(
|
283
|
+
uri.split('/').grep(/\A:.+/).map { |t| t[1..-1].to_sym }
|
322
284
|
end
|
323
285
|
|
324
286
|
def transform_symbols(uri)
|
@@ -334,44 +296,40 @@ module Shaf
|
|
334
296
|
return uri if uri == '/'
|
335
297
|
|
336
298
|
transform_symbols(uri) do |segment, i|
|
337
|
-
|
338
|
-
|
299
|
+
# if the uri is templated (starting with a '{'), then we need to
|
300
|
+
# exclude it from the interpolated string but add it back to the end of
|
301
|
+
# the segment.
|
302
|
+
last = (segment.index('{') || 0) - 1
|
303
|
+
sym = segment[1..last]
|
304
|
+
template = segment[(last + 1)..-1] unless last == -1
|
305
|
+
"\#{arg#{i}.respond_to?(:#{sym}) ? arg#{i}.#{sym} : arg#{i}}#{template}"
|
339
306
|
end
|
340
307
|
end
|
341
308
|
|
342
309
|
def template_proc
|
343
|
-
uri
|
344
|
-
|
345
|
-
if alt_uri.nil?
|
346
|
-
->(_ = nil) { uri.freeze }
|
347
|
-
else
|
348
|
-
->(collection = false) { collection ? alt_uri : uri }
|
349
|
-
end
|
310
|
+
uri = @uri
|
311
|
+
-> { uri }
|
350
312
|
end
|
351
313
|
|
352
|
-
def
|
353
|
-
[
|
354
|
-
|
355
|
-
@alt_uri&.gsub(%r{:[^/]*}, '\w+')
|
356
|
-
].compact.map { |str| Regexp.new("\\A#{str}\\Z") }
|
314
|
+
def path_mather_pattern
|
315
|
+
pattern = uri.gsub(%r{:[^/]*}, '\w+')
|
316
|
+
Regexp.new("\\A#{pattern}\\Z")
|
357
317
|
end
|
358
318
|
|
359
319
|
def path_matcher_proc
|
360
|
-
|
320
|
+
pattern = path_mather_pattern
|
361
321
|
|
362
|
-
lambda do |path = nil
|
322
|
+
lambda do |path = nil|
|
363
323
|
unless path
|
364
324
|
r = request if respond_to? :request
|
365
|
-
path = r.path_info if r
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
)
|
372
|
-
end
|
325
|
+
path = r.path_info if r.respond_to? :path_info
|
326
|
+
|
327
|
+
raise(
|
328
|
+
ArgumentError,
|
329
|
+
"Uri must be given (or #{self} should respond to :request)"
|
330
|
+
) unless path
|
373
331
|
end
|
374
|
-
|
332
|
+
|
375
333
|
!!(pattern =~ path)
|
376
334
|
end
|
377
335
|
end
|
@@ -2,32 +2,22 @@ module Shaf
|
|
2
2
|
module SymbolicRoutes
|
3
3
|
class UriHelperNotRegisterdError < Error; end
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
:patch,
|
10
|
-
:delete,
|
11
|
-
:head,
|
12
|
-
:options,
|
13
|
-
:link,
|
14
|
-
:unlink
|
15
|
-
].freeze
|
16
|
-
|
17
|
-
SUPPORTED_METHODS.each do |m|
|
18
|
-
define_method m do |path, collection: false, &block|
|
19
|
-
super(rewrite_path(path, collection), &block)
|
5
|
+
Shaf::SUPPORTED_HTTP_METHODS.each do |m|
|
6
|
+
define_method m do |path, **options, &block|
|
7
|
+
path = rewrite_path(path, m)
|
8
|
+
super(path, **options, &block)
|
20
9
|
end
|
21
10
|
end
|
22
11
|
|
23
|
-
def rewrite_path(path,
|
12
|
+
def rewrite_path(path, method)
|
24
13
|
return path unless path.is_a? Symbol
|
25
14
|
|
26
|
-
|
27
|
-
|
15
|
+
["#{path}_template", "#{path}_path_template"].each do |method|
|
16
|
+
return send(method) if respond_to? method
|
17
|
+
end
|
28
18
|
|
29
19
|
raise UriHelperNotRegisterdError, <<~RUBY
|
30
|
-
Undefined method '#{
|
20
|
+
Undefined method '#{method}'. Did you forget to register a uri helper for #{path}?
|
31
21
|
RUBY
|
32
22
|
end
|
33
23
|
end
|
data/lib/shaf/extensions.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'shaf/extensions/log'
|
2
2
|
require 'shaf/extensions/resource_uris'
|
3
3
|
require 'shaf/extensions/controller_hooks'
|
4
|
-
require 'shaf/extensions/current_user'
|
5
4
|
require 'shaf/extensions/authorize'
|
6
5
|
require 'shaf/extensions/symbolic_routes'
|
6
|
+
require 'shaf/extensions/api_routes'
|
7
7
|
|
8
8
|
module Shaf
|
9
9
|
def self.extensions
|
@@ -11,9 +11,9 @@ module Shaf
|
|
11
11
|
Log,
|
12
12
|
ResourceUris,
|
13
13
|
ControllerHooks,
|
14
|
-
CurrentUser,
|
15
14
|
Authorize,
|
16
|
-
SymbolicRoutes
|
15
|
+
SymbolicRoutes,
|
16
|
+
ApiRoutes # This extension must be registered after `SymbolicRoutes`!
|
17
17
|
]
|
18
18
|
end
|
19
19
|
end
|