shaf 1.5.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/iana_link_relations.csv.gz +0 -0
  5. data/lib/shaf.rb +6 -0
  6. data/lib/shaf/alps/attribute_serializer.rb +41 -0
  7. data/lib/shaf/alps/json_serializer.rb +50 -0
  8. data/lib/shaf/alps/relation_serializer.rb +70 -0
  9. data/lib/shaf/api_doc/link_relations.rb +77 -0
  10. data/lib/shaf/app.rb +12 -5
  11. data/lib/shaf/authenticator.rb +56 -0
  12. data/lib/shaf/authenticator/base.rb +161 -0
  13. data/lib/shaf/authenticator/basic_auth.rb +25 -0
  14. data/lib/shaf/authenticator/challenge.rb +32 -0
  15. data/lib/shaf/authenticator/parameter.rb +31 -0
  16. data/lib/shaf/authenticator/request.rb +17 -0
  17. data/lib/shaf/command/console.rb +1 -1
  18. data/lib/shaf/command/generate.rb +5 -2
  19. data/lib/shaf/command/new.rb +20 -7
  20. data/lib/shaf/command/templates/Gemfile.erb +1 -0
  21. data/{templates/config/settings.yml → lib/shaf/command/templates/config/settings.yml.erb} +1 -5
  22. data/lib/shaf/errors.rb +11 -0
  23. data/lib/shaf/extensions.rb +3 -3
  24. data/lib/shaf/extensions/api_routes.rb +60 -0
  25. data/lib/shaf/extensions/authorize.rb +11 -9
  26. data/lib/shaf/extensions/log.rb +1 -1
  27. data/lib/shaf/extensions/resource_uris.rb +139 -63
  28. data/lib/shaf/extensions/symbolic_routes.rb +22 -19
  29. data/lib/shaf/formable.rb +1 -2
  30. data/lib/shaf/formable/form.rb +1 -1
  31. data/lib/shaf/generator.rb +2 -0
  32. data/lib/shaf/generator/base.rb +2 -3
  33. data/lib/shaf/generator/controller.rb +11 -7
  34. data/lib/shaf/generator/doc.rb +17 -0
  35. data/lib/shaf/generator/forms.rb +1 -0
  36. data/lib/shaf/generator/helper.rb +2 -1
  37. data/lib/shaf/generator/migration/base.rb +7 -3
  38. data/lib/shaf/generator/migration/type.rb +4 -26
  39. data/lib/shaf/generator/migration/types.rb +45 -16
  40. data/lib/shaf/generator/model.rb +1 -2
  41. data/lib/shaf/generator/profile.rb +52 -0
  42. data/lib/shaf/generator/serializer.rb +38 -73
  43. data/lib/shaf/generator/templates/api/policy.rb.erb +2 -2
  44. data/lib/shaf/generator/templates/api/profile.rb.erb +16 -0
  45. data/lib/shaf/generator/templates/api/serializer.rb.erb +2 -2
  46. data/lib/shaf/generator/templates/spec/integration_spec.rb.erb +1 -2
  47. data/lib/shaf/generator/templates/spec/serializer_spec.rb.erb +5 -5
  48. data/lib/shaf/helpers.rb +4 -0
  49. data/lib/shaf/helpers/authentication.rb +79 -0
  50. data/lib/shaf/helpers/cache_control.rb +1 -2
  51. data/lib/shaf/helpers/json_html.rb +58 -18
  52. data/lib/shaf/helpers/payload.rb +27 -41
  53. data/lib/shaf/helpers/vary.rb +8 -0
  54. data/lib/shaf/logger.rb +12 -0
  55. data/lib/shaf/parser.rb +65 -0
  56. data/lib/shaf/parser/base.rb +44 -0
  57. data/lib/shaf/parser/form_data.rb +15 -0
  58. data/lib/shaf/parser/json.rb +26 -0
  59. data/lib/shaf/profile.rb +110 -0
  60. data/lib/shaf/profile/attribute.rb +29 -0
  61. data/lib/shaf/profile/evaluator.rb +46 -0
  62. data/lib/shaf/profile/relation.rb +29 -0
  63. data/lib/shaf/profile/unique_id.rb +58 -0
  64. data/lib/shaf/profiles.rb +42 -0
  65. data/lib/shaf/profiles/shaf_basic.rb +20 -0
  66. data/lib/shaf/profiles/shaf_error.rb +48 -0
  67. data/lib/shaf/profiles/shaf_form.rb +109 -0
  68. data/lib/shaf/responder.rb +41 -2
  69. data/lib/shaf/responder/alps_json.rb +25 -0
  70. data/lib/shaf/responder/base.rb +20 -17
  71. data/lib/shaf/responder/hal.rb +62 -7
  72. data/lib/shaf/responder/html.rb +65 -9
  73. data/lib/shaf/responder/problem_json.rb +1 -1
  74. data/lib/shaf/serializer.rb +31 -0
  75. data/lib/shaf/settings.rb +25 -12
  76. data/lib/shaf/spec.rb +1 -0
  77. data/lib/shaf/spec/authenticator.rb +13 -0
  78. data/lib/shaf/spec/base.rb +1 -1
  79. data/lib/shaf/spec/http_method_utils.rb +1 -1
  80. data/lib/shaf/spec/integration_spec.rb +25 -13
  81. data/lib/shaf/spec/payload_utils.rb +2 -2
  82. data/lib/shaf/supported_http_methods.rb +15 -0
  83. data/lib/shaf/tasks/api_doc_task.rb +24 -3
  84. data/lib/shaf/tasks/routes_task.rb +14 -17
  85. data/lib/shaf/upgrade/manifest.rb +11 -2
  86. data/lib/shaf/upgrade/package.rb +78 -49
  87. data/lib/shaf/upgrade/version.rb +11 -10
  88. data/lib/shaf/utils.rb +19 -5
  89. data/lib/shaf/version.rb +3 -1
  90. data/lib/shaf/yard.rb +34 -0
  91. data/lib/shaf/yard/attribute_method_handler.rb +19 -0
  92. data/lib/shaf/yard/attribute_object.rb +30 -0
  93. data/lib/shaf/yard/base_method_handler.rb +30 -0
  94. data/lib/shaf/yard/link_method_handler.rb +39 -0
  95. data/lib/shaf/yard/link_object.rb +60 -0
  96. data/lib/shaf/yard/nested_attributes.rb +37 -0
  97. data/lib/shaf/yard/parser.rb +64 -0
  98. data/lib/shaf/yard/profile_method_handler.rb +51 -0
  99. data/lib/shaf/yard/profile_object.rb +21 -0
  100. data/lib/shaf/yard/resource_object.rb +55 -0
  101. data/lib/shaf/yard/serializer_handler.rb +27 -0
  102. data/templates/api/controllers/base_controller.rb +0 -10
  103. data/templates/api/controllers/docs_controller.rb +5 -3
  104. data/templates/api/controllers/root_controller.rb +7 -1
  105. data/templates/api/policies/base_policy.rb +2 -0
  106. data/templates/api/serializers/base_serializer.rb +1 -3
  107. data/templates/api/serializers/error_serializer.rb +1 -5
  108. data/templates/api/serializers/form_serializer.rb +1 -5
  109. data/templates/api/serializers/validation_error_serializer.rb +1 -5
  110. data/templates/config.ru +1 -1
  111. data/templates/config/bootstrap.rb +1 -2
  112. data/templates/config/directories.rb +52 -44
  113. data/templates/config/helpers.rb +1 -1
  114. data/templates/config/initializers.rb +52 -8
  115. data/templates/config/initializers/authentication.rb +18 -0
  116. data/templates/config/initializers/db_migrations.rb +2 -2
  117. data/templates/config/initializers/logging.rb +2 -2
  118. data/templates/frontend/assets/css/main.css +33 -1
  119. data/templates/frontend/views/headers.erb +20 -0
  120. data/templates/frontend/views/layout.erb +7 -1
  121. data/templates/frontend/views/payload.erb +1 -0
  122. data/templates/spec/spec_helper.rb +2 -0
  123. data/upgrades/0.5.0.tar.gz +0 -0
  124. data/upgrades/1.0.4.tar.gz +0 -0
  125. data/upgrades/1.1.0.tar.gz +0 -0
  126. data/upgrades/1.6.0.tar.gz +0 -0
  127. data/upgrades/1.6.1.tar.gz +0 -0
  128. data/upgrades/2.0.0.tar.gz +0 -0
  129. data/yard_templates/api_doc/doc_index/html/body.erb +3 -0
  130. data/yard_templates/api_doc/doc_index/setup.rb +8 -0
  131. data/yard_templates/api_doc/html/css/api-doc.css +222 -0
  132. data/yard_templates/api_doc/html/favicon.ico +0 -0
  133. data/yard_templates/api_doc/html/js/switch_tab.js +17 -0
  134. data/yard_templates/api_doc/html/setup.rb +59 -0
  135. data/yard_templates/api_doc/layout/html/footer.erb +3 -0
  136. data/yard_templates/api_doc/layout/html/header.erb +7 -0
  137. data/yard_templates/api_doc/layout/html/layout.erb +13 -0
  138. data/yard_templates/api_doc/layout/setup.rb +24 -0
  139. data/yard_templates/api_doc/profile/html/attributes.erb +10 -0
  140. data/yard_templates/api_doc/profile/html/profile.erb +6 -0
  141. data/yard_templates/api_doc/profile/html/relations.erb +10 -0
  142. data/yard_templates/api_doc/profile/setup.rb +38 -0
  143. data/yard_templates/api_doc/profile_attribute/html/attribute.erb +23 -0
  144. data/yard_templates/api_doc/profile_attribute/setup.rb +21 -0
  145. data/yard_templates/api_doc/profile_relation/html/relation.erb +37 -0
  146. data/yard_templates/api_doc/profile_relation/setup.rb +41 -0
  147. data/yard_templates/api_doc/resource/html/attributes.erb +10 -0
  148. data/yard_templates/api_doc/resource/html/profile.erb +14 -0
  149. data/yard_templates/api_doc/resource/html/relations.erb +10 -0
  150. data/yard_templates/api_doc/resource/html/resource.erb +5 -0
  151. data/yard_templates/api_doc/resource/setup.rb +56 -0
  152. data/yard_templates/api_doc/resource_attribute/html/attribute.erb +23 -0
  153. data/yard_templates/api_doc/resource_attribute/setup.rb +20 -0
  154. data/yard_templates/api_doc/resource_relation/html/relation.erb +47 -0
  155. data/yard_templates/api_doc/resource_relation/setup.rb +80 -0
  156. data/yard_templates/api_doc/setup.rb +31 -0
  157. data/yard_templates/api_doc/sidebar/html/profile_list.erb +8 -0
  158. data/yard_templates/api_doc/sidebar/html/search.erb +7 -0
  159. data/yard_templates/api_doc/sidebar/html/serializer_list.erb +8 -0
  160. data/yard_templates/api_doc/sidebar/html/sidebar.erb +13 -0
  161. data/yard_templates/api_doc/sidebar/setup.rb +56 -0
  162. metadata +140 -30
  163. metadata.gz.sig +1 -2
  164. data/lib/shaf/extensions/current_user.rb +0 -48
  165. data/lib/shaf/responder/hal_serializable.rb +0 -54
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ include Shaf::Yard::NestedAttributes
4
+
5
+ def init
6
+ super
7
+
8
+ @attribute = object
9
+ @nested_attributes = nested_attributes_for(object)
10
+
11
+ sections %i[attribute]
12
+ end
13
+
14
+ def value_types
15
+ Array(@attribute&.type).compact.map do |type|
16
+ {
17
+ type: type.to_s,
18
+ class_name: 'profile'
19
+ }
20
+ end
21
+ end
@@ -0,0 +1,37 @@
1
+ <h4 class=description-list>
2
+ <strong> <%= object.name %></strong>
3
+ </h4>
4
+ <p><%= object.doc %></p>
5
+
6
+ <% if href %>
7
+ <p>Reference: <a href="<%= href %>"><%= href %></a></p>
8
+ <% end %>
9
+
10
+ <% unless http_methods.empty? %>
11
+ <p>Supported HTTP Methods: <% http_methods.each do |method| %>
12
+ <span class="<%= css_classes_for(http_method: method) %>"><%= method %></span>
13
+ <%= %>
14
+ <% end %>
15
+ </p>
16
+ <% end %>
17
+
18
+ <% unless @nested_attributes.empty? %>
19
+ <div class="nested-attributes">
20
+ <h5>Nested attributes</h5>
21
+ <ul>
22
+ <% @nested_attributes.each do |key, attr| %>
23
+ <li>
24
+ <h6>
25
+ <strong> <%= key %></strong>
26
+ <span class="property-source profile"><%= attr.type %></span>
27
+ </h6>
28
+ <p><%= attr.doc %></p>
29
+ </li>
30
+ <% end %>
31
+ </ul>
32
+ </div>
33
+ <% end %>
34
+
35
+ <% if content_type %>
36
+ <p>Response type: <%= content_type %></p>
37
+ <% end %>
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ extend Forwardable
6
+ include Shaf::Yard::NestedAttributes
7
+
8
+ def_delegators :object, :name, :http_methods, :href, :content_type
9
+
10
+ HTTP_METHODS = %w(head options get put patch post delete).freeze
11
+
12
+ def init
13
+ super
14
+
15
+ @relation = object
16
+ @nested_attributes = nested_attributes_for(object)
17
+
18
+ sections %i[relation]
19
+ end
20
+
21
+ def value_types
22
+ Array(@attribute&.type).compact.map do |type|
23
+ {
24
+ type: type.to_s,
25
+ class_name: 'profile'
26
+ }
27
+ end
28
+ end
29
+
30
+ def css_classes_for(**options)
31
+ classes = []
32
+ classes.concat http_method_classes(options[:http_method]) if options.key? :http_method
33
+ classes.join(' ' )
34
+ end
35
+
36
+ def http_method_classes(method)
37
+ method = method.to_s.downcase
38
+ css_classes = ['http-method']
39
+ css_classes << method if HTTP_METHODS.include? method
40
+ css_classes
41
+ end
@@ -0,0 +1,10 @@
1
+ <div class="resource-section attributes">
2
+ <h3>Attributes</h3>
3
+ <ul>
4
+ <% @attributes.each do |attr| %>
5
+ <li class="description-item">
6
+ <%= serialize_attribute(attr) %>
7
+ </li>
8
+ <% end %>
9
+ </ul>
10
+ </div>
@@ -0,0 +1,14 @@
1
+ <div class="resource-section media-type-profile">
2
+ <h3>Media type profile</h3>
3
+ <p>
4
+ <% profile_links.each do |profile_link| %>
5
+ <span class="property-source <%= profile_link[:css_classes] %>">
6
+ <% if profile_link[:href] %>
7
+ <a href="<%= profile_link[:href] %>"><%= profile_link[:name] %></a>
8
+ <% else %>
9
+ <span><%= profile_link[:name] %></span>
10
+ <% end %>
11
+ </span>
12
+ <% end %>
13
+ </p>
14
+ </div>
@@ -0,0 +1,10 @@
1
+ <div class="resource-section relations">
2
+ <h3>Link relations</h3>
3
+ <ul>
4
+ <% @relations.each do |rel| %>
5
+ <li class="description-item">
6
+ <%= serialize_relation(rel) %>
7
+ </li>
8
+ <% end %>
9
+ </ul>
10
+ </div>
@@ -0,0 +1,5 @@
1
+ <div class="resource">
2
+ <h2>The <span class="resource-title"><%= name %></span> resource</h2>
3
+ <p><%= description %>
4
+ <%= yieldall %>
5
+ </div>
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ def init
4
+ super
5
+
6
+ @attributes = object.attributes
7
+ @relations = object.links
8
+
9
+ sections :resource, %i[profile attributes relations]
10
+ end
11
+
12
+ def serialize_attribute(attr)
13
+ Templates::Engine.render(
14
+ template: :api_doc,
15
+ type: :resource_attribute,
16
+ object: attr,
17
+ format: options.format
18
+ )
19
+ end
20
+
21
+ def serialize_relation(rel)
22
+ Templates::Engine.render(
23
+ template: :api_doc,
24
+ type: :resource_relation,
25
+ object: rel,
26
+ format: options.format
27
+ )
28
+ end
29
+
30
+ def name
31
+ object.resource_name
32
+ end
33
+
34
+ def description
35
+ object.description
36
+ end
37
+
38
+ def profile_links
39
+ object.profile_objects.map do |profile_object|
40
+
41
+ if profile_object.profile
42
+ path = profile_object.path.sub(/Profiles::/, '')
43
+ href = "/api_doc/#{path}.html"
44
+ css_classes = 'profile'
45
+ else
46
+ href = nil
47
+ css_classes = 'unknown'
48
+ end
49
+
50
+ {
51
+ name: profile_object.profile_name,
52
+ href: href,
53
+ css_classes: css_classes
54
+ }
55
+ end
56
+ end
@@ -0,0 +1,23 @@
1
+ <h4 class=description-list>
2
+ <strong> <%= object.name %></strong>
3
+ <% value_types.each do |type:, class_name:| %>
4
+ <span class="property-source <%= class_name %>"><%= type %></span>
5
+ <% end %>
6
+ </h4>
7
+ <p><%= object.documentation %></p>
8
+ <% unless @nested_attributes.empty? %>
9
+ <div class="nested-attributes">
10
+ <h5>Nested attributes</h5>
11
+ <ul>
12
+ <% @nested_attributes.each do |key, attr| %>
13
+ <li>
14
+ <h6>
15
+ <strong> <%= key %></strong>
16
+ <span class="property-source profile"><%= attr.type %></span>
17
+ </h6>
18
+ <p><%= attr.doc %></p>
19
+ </li>
20
+ <% end %>
21
+ </ul>
22
+ </div>
23
+ <% end %>
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ include Shaf::Yard::NestedAttributes
4
+
5
+ def init
6
+ super
7
+
8
+ @attribute = object
9
+ @nested_attributes = nested_attributes_for(object.descriptor)
10
+
11
+ sections %i[attribute] end
12
+
13
+ def value_types
14
+ object.value_types.map do |type|
15
+ {
16
+ type: type,
17
+ class_name: 'profile'
18
+ }
19
+ end
20
+ end
@@ -0,0 +1,47 @@
1
+ <h4 class=description-list>
2
+ <strong> <%= object.name %></strong>
3
+ </h4>
4
+ <p><%= object.documentation %></p>
5
+
6
+ <p>Source:
7
+ <% if source.href %>
8
+ <span class="property-source <%= source.type %>">
9
+ <a href="<%= source.href %>"><%= source.name %></a>
10
+ </span>
11
+ <% else %>
12
+ <span class="property-source <%= source.type %>"><%= source.name %></span>
13
+ <% end %>
14
+ </p>
15
+
16
+ <% if href %>
17
+ <p>Reference: <a href="<%= href %>"><%= href %></a></p>
18
+ <% end %>
19
+
20
+ <% unless http_methods.empty? %>
21
+ <p>Supported HTTP Methods: <% http_methods.each do |method| %>
22
+ <span class="<%= css_classes_for(http_method: method) %>"><%= method %></span>
23
+ <%= %>
24
+ <% end %>
25
+ </p>
26
+ <% end %>
27
+
28
+ <% unless @nested_attributes.empty? %>
29
+ <div class="nested-attributes">
30
+ <h5>Nested attributes</h5>
31
+ <ul>
32
+ <% @nested_attributes.each do |key, attr| %>
33
+ <li>
34
+ <h6>
35
+ <strong> <%= key %></strong>
36
+ <span class="property-source profile"><%= attr.type %></span>
37
+ </h6>
38
+ <p><%= attr.doc %></p>
39
+ </li>
40
+ <% end %>
41
+ </ul>
42
+ </div>
43
+ <% end %>
44
+
45
+ <% if content_type %>
46
+ <p>Response type: <%= content_type %></p>
47
+ <% end %>
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ extend Forwardable
6
+ include Shaf::Yard::NestedAttributes
7
+
8
+ def_delegators :object, :name, :http_methods, :href, :content_type
9
+
10
+ HTTP_METHODS = %w(head options get put patch post delete).freeze
11
+ IANA_HREF = 'https://www.iana.org/assignments/link-relations/link-relations.xhtml'
12
+
13
+ class Source
14
+ attr_reader :name, :href, :type
15
+
16
+ def initialize(name:, href:, type:)
17
+ @name, @href, @type = name, href, type
18
+ end
19
+ end
20
+
21
+ def init
22
+ super
23
+
24
+ @nested_attributes = nested_attributes_for(object.descriptor)
25
+
26
+ sections %i[relation]
27
+ end
28
+
29
+ def source
30
+ return @source if defined? @source
31
+
32
+ # We currently only support one profile per resource.
33
+ # But this might change in the future.
34
+ profile_objects = [object.profile_object].compact
35
+ profile_objects.each do |profile_object|
36
+ next unless profile_object.profile.find_relation(name)
37
+
38
+ path = profile_object.path.sub(/Profiles::/, '')
39
+
40
+ @source = Source.new(
41
+ name: profile_object.name,
42
+ href: "/api_doc/#{path}.html",
43
+ type: :profile
44
+ )
45
+ break
46
+ end
47
+
48
+ @source ||= iana_source if object.iana_doc
49
+ @source ||= unknown_source
50
+ @source
51
+ end
52
+
53
+ def iana_source
54
+ Source.new(
55
+ name: 'IANA',
56
+ href: IANA_HREF,
57
+ type: :iana
58
+ )
59
+ end
60
+
61
+ def unknown_source
62
+ Source.new(
63
+ name: 'Unknown',
64
+ href: nil,
65
+ type: :unknown
66
+ )
67
+ end
68
+
69
+ def css_classes_for(**options)
70
+ classes = []
71
+ classes.concat http_method_classes(options[:http_method]) if options.key? :http_method
72
+ classes.join(' ' )
73
+ end
74
+
75
+ def http_method_classes(method)
76
+ method = method.to_s.downcase
77
+ css_classes = ['http-method']
78
+ css_classes << method if HTTP_METHODS.include? method
79
+ css_classes
80
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shaf/profiles'
4
+
5
+ def init
6
+ super
7
+
8
+ init_options
9
+ end
10
+
11
+ def init_options
12
+ options.resources = YARD::Registry.all(:resource)
13
+ options.profiles = YARD::Registry.all(:profile)
14
+ end
15
+
16
+ def serializer(base_path: nil)
17
+ YARD::Serializers::FileSystemSerializer.new.tap do |serializer|
18
+ base_path ||= self.base_path
19
+ serializer.basepath = base_path
20
+ end
21
+ end
22
+
23
+ def base_path
24
+ "#{public_path}/api_doc"
25
+ end
26
+
27
+ def public_path
28
+ path = String(Shaf::Settings.public_folder)
29
+ return 'public' if path.empty?
30
+ path
31
+ end
@@ -0,0 +1,8 @@
1
+ <div id="profile-list" class="list sidebar-content <%= 'active' if profile? %>">
2
+ <ul>
3
+ <% @profiles.each do |name:, path:| %>
4
+ <li class="sidebar-link <%= 'active' if profile_active?(name) %>">
5
+ <a href=<%= path %> class="<%= 'active' if profile_active?(name) %>"><%= name%></a>
6
+ </li>
7
+ <% end %>
8
+ </div>
@@ -0,0 +1,7 @@
1
+ <%# TODO: This partial is urrently not used!! %>
2
+ <div id="search" class="sidebar-search">
3
+ <form>
4
+ <label for="search">Search</label>
5
+ <input id="search-input" type="text">
6
+ </form>
7
+ </div>
@@ -0,0 +1,8 @@
1
+ <div id="serializer-list" class="list sidebar-content <%= 'active' if resource? || index? %>">
2
+ <ul>
3
+ <% @resources.each do |name:, path:| %>
4
+ <li class="sidebar-link <%= 'active' if resource_active?(name) %>">
5
+ <a href=<%= path %> class="<%= 'active' if resource_active?(name) %>"><%= name%></a>
6
+ </li>
7
+ <% end %>
8
+ </div>