strelka-presenters 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: bde111f87ede9477a436d489cf6b8675ab93031fb3231a08965c227389c59aa3
4
+ data.tar.gz: b64413df84cec398d8f5c45a22fca13c09deb5caa73238ca360ca5fd68d88869
5
+ SHA512:
6
+ metadata.gz: bb85149d0e58a0ed2fc38b6b675a955315933d4d67885044c5e07a785621b80e0f818f766aa30bbadf13c511dd5baf640bd77588c96ce0456076e5719369e1d5
7
+ data.tar.gz: 0304763de7b1e4f01b2f64cfb618e968af67780843038d28b876ba671777d44d354a914dee477b750e1866726a969bc28ec665126c6cdb561ce222e9251a6052
Binary file
@@ -0,0 +1,4 @@
1
+ ��Cʥ�Fe؈��|�����znk��vwn3����!��
2
+ zG+HA��Pi���Ѝhewu�T�l���vX`&��^�d32])�?J(� ��s�!x�cS�+vٳ,,O
3
+ ���w0�q�*N�w�8{�n{5w�3�Aޑ����5op/�6�摰qZ&��lv屹�[��u�0 &>�$W_Z�G4^#�y,��St&^0�b=B���7P}[; 6��DZ!MD��<�G1�M4i��e��j~�P��2�%����]Z6�6?V���B(wq����y�h٩�s�������!Ѽ��N���G�5��O����w{89X��vbfyGm�?#:Չ;��)
4
+ �g���"hk%���K�0����9����G]Nеi������
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ README.md
3
+ ChangeLog.md
4
+
5
+ LICENSE.txt
@@ -0,0 +1,16 @@
1
+ --- !ruby/object:RDoc::Options
2
+ encoding: UTF-8
3
+ static_path: []
4
+ rdoc_include:
5
+ - .
6
+ charset: UTF-8
7
+ exclude:
8
+ hyperlink_all: false
9
+ line_numbers: false
10
+ main_page: README.md
11
+ markup: markdown
12
+ show_hash: false
13
+ tab_width: 8
14
+ title: strelka-Presenters Documentation
15
+ visibility: :protected
16
+ webcvs:
@@ -0,0 +1,9 @@
1
+ # Simplecov config
2
+
3
+ SimpleCov.start do
4
+ add_filter 'spec'
5
+ add_filter 'integration'
6
+ add_group "Needing tests" do |file|
7
+ file.covered_percent < 90
8
+ end
9
+ end
@@ -0,0 +1,42 @@
1
+ 2019-08-13 Michael Granger <ged@FaerieMUD.org>
2
+
3
+ @ * README.md:
4
+ | Fix URLs and title in the README
5
+ | [6ac7de9075da] [tip]
6
+ |
7
+ 2019-08-12 Michael Granger <ged@FaerieMUD.org>
8
+
9
+ o * Rakefile, strelka-presenters.gemspec:
10
+ | Add URLs to the gemspec.
11
+ | [720bbedd3323]
12
+ |
13
+ o * strelka-presenters.gemspec:
14
+ | Commit the pre-release gemspec
15
+ | [7193b0e574d3]
16
+ |
17
+ 2019-08-11 Michael Granger <ged@FaerieMUD.org>
18
+
19
+ o * .hgignore, Manifest.txt:
20
+ | Update the manifest
21
+ | [dfa3077cfaeb]
22
+ |
23
+ o * Rakefile:
24
+ | Remove the publishing guard
25
+ | [3d2b12a06693]
26
+ |
27
+ 2019-08-07 Michael Granger <ged@FaerieMUD.org>
28
+
29
+ o * .document, .editorconfig, .gems, .hgignore, .pryrc, .rdoc_options,
30
+ .ruby-gemset, .ruby-version, .simplecov, Gemfile, History.md,
31
+ LICENSE.txt, Manifest.txt, README.md, Rakefile, certs/ged.pem,
32
+ lib/strelka/app/presenters.rb,
33
+ lib/strelka/httpresponse/presenters.rb, lib/strelka/presenters.rb,
34
+ spec/spec_helper.rb, spec/strelka/app/presenters_spec.rb,
35
+ spec/strelka/httpresponse/presenters_spec.rb,
36
+ spec/strelka/presenters_spec.rb, spec/testlib.rb,
37
+ spec/testlib/base.rb, spec/testlib/fixtures.rb,
38
+ spec/testlib/fixtures/groups.rb, spec/testlib/fixtures/users.rb,
39
+ spec/testlib/group.rb, spec/testlib/presenters.rb,
40
+ spec/testlib/user.rb:
41
+ Initial commit.
42
+ [83ab22adf1b7]
@@ -0,0 +1,4 @@
1
+ ## v0.1.0 [2019-08-24] Michael Granger <ged@FaerieMUD.org>
2
+
3
+ Initial release.
4
+
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2019 Michael Granger
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,24 @@
1
+ .document
2
+ .rdoc_options
3
+ .simplecov
4
+ ChangeLog
5
+ History.md
6
+ LICENSE.txt
7
+ Manifest.txt
8
+ README.md
9
+ Rakefile
10
+ lib/strelka/app/presenters.rb
11
+ lib/strelka/httpresponse/presenters.rb
12
+ lib/strelka/presenters.rb
13
+ spec/spec_helper.rb
14
+ spec/strelka/app/presenters_spec.rb
15
+ spec/strelka/httpresponse/presenters_spec.rb
16
+ spec/strelka/presenters_spec.rb
17
+ spec/testlib.rb
18
+ spec/testlib/base.rb
19
+ spec/testlib/fixtures.rb
20
+ spec/testlib/fixtures/groups.rb
21
+ spec/testlib/fixtures/users.rb
22
+ spec/testlib/group.rb
23
+ spec/testlib/presenters.rb
24
+ spec/testlib/user.rb
@@ -0,0 +1,107 @@
1
+ # Strelka-Presenters
2
+
3
+ home
4
+ : http://deveiate.org/projects/strelka-presenters
5
+
6
+ code
7
+ : http://bitbucket.org/ged/strelka-presenters
8
+
9
+ github
10
+ : https://github.com/ged/strelka-presenters
11
+
12
+ docs
13
+ : http://deveiate.org/code/strelka-presenters
14
+
15
+
16
+ ## Description
17
+
18
+ Strelka-Presenters is a plugin for the Strelka web framework that adds
19
+ integration with the [Yaks hypermedia library][Yaks].
20
+
21
+ ### Usage
22
+
23
+ Load the plugin in your Strelka app, passing the Yaks configuration block:
24
+
25
+ class Example::App < Strelka::App
26
+
27
+ plugin :presenters do
28
+ default_format :hal
29
+ rel_template 'https://example.com/rels/{rel}'
30
+ mapper_namespace Example::Service::Presenters
31
+ end
32
+
33
+ plugin :router
34
+
35
+ get 'users' do |request|
36
+ users = Example::Users.all
37
+ response = request.response
38
+ response.present( users )
39
+ return response
40
+ end
41
+
42
+ end
43
+
44
+ Your app will automatically load the `negotiation` plugin, and responses will have a #present method that will map objects passed to it using Yaks into an acceptable response format using HTTP content negotiation.
45
+
46
+ See the documentation for [Yaks][] for more info.
47
+
48
+ ## Prerequisites
49
+
50
+ * Ruby
51
+
52
+
53
+ ## Installation
54
+
55
+ $ gem install strelka-presenters
56
+
57
+
58
+ ## Contributing
59
+
60
+ You can check out the current development source with Mercurial via its
61
+ [project page][project]. Or if you prefer Git, via
62
+ [its Github mirror][gitmirror].
63
+
64
+ After checking out the source, run:
65
+
66
+ $ rake newb
67
+
68
+ This task will install any missing dependencies, run the tests/specs,
69
+ and generate the API documentation.
70
+
71
+
72
+ ## License
73
+
74
+ Copyright (c) 2019, Michael Granger
75
+ All rights reserved.
76
+
77
+ Redistribution and use in source and binary forms, with or without
78
+ modification, are permitted provided that the following conditions are met:
79
+
80
+ * Redistributions of source code must retain the above copyright notice,
81
+ this list of conditions and the following disclaimer.
82
+
83
+ * Redistributions in binary form must reproduce the above copyright notice,
84
+ this list of conditions and the following disclaimer in the documentation
85
+ and/or other materials provided with the distribution.
86
+
87
+ * Neither the name of the author/s, nor the names of the project's
88
+ contributors may be used to endorse or promote products derived from this
89
+ software without specific prior written permission.
90
+
91
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
92
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
93
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
94
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
95
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
96
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
97
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
98
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
99
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
100
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
101
+
102
+
103
+
104
+ [project]: http://bitbucket.org/ged/strelka-presenters
105
+ [Yaks]: https://github.com/plexus/yaks/blob/master/yaks/README.md
106
+ [gitmirror]: https://github.com/ged/strelka-presenters
107
+
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env rake
2
+
3
+ begin
4
+ require 'hoe'
5
+ rescue LoadError
6
+ abort "This Rakefile requires hoe (gem install hoe)"
7
+ end
8
+
9
+ GEMSPEC = 'strelka-presenters.gemspec'
10
+
11
+
12
+ Hoe.plugin :mercurial
13
+ Hoe.plugin :signing
14
+ Hoe.plugin :deveiate
15
+
16
+ Hoe.plugins.delete :rubyforge
17
+
18
+ hoespec = Hoe.spec 'strelka-presenters' do |spec|
19
+
20
+ spec.readme_file = 'README.md'
21
+ spec.history_file = 'History.md'
22
+
23
+ spec.extra_rdoc_files = FileList[ '*.rdoc', '*.md' ]
24
+ spec.license 'BSD-3-Clause'
25
+ spec.urls = {
26
+ home: 'https://bitbucket.org/ged/strelka-presenters',
27
+ code: 'https://bitbucket.org/ged/strelka-presenters/src',
28
+ docs: 'https://deveiate.org/code/strelka-presenters',
29
+ github: 'https://github.com/ged/strelka-presenters',
30
+ }
31
+
32
+ spec.developer 'Michael Granger', 'ged@FaerieMUD.org'
33
+
34
+ spec.dependency 'strelka', '~> 0.16'
35
+ spec.dependency 'yaks', '~> 0.13'
36
+
37
+ spec.dependency 'faker', '~> 2.1'
38
+ spec.dependency 'fluent_fixtures', '~> 0.8'
39
+ spec.dependency 'hoe-deveiate', '~> 0.10', :developer
40
+ spec.dependency 'simplecov', '~> 0.17', :developer
41
+ spec.dependency 'rdoc-generator-fivefish', '~> 0.1', :developer
42
+
43
+ spec.require_ruby_version( '>=2.5.0' )
44
+ spec.hg_sign_tags = true if spec.respond_to?( :hg_sign_tags= )
45
+ spec.check_history_on_release = true if spec.respond_to?( :check_history_on_release= )
46
+
47
+ self.rdoc_locations << "deveiate:/usr/local/www/public/code/#{remote_rdoc_dir}"
48
+ end
49
+
50
+
51
+ ENV['VERSION'] ||= hoespec.spec.version.to_s
52
+
53
+ # Run the tests before checking in
54
+ task 'hg:precheckin' => [ :check_history, :check_manifest, :gemspec, :spec ]
55
+
56
+ task :test => :spec
57
+
58
+ # Rebuild the ChangeLog immediately before release
59
+ task :prerelease => 'ChangeLog'
60
+ CLOBBER.include( 'ChangeLog' )
61
+
62
+ desc "Build a coverage report"
63
+ task :coverage do
64
+ ENV["COVERAGE"] = 'yes'
65
+ Rake::Task[:spec].invoke
66
+ end
67
+ CLOBBER.include( 'coverage' )
68
+
69
+
70
+ # Use the fivefish formatter for docs generated from development checkout
71
+ if File.directory?( '.hg' )
72
+ require 'rdoc/task'
73
+
74
+ Rake::Task[ 'docs' ].clear
75
+ RDoc::Task.new( 'docs' ) do |rdoc|
76
+ rdoc.main = "README.md"
77
+ rdoc.markup = 'markdown'
78
+ rdoc.rdoc_files.include( "*.md", "ChangeLog", "lib/**/*.rb" )
79
+ rdoc.generator = :fivefish
80
+ rdoc.title = 'Strelka Presenters Plugin'
81
+ rdoc.rdoc_dir = 'doc'
82
+ end
83
+ end
84
+
85
+ task :gemspec => GEMSPEC
86
+ file GEMSPEC => __FILE__
87
+ task GEMSPEC do |task|
88
+ spec = $hoespec.spec
89
+ spec.files.delete( '.gemtest' )
90
+ spec.signing_key = nil
91
+ spec.cert_chain = ['certs/ged.pem']
92
+ spec.version = "#{spec.version.bump}.0.pre#{Time.now.strftime("%Y%m%d%H%M%S")}"
93
+ File.open( task.name, 'w' ) do |fh|
94
+ fh.write( spec.to_ruby )
95
+ end
96
+ end
97
+ CLOBBER.include( GEMSPEC.to_s )
98
+
99
+ task :default => :gemspec
100
+
@@ -0,0 +1,85 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'yaks'
5
+
6
+ require 'strelka/app' unless defined?( Strelka::App )
7
+ require 'strelka/plugins'
8
+ require 'strelka/mixins'
9
+
10
+ require 'strelka/httpresponse/presenters'
11
+
12
+
13
+ # Presentation layer plugin for Strelka service apps.
14
+ module Strelka::App::Presenters
15
+ include Strelka::Constants
16
+ extend Strelka::Plugin
17
+
18
+ run_outside :routing, :filters
19
+ run_inside :templating, :parameters, :negotiation
20
+
21
+
22
+ # Class methods to add to classes with presenters
23
+ module ClassMethods
24
+
25
+ ##
26
+ # The block given to Yaks when it's configured
27
+ attr_accessor :presenter_config_block
28
+
29
+ end # module ClassMethods
30
+
31
+
32
+ ### Extension callback -- extend the HTTPResponse classes with presenter logic
33
+ ### when the plugin is loaded.
34
+ def self::included( object )
35
+ Strelka::HTTPResponse.include( Strelka::HTTPResponse::Presenters )
36
+ super
37
+ end
38
+
39
+
40
+ ### Set a block passed to `plugin` to the presenter library when it's
41
+ ### configured.
42
+ def self::configure_block( app, &block )
43
+ app.plugin( :negotiation )
44
+ app.presenter_config_block = block
45
+ end
46
+
47
+
48
+ ### Set up the presenters when the app is created.
49
+ def initialize( * )
50
+ super
51
+ @presenter = self.setup_presentation_layer
52
+ end
53
+
54
+
55
+ ######
56
+ public
57
+ ######
58
+
59
+ ##
60
+ # The configured Yaks presenter object
61
+ attr_reader :presenter
62
+
63
+
64
+ ### Set up the presenter when the response has returned.
65
+ def handle_request( req )
66
+ self.log.debug "[:negotiation] Wrapping response with hypermedia presentation."
67
+
68
+ response = super
69
+ response.presenter = self.presenter
70
+
71
+ return response
72
+ end
73
+
74
+
75
+ ### Configure and return the presenter.
76
+ def setup_presentation_layer
77
+ self.log.debug "Setting up presenters using %s" %
78
+ [ self.class.presenter_config_block&.inspect || 'defaults' ]
79
+ config_block = self.class.presenter_config_block || Proc.new {}
80
+ return Yaks.new( &config_block )
81
+ end
82
+
83
+ end # module Strelka::App::Presenters
84
+
85
+
@@ -0,0 +1,45 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'yaks'
5
+
6
+ require 'strelka/httpresponse' unless defined?( Strelka::HTTPResponse )
7
+
8
+
9
+ # Hypermedia presentation logic for Strelka HTTP responses
10
+ module Strelka::HTTPResponse::Presenters
11
+
12
+ ### Initialize some instance variables when a new response is created.
13
+ def initialize( * )
14
+ @presenter = nil
15
+ super
16
+ end
17
+
18
+
19
+ ######
20
+ public
21
+ ######
22
+
23
+ ##
24
+ # The presenter to use when negotiating the response
25
+ attr_accessor :presenter
26
+
27
+
28
+ ### Added so we can pass the response object to the presenters as the `env`.
29
+ def []( * ) # :nodoc:
30
+ return nil
31
+ end
32
+
33
+
34
+ ### Set the entity that should be rendered to form the response body.
35
+ def present( entity, **options )
36
+ Yaks::Format.all.each do |format|
37
+ self.for( format.media_type ) do
38
+ opts = options.merge( format: format.format_name, env: self )
39
+ self.presenter.call( entity, opts )
40
+ end
41
+ end
42
+ end
43
+
44
+ end # module Strelka::HTTPResponse::Presenters
45
+
@@ -0,0 +1,21 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'strelka' unless defined?( Strelka )
5
+
6
+
7
+ # Toplevel namespace
8
+ module Strelka::Presenters
9
+
10
+ # Package version
11
+ VERSION = '0.1.0'
12
+
13
+ # Version control revision
14
+ REVISION = %q$Revision$
15
+
16
+
17
+ require 'strelka/app/presenters'
18
+ require 'strelka/httpresponse/presenters'
19
+
20
+ end # module Strelka::Presenters
21
+
@@ -0,0 +1,42 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ BEGIN {
5
+ $LOAD_PATH.unshift( '../Strelka/lib' )
6
+ }
7
+
8
+ require 'simplecov' if ENV['COVERAGE']
9
+
10
+ require 'rspec'
11
+
12
+ require 'loggability/spechelpers'
13
+ require 'mongrel2'
14
+ require 'mongrel2/testing'
15
+ require 'strelka'
16
+ require 'strelka/testing'
17
+
18
+
19
+ ### Mock with RSpec
20
+ RSpec.configure do |config|
21
+ config.mock_with( :rspec ) do |mock|
22
+ mock.syntax = :expect
23
+ end
24
+
25
+ config.disable_monkey_patching!
26
+ config.example_status_persistence_file_path = "spec/.status"
27
+ config.filter_run :focus
28
+ config.filter_run_when_matching :focus
29
+ config.order = :random
30
+ config.profile_examples = 5
31
+ config.run_all_when_everything_filtered = true
32
+ config.shared_context_metadata_behavior = :apply_to_host_groups
33
+ # config.warnings = true
34
+
35
+ config.include( Loggability::SpecHelpers )
36
+ config.include( Mongrel2::SpecHelpers )
37
+ config.include( Mongrel2::Constants )
38
+ config.include( Strelka::Constants )
39
+ config.include( Strelka::Testing )
40
+ end
41
+
42
+
@@ -0,0 +1,260 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../../spec_helper'
5
+
6
+ require 'strelka'
7
+ require 'mongrel2/testing'
8
+ require 'strelka/testing'
9
+ require 'strelka/constants'
10
+ require 'strelka/behavior/plugin'
11
+
12
+ require 'testlib'
13
+
14
+ require 'strelka/app/presenters'
15
+
16
+
17
+ RSpec.describe( Strelka::App::Presenters ) do
18
+
19
+ TEST_SEND_SPEC = 'tcp://127.0.0.1:12012'
20
+ TEST_RECV_SPEC = 'tcp://127.0.0.1:12013'
21
+
22
+
23
+ before( :all ) do
24
+ Testlib::Fixtures.load( :users, :groups )
25
+
26
+ @request_factory = Mongrel2::RequestFactory.new(
27
+ route: '/api/v1',
28
+ host: 'acme.com',
29
+ port: 80
30
+ )
31
+ end
32
+
33
+
34
+ it_should_behave_like( "A Strelka Plugin" )
35
+
36
+
37
+ describe "an including App" do
38
+
39
+ before( :each ) do
40
+ Testlib.reset
41
+
42
+ @app = Class.new( Strelka::App ) do
43
+ def initialize( appid='presenters-test', sspec=TEST_SEND_SPEC, rspec=TEST_RECV_SPEC )
44
+ super
45
+ end
46
+
47
+ plugins :routing
48
+ router :exclusive
49
+
50
+ plugin :parameters
51
+ param :id, :integer
52
+
53
+ plugin :presenters do
54
+ default_format :hal
55
+ rel_template 'https://acme.com/rels/{rel}'
56
+ format_options( :hal, plural_links: [:copyright] )
57
+ mapper_namespace Testlib::Presenters
58
+ end
59
+
60
+ get '/users' do |req|
61
+ res = req.response
62
+ res.present( Testlib::User.all )
63
+ return res
64
+ end
65
+
66
+ get '/users/:id' do |req|
67
+ id = req.params[:id]
68
+
69
+ user = Testlib::User[ id ] or finish_with HTTP::NOT_FOUND, "no such user"
70
+ res = req.response
71
+ res.present( user )
72
+
73
+ return res
74
+ end
75
+ end
76
+ end
77
+
78
+
79
+ let( :user_factory ) { Testlib::Fixtures.user.full }
80
+ let( :user_generator ) { user_factory.generator(create: true) }
81
+
82
+
83
+ it "can render a singleton HAL response" do
84
+ user = user_factory.create
85
+
86
+ req = @request_factory.get( "/api/v1/users/#{user.id}", accept: 'application/hal+json' )
87
+ res = @app.new.handle( req )
88
+
89
+ expect( res.status ).to eq( 200 )
90
+ expect( res.content_type ).to eq( 'application/hal+json' )
91
+ expect( res ).to have_json_body( Object ).
92
+ that_includes(
93
+ id: user.id,
94
+ name: user.name,
95
+ email: user.email,
96
+ login: user.login,
97
+ _links: a_hash_including(
98
+ self: {
99
+ href: "/api/v1/users/#{user.id}"
100
+ }
101
+ )
102
+ )
103
+ end
104
+
105
+
106
+ it "can render a collection HAL response" do
107
+ users = user_generator.take( 10 )
108
+
109
+ req = @request_factory.get( "/api/v1/users", accept: 'application/hal+json' )
110
+ res = @app.new.handle( req )
111
+
112
+ expect( res.status ).to eq( 200 )
113
+ expect( res.content_type ).to eq( 'application/hal+json' )
114
+ expect( res ).to have_json_body( Object ).
115
+ that_includes(
116
+ _embedded: {
117
+ "https://acme.com/rels/users": a_collection_containing_exactly(
118
+ *users.map {|u| a_hash_including(id: u.id) }
119
+ )
120
+ }
121
+ )
122
+ end
123
+
124
+
125
+ it "can render a singleton HALO response" do
126
+ user = user_factory.create
127
+
128
+ req = @request_factory.get( "/api/v1/users/#{user.id}", accept: 'application/halo+json' )
129
+ res = @app.new.handle( req )
130
+
131
+ expect( res.status ).to eq( 200 )
132
+ expect( res.content_type ).to eq( 'application/halo+json' )
133
+ expect( res ).to have_json_body( Object ).
134
+ that_includes(
135
+ id: user.id,
136
+ name: user.name,
137
+ email: user.email,
138
+ login: user.login,
139
+ _links: a_hash_including(
140
+ self: {
141
+ href: "/api/v1/users/#{user.id}"
142
+ }
143
+ )
144
+ )
145
+ end
146
+
147
+
148
+ it "can render a collection HALO response" do
149
+ users = user_generator.take( 10 )
150
+
151
+ req = @request_factory.get( "/api/v1/users", accept: 'application/halo+json' )
152
+ res = @app.new.handle( req )
153
+
154
+ expect( res.status ).to eq( 200 )
155
+ expect( res.content_type ).to eq( 'application/halo+json' )
156
+ expect( res ).to have_json_body( Object ).
157
+ that_includes(
158
+ _embedded: {
159
+ "https://acme.com/rels/users": a_collection_containing_exactly(
160
+ *users.map {|u| a_hash_including(id: u.id) }
161
+ )
162
+ }
163
+ )
164
+ end
165
+
166
+
167
+ it "can render a singleton JSON-API response" do
168
+ user = user_factory.create
169
+
170
+ req = @request_factory.get( "/api/v1/users/#{user.id}", accept: 'application/vnd.api+json' )
171
+ res = @app.new.handle( req )
172
+
173
+ expect( res.status ).to eq( 200 )
174
+ expect( res.content_type ).to eq( 'application/vnd.api+json' )
175
+ expect( res ).to have_json_body( Object ).
176
+ that_includes(
177
+ data: {
178
+ attributes: {
179
+ name: user.name,
180
+ email: user.email,
181
+ login: user.login,
182
+ },
183
+ id: user.id.to_s,
184
+ links: {
185
+ self: "/api/v1/users/#{user.id}"
186
+ },
187
+ type: "users"
188
+ }
189
+ )
190
+ end
191
+
192
+
193
+ it "can render a collection JSON-API response" do
194
+ users = user_generator.take( 10 )
195
+
196
+ req = @request_factory.get( "/api/v1/users", accept: 'application/vnd.api+json' )
197
+ res = @app.new.handle( req )
198
+
199
+ expect( res.status ).to eq( 200 )
200
+ expect( res.content_type ).to eq( 'application/vnd.api+json' )
201
+ expect( res ).to have_json_body( Object ).
202
+ that_includes(
203
+ data: a_collection_containing_exactly(
204
+ *users.map {|u| a_hash_including(id: u.id.to_s) }
205
+ )
206
+ )
207
+ end
208
+
209
+
210
+ it "can render a singleton Collection+JSON response" do
211
+ user = user_factory.create
212
+
213
+ req = @request_factory.get(
214
+ "/api/v1/users/#{user.id}",
215
+ accept: 'application/vnd.collection+json'
216
+ )
217
+ res = @app.new.handle( req )
218
+
219
+ expect( res.status ).to eq( 200 )
220
+ expect( res.content_type ).to eq( 'application/vnd.collection+json' )
221
+ expect( res ).to have_json_body( Object ).
222
+ that_includes(
223
+ collection: {
224
+ version: '1.0',
225
+ items: [
226
+ {
227
+ data: a_collection_containing_exactly(
228
+ { name: "name", value: user.name},
229
+ { name: "email", value: user.email},
230
+ { name: "login", value: user.login},
231
+ { name: "id", value: user.id},
232
+ ),
233
+ href: "/api/v1/users/#{user.id}"
234
+ }
235
+ ],
236
+ href: "/api/v1/users/#{user.id}"
237
+ }
238
+ )
239
+ end
240
+
241
+
242
+ it "can render a collection Collection+JSON response" do
243
+ users = user_generator.take( 10 )
244
+
245
+ req = @request_factory.get(
246
+ "/api/v1/users",
247
+ accept: 'application/vnd.collection+json'
248
+ )
249
+ res = @app.new.handle( req )
250
+
251
+ expect( res.status ).to eq( 200 )
252
+ expect( res.content_type ).to eq( 'application/vnd.collection+json' )
253
+ expect( res ).to have_json_body( Object ).
254
+ that_includes( :collection )
255
+ end
256
+
257
+ end
258
+
259
+ end
260
+
@@ -0,0 +1,22 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../../spec_helper'
5
+
6
+ require 'strelka/httpresponse/presenters'
7
+
8
+
9
+ RSpec.describe Strelka::HTTPResponse::Presenters do
10
+
11
+ it "adds an index operator to the response to allow it to quack like a Rack ENV" do
12
+ dummy_response_class = Class.new
13
+ dummy_response_class.include( described_class )
14
+ dummy_response = dummy_response_class.new
15
+
16
+ expect {
17
+ dummy_response[ 'HTTP_ACCEPT' ]
18
+ }.to_not raise_error()
19
+ end
20
+
21
+ end
22
+
@@ -0,0 +1,17 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../spec_helper'
5
+
6
+ require 'rspec'
7
+ require 'strelka/presenters'
8
+
9
+
10
+ RSpec.describe( Strelka::Presenters ) do
11
+
12
+ it "knows what version of the library it is" do
13
+ expect( described_class::VERSION ).to be_a( String ).and( match(/\A\d+\.\d+\.\d+\z/) )
14
+ end
15
+
16
+ end
17
+
@@ -0,0 +1,20 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ module Testlib
5
+
6
+ autoload :Base, 'testlib/base'
7
+ autoload :Group, 'testlib/group'
8
+ autoload :User, 'testlib/user'
9
+ autoload :Fixtures, 'testlib/fixtures'
10
+ autoload :Presenters, 'testlib/presenters'
11
+
12
+
13
+ def self::reset
14
+ Testlib::Base::DATASTORE.clear
15
+ Testlib::Base::SERIAL_GENERATOR.clear
16
+ end
17
+
18
+ end
19
+
20
+
@@ -0,0 +1,57 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'testlib' unless defined?( Testlib )
5
+
6
+
7
+ class Testlib::Base
8
+
9
+ # Fancy database simulator
10
+ DATASTORE = Hash.new do |h, modelclass|
11
+ h[ modelclass ] = {}
12
+ end
13
+ SERIAL_GENERATOR = Hash.new do |h, modelclass|
14
+ h[ modelclass ] = Enumerator.new do |yielder|
15
+ i = 0
16
+ loop { i += 1; yielder.yield(i) }
17
+ end
18
+ end
19
+
20
+
21
+ def self::[]( id )
22
+ return DATASTORE[ self ][ id ]
23
+ end
24
+
25
+ def self::all
26
+ return DATASTORE[ self ].values
27
+ end
28
+
29
+
30
+ def initialize( params={} )
31
+ params.each do |name, value|
32
+ self.send( "#{name}=", value )
33
+ end
34
+ end
35
+
36
+
37
+ attr_accessor :id
38
+ alias_method :pk, :id
39
+
40
+
41
+ def []( attr_name )
42
+ return self.public_send( param )
43
+ end
44
+
45
+
46
+ def save
47
+ self.id ||= SERIAL_GENERATOR[ self.class ].next
48
+ DATASTORE[ self.class ][ self.id ] = self.dup
49
+ end
50
+
51
+
52
+ def saved?
53
+ return self.id && DATASTORE[ self.class ].key?( self.id )
54
+ end
55
+
56
+ end
57
+
@@ -0,0 +1,22 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'fluent_fixtures'
5
+ require 'testlib' unless defined?( Testlib )
6
+
7
+ module Testlib::Fixtures
8
+ extend FluentFixtures::Collection
9
+
10
+ fixture_path_prefix 'testlib/fixtures'
11
+
12
+
13
+ def self::describe
14
+ desc = "%p: %d fixtures loaded" % [ self, self.modules.length ]
15
+ unless self.modules.empty?
16
+ desc << ": %s" % [ self.modules.keys.map(&:to_s).join(', ') ]
17
+ end
18
+ return desc
19
+ end
20
+
21
+ end
22
+
@@ -0,0 +1,12 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'faker'
5
+ require 'testlib/fixtures'
6
+ require 'testlib/group'
7
+
8
+ module Testlib::Fixtures::Groups
9
+ extend Testlib::Fixtures
10
+ fixtured_class Testlib::Group
11
+ end
12
+
@@ -0,0 +1,28 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'faker'
5
+ require 'testlib/fixtures'
6
+ require 'testlib/user'
7
+
8
+ module Testlib::Fixtures::Users
9
+ extend Testlib::Fixtures
10
+ fixtured_class Testlib::User
11
+
12
+ decorator :with_random_first_name do
13
+ self.first_name = Faker::Name.first_name
14
+ end
15
+
16
+ decorator :with_random_last_name do
17
+ self.last_name = Faker::Name.last_name
18
+ end
19
+
20
+ compose :with_random_name => [ :with_random_first_name, :with_random_last_name ]
21
+
22
+ compose :full => :with_random_name do
23
+ self.login = "%s%s" % [ self.first_name[0,1].downcase, self.last_name.downcase ]
24
+ self.email = "%s@example.com" % [ self.first_name.downcase ]
25
+ end
26
+
27
+ end
28
+
@@ -0,0 +1,19 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'set'
5
+ require 'testlib' unless defined?( Testlib )
6
+ require 'testlib/base'
7
+
8
+
9
+ class Testlib::Group < Testlib::Base
10
+
11
+ def initialize( * )
12
+ super
13
+ @members ||= Set.new
14
+ end
15
+
16
+ attr_accessor :name, :members
17
+
18
+ end
19
+
@@ -0,0 +1,20 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'yaks'
5
+
6
+ require 'testlib' unless defined?( Testlib )
7
+
8
+
9
+ # Presentation entities
10
+ module Testlib::Presenters
11
+
12
+ class UserMapper < Yaks::Mapper
13
+ link :self, '/api/v1/users/{id}'
14
+
15
+ attributes :id, :name, :email, :login
16
+
17
+ end # class PersonaMapper
18
+
19
+ end # module Prestigio::Service::Presenters
20
+
@@ -0,0 +1,24 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'set'
5
+ require 'testlib' unless defined?( Testlib )
6
+ require 'testlib/base'
7
+
8
+
9
+ class Testlib::User < Testlib::Base
10
+
11
+ def initialize( * )
12
+ super
13
+ @roles ||= Set.new
14
+ end
15
+
16
+ attr_accessor :first_name, :last_name, :email, :login, :groups
17
+
18
+
19
+ def name
20
+ return [ self.first_name, self.last_name ].compact.join( ' ' )
21
+ end
22
+
23
+ end
24
+
metadata ADDED
@@ -0,0 +1,262 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: strelka-presenters
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Michael Granger
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIENDCCApygAwIBAgIBATANBgkqhkiG9w0BAQsFADAiMSAwHgYDVQQDDBdnZWQv
14
+ REM9RmFlcmllTVVEL0RDPW9yZzAeFw0xODExMjAxODI5NTlaFw0xOTExMjAxODI5
15
+ NTlaMCIxIDAeBgNVBAMMF2dlZC9EQz1GYWVyaWVNVUQvREM9b3JnMIIBojANBgkq
16
+ hkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAvyVhkRzvlEs0fe7145BYLfN6njX9ih5H
17
+ L60U0p0euIurpv84op9CNKF9tx+1WKwyQvQP7qFGuZxkSUuWcP/sFhDXL1lWUuIl
18
+ M4uHbGCRmOshDrF4dgnBeOvkHr1fIhPlJm5FO+Vew8tSQmlDsosxLUx+VB7DrVFO
19
+ 5PU2AEbf04GGSrmqADGWXeaslaoRdb1fu/0M5qfPTRn5V39sWD9umuDAF9qqil/x
20
+ Sl6phTvgBrG8GExHbNZpLARd3xrBYLEFsX7RvBn2UPfgsrtvpdXjsHGfpT3IPN+B
21
+ vQ66lts4alKC69TE5cuKasWBm+16A4aEe3XdZBRNmtOu/g81gvwA7fkJHKllJuaI
22
+ dXzdHqq+zbGZVSQ7pRYHYomD0IiDe1DbIouFnPWmagaBnGHwXkDT2bKKP+s2v21m
23
+ ozilJg4aar2okb/RA6VS87o+d7g6LpDDMMQjH4G9OPnJENLdhu8KnPw/ivSVvQw7
24
+ N2I4L/ZOIe2DIVuYH7aLHfjZDQv/mNgpAgMBAAGjdTBzMAkGA1UdEwQCMAAwCwYD
25
+ VR0PBAQDAgSwMB0GA1UdDgQWBBRyjf55EbrHagiRLqt5YAd3yb8k4DAcBgNVHREE
26
+ FTATgRFnZWRARmFlcmllTVVELm9yZzAcBgNVHRIEFTATgRFnZWRARmFlcmllTVVE
27
+ Lm9yZzANBgkqhkiG9w0BAQsFAAOCAYEAP9Ffkvg4e8CjIWi8SykQ8oJSS8jbmbgF
28
+ abke3vXWLG6V9kFiObuJd5wZRBluJANu7bEtjgc3fFaGVP2XxVdCpVjNbmMDg4Qp
29
+ ovvczP53X6pQP2RSZgxF6Lblvy8y11RziUTVRG/Z2aJHsElo6gI7vQznE/OSDrhC
30
+ gEhr8uaIUt7D+HZWRbU0+MkKPpL5uMqaFuJbqXEvSwPTuUuYkDfNfsjQO7ruWBac
31
+ bxHCrvpZ6Tijc0nrlyXi6gPOCLeaqhau2xFnlvKgELwsGYSoKBJyDwqtQ5kwrOlU
32
+ tkSyLrfZ+RZcH535Hyvif7ZxB0v5OxXXoec+N2vrUsEUMRDL9dg4/WFdN8hIOixF
33
+ 3IPKpZ1ho0Ya5q7yhygtBK9/NBFHw+nbJjcltfPDBXleRe8u73gnQo8AZIhStYSP
34
+ v4qqqa27Bs468d6SoPxjSm8a2mM9HZ4OdWhq4tFsbTeXDVquCfi64OTEaTt2xQdR
35
+ JnC4lpJfCP6aCXa5h2XAQfPSH636cQap
36
+ -----END CERTIFICATE-----
37
+ date: 2019-08-25 00:00:00.000000000 Z
38
+ dependencies:
39
+ - !ruby/object:Gem::Dependency
40
+ name: strelka
41
+ requirement: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - "~>"
44
+ - !ruby/object:Gem::Version
45
+ version: '0.16'
46
+ type: :runtime
47
+ prerelease: false
48
+ version_requirements: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: '0.16'
53
+ - !ruby/object:Gem::Dependency
54
+ name: yaks
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '0.13'
60
+ type: :runtime
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '0.13'
67
+ - !ruby/object:Gem::Dependency
68
+ name: faker
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '2.1'
74
+ type: :runtime
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '2.1'
81
+ - !ruby/object:Gem::Dependency
82
+ name: fluent_fixtures
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '0.8'
88
+ type: :runtime
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '0.8'
95
+ - !ruby/object:Gem::Dependency
96
+ name: hoe-mercurial
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '1.4'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '1.4'
109
+ - !ruby/object:Gem::Dependency
110
+ name: hoe-deveiate
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '0.10'
116
+ type: :development
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - "~>"
121
+ - !ruby/object:Gem::Version
122
+ version: '0.10'
123
+ - !ruby/object:Gem::Dependency
124
+ name: hoe-highline
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: '0.2'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - "~>"
135
+ - !ruby/object:Gem::Version
136
+ version: '0.2'
137
+ - !ruby/object:Gem::Dependency
138
+ name: simplecov
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - "~>"
142
+ - !ruby/object:Gem::Version
143
+ version: '0.17'
144
+ type: :development
145
+ prerelease: false
146
+ version_requirements: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - "~>"
149
+ - !ruby/object:Gem::Version
150
+ version: '0.17'
151
+ - !ruby/object:Gem::Dependency
152
+ name: rdoc-generator-fivefish
153
+ requirement: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - "~>"
156
+ - !ruby/object:Gem::Version
157
+ version: '0.1'
158
+ type: :development
159
+ prerelease: false
160
+ version_requirements: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - "~>"
163
+ - !ruby/object:Gem::Version
164
+ version: '0.1'
165
+ - !ruby/object:Gem::Dependency
166
+ name: rdoc
167
+ requirement: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: '4.0'
172
+ - - "<"
173
+ - !ruby/object:Gem::Version
174
+ version: '7'
175
+ type: :development
176
+ prerelease: false
177
+ version_requirements: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: '4.0'
182
+ - - "<"
183
+ - !ruby/object:Gem::Version
184
+ version: '7'
185
+ - !ruby/object:Gem::Dependency
186
+ name: hoe
187
+ requirement: !ruby/object:Gem::Requirement
188
+ requirements:
189
+ - - "~>"
190
+ - !ruby/object:Gem::Version
191
+ version: '3.18'
192
+ type: :development
193
+ prerelease: false
194
+ version_requirements: !ruby/object:Gem::Requirement
195
+ requirements:
196
+ - - "~>"
197
+ - !ruby/object:Gem::Version
198
+ version: '3.18'
199
+ description: |-
200
+ Strelka-Presenters is a plugin for the Strelka web framework that adds
201
+ integration with the [Yaks hypermedia library][Yaks].
202
+ email:
203
+ - ged@FaerieMUD.org
204
+ executables: []
205
+ extensions: []
206
+ extra_rdoc_files:
207
+ - History.md
208
+ - LICENSE.txt
209
+ - Manifest.txt
210
+ - README.md
211
+ files:
212
+ - ".document"
213
+ - ".rdoc_options"
214
+ - ".simplecov"
215
+ - ChangeLog
216
+ - History.md
217
+ - LICENSE.txt
218
+ - Manifest.txt
219
+ - README.md
220
+ - Rakefile
221
+ - lib/strelka/app/presenters.rb
222
+ - lib/strelka/httpresponse/presenters.rb
223
+ - lib/strelka/presenters.rb
224
+ - spec/spec_helper.rb
225
+ - spec/strelka/app/presenters_spec.rb
226
+ - spec/strelka/httpresponse/presenters_spec.rb
227
+ - spec/strelka/presenters_spec.rb
228
+ - spec/testlib.rb
229
+ - spec/testlib/base.rb
230
+ - spec/testlib/fixtures.rb
231
+ - spec/testlib/fixtures/groups.rb
232
+ - spec/testlib/fixtures/users.rb
233
+ - spec/testlib/group.rb
234
+ - spec/testlib/presenters.rb
235
+ - spec/testlib/user.rb
236
+ homepage: https://bitbucket.org/ged/strelka-presenters
237
+ licenses:
238
+ - BSD-3-Clause
239
+ metadata: {}
240
+ post_install_message:
241
+ rdoc_options:
242
+ - "--main"
243
+ - README.md
244
+ require_paths:
245
+ - lib
246
+ required_ruby_version: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - ">="
249
+ - !ruby/object:Gem::Version
250
+ version: 2.5.0
251
+ required_rubygems_version: !ruby/object:Gem::Requirement
252
+ requirements:
253
+ - - ">="
254
+ - !ruby/object:Gem::Version
255
+ version: '0'
256
+ requirements: []
257
+ rubygems_version: 3.0.3
258
+ signing_key:
259
+ specification_version: 4
260
+ summary: Strelka-Presenters is a plugin for the Strelka web framework that adds integration
261
+ with the [Yaks hypermedia library][Yaks].
262
+ test_files: []
Binary file