hanami-ujs 0.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7366f61ed357db67e1fcf491940de1d8eeface420cea6075f97986aa4168f7ad
4
+ data.tar.gz: 82a823ff8fc85178303997c9d15509602da2b766019f21016f822c143bea6537
5
+ SHA512:
6
+ metadata.gz: 694be9fbec41848a001644c0ff142e854ba4b690ba415369af91d8fd819b7f509f5ed1f322d88ca5999b296294394599509793427c63f831ba7e3bf84c00ac09
7
+ data.tar.gz: 6c151a1ca85f045d69378a81d49dd9bad121cc0f0a8360e18247863fccdf4d86c9a89c6a6c19cee2e24584950b89853c5bc5437f99ac6487010158f749a49cdf
data/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # Hanami::UJS
2
+ Hanami Unobtrusive JavaScript (UJS)
3
+
4
+ ## v0.1.0.beta1 - 2018-02-28
5
+ ### Added
6
+ - [Luca Guidi] Added support for AJAX forms
7
+ - [Luca Guidi] Package as Hanami plugin
8
+ - [Marion Schleifer] Package JS code from `vanilla-ujs` gem
data/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # Hanami::UJS
2
+
3
+ Unobtrusive JavaScript (UJS) for [Hanami](http://hanamirb.org).
4
+
5
+ ## Status
6
+
7
+ [![Gem Version](http://img.shields.io/gem/v/hanami-ujs.svg)](https://badge.fury.io/rb/hanami-ujs)
8
+
9
+ ## Installation
10
+
11
+ Add this line to your Hanami project's `Gemfile`:
12
+
13
+ ```ruby
14
+ gem "hanami-ujs"
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install hanami-ujs
24
+
25
+ ## Usage
26
+
27
+ ### Setup
28
+
29
+ You have to add two lines to the application layout (eg. `apps/web/templates/application.html.erb`):
30
+
31
+ 1. `<%= csrf_meta_tags %>` inside `<head>`
32
+ 2. `<%= javascript "hanami-ujs" %>` the location is optional, but before `</body>` is a good spot.
33
+
34
+ ### Events
35
+
36
+ Hanami UJS fires events to notify listeners that a certain operation happened:
37
+
38
+ * `"ajax.before"`
39
+ * `"ajax.complete"`
40
+
41
+ You can listen to these events with:
42
+
43
+ ```js
44
+ (function() {
45
+ var ajaxBeforeHandler = function(event) {
46
+ console.log(event);
47
+ };
48
+
49
+ var ajaxCompleteHandler = function(event) {
50
+ console.log(event);
51
+ };
52
+
53
+ document.addEventListener("ajax:before", ajaxBeforeHandler);
54
+ document.addEventListener("ajax:complete", ajaxCompleteHandler);
55
+ })();
56
+ ```
57
+
58
+ ### AJAX Form
59
+
60
+ ```erb
61
+ <%=
62
+ form_for :search, "/search", remote: true do
63
+ # ...
64
+
65
+ submit "Search"
66
+ end
67
+ %>
68
+ ```
69
+
70
+ When the user will hit "Search" the form will be sent via AJAX.
71
+
72
+ ## Development
73
+
74
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
75
+
76
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
77
+
78
+ ## Contributing
79
+
80
+ Bug reports and pull requests are welcome on GitHub at https://github.com/hanami/ujs.
81
+
82
+ ## Acknowledgements
83
+
84
+ This gem JavaScript code is from the great [vanilla-ujs](https://rubygems.org/gems/vanilla-ujs) gem,
85
+ which is MIT licensed (Copyright (c) 2013 Łukasz Niemier). Thank you for your awesome work!
86
+
87
+ ## Copyright
88
+
89
+ Copyright © 2018 Luca Guidi – Released under MIT License
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("../lib", __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "hanami/ujs/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "hanami-ujs"
9
+ spec.version = Hanami::UJS::VERSION
10
+ spec.authors = ["Luca Guidi"]
11
+ spec.email = ["me@lucaguidi.com"]
12
+
13
+ spec.summary = "Hanami UJS"
14
+ spec.description = "Hanami Unobtrusive JavaScript"
15
+ spec.homepage = "http://hanamirb.org"
16
+ spec.license = "MIT"
17
+
18
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
19
+
20
+ spec.files = `git ls-files -- lib/* CHANGELOG.md LICENSE.md README.md hanami-ujs.gemspec`.split($INPUT_RECORD_SEPARATOR)
21
+ spec.bindir = "exe"
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ["lib"]
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.16"
26
+ spec.add_development_dependency "rake", "~> 12.0"
27
+ spec.add_development_dependency "rspec", "~> 3.7"
28
+ end
@@ -0,0 +1,314 @@
1
+ //
2
+ // hanami-ujs (0.1.0)
3
+ // vanilla-ujs (1.3.0)
4
+ //
5
+ // Mon Feb 12 10:45:37 UTC 2018
6
+ //
7
+
8
+ //
9
+ // source: confirm.js
10
+ //
11
+ document.addEventListener('click', function (event) {
12
+ var message, element;
13
+
14
+ element = event.target;
15
+
16
+ if (matches.call(element, 'a[data-confirm], button[data-confirm], input[data-confirm]')) {
17
+ message = element.getAttribute('data-confirm');
18
+ if (!confirm(message)) {
19
+ event.stopPropagation();
20
+ event.stopImmediatePropagation();
21
+ event.preventDefault();
22
+ return false;
23
+ }
24
+
25
+ return;
26
+ }
27
+ }, false);
28
+
29
+ //
30
+ // source: csrf.js
31
+ //
32
+ var CSRF = {
33
+ token: function () {
34
+ var token = document.querySelector('meta[name="csrf-token"]');
35
+ return token && token.getAttribute('content');
36
+ },
37
+ param: function () {
38
+ var param = document.querySelector('meta[name="csrf-param"]');
39
+ return param && param.getAttribute('content');
40
+ }
41
+ };
42
+
43
+ var sameOrigin = function (url) {
44
+ var a = document.createElement('a'), origin;
45
+ a.href = url;
46
+ origin = a.href.split('/', 3).join('/');
47
+
48
+ return window.location.href.indexOf(origin) === 0;
49
+ };
50
+
51
+ window.CSRF = CSRF;
52
+
53
+ document.addEventListener('ajax:before', function (e) {
54
+ var token = CSRF.token(), xhr = e.detail;
55
+ if (token)
56
+ xhr.setRequestHeader('X-CSRF-Token', token);
57
+ });
58
+
59
+ document.addEventListener('submit', function (e) {
60
+ var token = CSRF.token(),
61
+ param = CSRF.param(),
62
+ form = e.target;
63
+
64
+ if (matches.call(form, 'form')) {
65
+ if (matches.call(form, 'form[data-remote]'))
66
+ return true;
67
+ if (!form.method || form.method.toUpperCase() == 'GET')
68
+ return true;
69
+ if (!sameOrigin(form.action))
70
+ return true;
71
+
72
+ if (param && token && !form.querySelector('input[name='+param+']')) {
73
+ var input = document.createElement('input');
74
+ input.setAttribute('type', 'hidden');
75
+ input.setAttribute('name', param);
76
+ input.setAttribute('value', token);
77
+
78
+ form.appendChild(input);
79
+ }
80
+
81
+ return true;
82
+ }
83
+ });
84
+
85
+ //
86
+ // source: disable.js
87
+ //
88
+ document.addEventListener('click', function (event) {
89
+ var message, element;
90
+
91
+ // do not disable on right click. Work on left and middle click
92
+ if (event.which == 3) {
93
+ return;
94
+ }
95
+
96
+ element = event.target;
97
+
98
+ // do not disable if the element is a submit button and its form has invalid input elements.
99
+ // since failed validations prevent the form from being submitted, we would lock the form permanently
100
+ // by disabling the submit button even though the form was never submitted
101
+
102
+ if(element.getAttribute("type") === "submit" && element.form.querySelector(":invalid") !== null) {
103
+ return;
104
+ }
105
+
106
+ if (matches.call(element, 'a[data-disable-with], button[data-disable-with], input[data-disable-with]')) {
107
+ message = element.getAttribute('data-disable-with');
108
+ if(!!element.value){
109
+ element.value = message;
110
+ }else{
111
+ element.innerHTML = message;
112
+ }
113
+ // timeout is needed because Safari stops the submit if the button is immediately disabled
114
+ setTimeout(function(){
115
+ element.setAttribute('disabled', 'disabled');
116
+ }, 0);
117
+ }
118
+ }, false);
119
+
120
+ //
121
+ // source: form.js
122
+ //
123
+ document.addEventListener('submit', function(event) {
124
+
125
+ var form = event.target;
126
+
127
+ if (matches.call(form, 'form[data-remote]')) {
128
+ var url = form.action;
129
+ var method = (form.method || form.getAttribute('data-method') || 'POST').toUpperCase();
130
+ var data = new FormData(form);
131
+
132
+ if (CSRF.param() && CSRF.token()) {
133
+ data[CSRF.param()] = CSRF.token();
134
+ }
135
+
136
+ if (LiteAjax.ajax({ url: url, method: method, data: data, target: form })){
137
+ event.preventDefault();
138
+ } else {
139
+ return true;
140
+ }
141
+ }
142
+ });
143
+
144
+ //
145
+ // source: liteajax.js
146
+ //
147
+ var LiteAjax = (function () {
148
+ var LiteAjax = {};
149
+
150
+ LiteAjax.options = {
151
+ method: 'GET',
152
+ url: window.location.href
153
+ };
154
+
155
+ LiteAjax.ajax = function (url, options) {
156
+ if (typeof url == 'object') {
157
+ options = url;
158
+ url = undefined;
159
+ }
160
+
161
+ options = options || {};
162
+
163
+ if(!options.accepts) {
164
+ options.accepts = 'text/javascript, application/javascript, ' +
165
+ 'application/ecmascript, application/x-ecmascript';
166
+ }
167
+
168
+ url = url || options.url || location.href || '';
169
+ var data = options.data;
170
+ var target = options.target || document;
171
+ var xhr = new XMLHttpRequest();
172
+
173
+ xhr.addEventListener('load', function () {
174
+ var responseType = xhr.getResponseHeader('content-type');
175
+ if(responseType === 'text/javascript; charset=utf-8') {
176
+ eval(xhr.response);
177
+ }
178
+
179
+ var event = new CustomEvent('ajax:complete', {detail: xhr, bubbles: true});
180
+ target.dispatchEvent(event);
181
+ });
182
+
183
+ if (typeof options.success == 'function')
184
+ xhr.addEventListener('load', function (event) {
185
+ if (xhr.status >= 200 && xhr.status < 300)
186
+ options.success(xhr);
187
+ });
188
+
189
+ if (typeof options.error == 'function') {
190
+ xhr.addEventListener('load', function (event) {
191
+ if (xhr.status < 200 || xhr.status >= 300)
192
+ options.error(xhr);
193
+ });
194
+ xhr.addEventListener('error', function (event) {
195
+ options.error(xhr);
196
+ });
197
+ }
198
+
199
+ xhr.open(options.method || 'GET', url);
200
+ xhr.setRequestHeader('X-Requested-With', 'XmlHttpRequest');
201
+ xhr.setRequestHeader('Accept', '*/*;q=0.5, ' + options.accepts);
202
+
203
+ if(options.json) {
204
+ xhr.setRequestHeader('Content-type', 'application/json');
205
+ data = JSON.stringify(data);
206
+ }
207
+
208
+ var beforeSend = new CustomEvent('ajax:before', {detail: xhr, bubbles: true});
209
+ target.dispatchEvent(beforeSend);
210
+ xhr.send(data);
211
+
212
+ return xhr;
213
+ };
214
+
215
+ return LiteAjax;
216
+ })();
217
+
218
+ //
219
+ // source: method.js
220
+ //
221
+ document.addEventListener('click', function(event) {
222
+ var element, url, method, data, handler;
223
+
224
+ // Only left click allowed. Firefox triggers click event on right click/contextmenu.
225
+ if (event.button !== 0) {
226
+ return;
227
+ }
228
+
229
+ element = event.target;
230
+
231
+ if (matches.call(element, 'a[data-method]')) {
232
+ url = element.getAttribute('href');
233
+ method = element.getAttribute('data-method').toUpperCase();
234
+ data = {};
235
+
236
+ if (CSRF.param() && CSRF.token()) {
237
+ data[CSRF.param()] = CSRF.token();
238
+ }
239
+
240
+ if (matches.call(element, 'a[data-remote]')) {
241
+ handler = xhr;
242
+ } else {
243
+ handler = submit;
244
+ }
245
+
246
+ if (handler({ url: url, method: method, data: data, target: element })) {
247
+ event.preventDefault();
248
+ } else {
249
+ return true;
250
+ }
251
+ }
252
+
253
+ function submit(options) {
254
+ var form, input, param;
255
+
256
+ if (options.method == 'GET') {
257
+ return false;
258
+ }
259
+
260
+ form = document.createElement('form');
261
+ form.method = 'POST';
262
+ form.action = options.url;
263
+ form.style.display = 'none';
264
+
265
+ for (param in options.data) {
266
+ if (Object.prototype.hasOwnProperty.call(options.data, param)) {
267
+ input = document.createElement('input');
268
+ input.setAttribute('type', 'hidden');
269
+ input.setAttribute('name', param);
270
+ input.setAttribute('value', options.data[param]);
271
+ form.appendChild(input);
272
+ }
273
+ }
274
+
275
+ if (options.method != 'POST') {
276
+ input = document.createElement('input');
277
+ input.setAttribute('type', 'hidden');
278
+ input.setAttribute('name', '_method');
279
+ input.setAttribute('value', options.method);
280
+ form.appendChild(input);
281
+ }
282
+
283
+ document.body.appendChild(form);
284
+ form.submit();
285
+ return true;
286
+ }
287
+
288
+ function xhr(options) {
289
+ LiteAjax.ajax(options);
290
+ return true;
291
+ }
292
+ }, false);
293
+
294
+ //
295
+ // source: polyfills.js
296
+ //
297
+ var matches = (function(doc) {
298
+ return doc.matchesSelector ||
299
+ doc.webkitMatchesSelector ||
300
+ doc.mozMatchesSelector ||
301
+ doc.oMatchesSelector ||
302
+ doc.msMatchesSelector;
303
+ })(document.documentElement);
304
+
305
+ var CustomEvent = function (event, params) {
306
+ params = params || {bubbles: false, cancelable: false, detail: undefined};
307
+ var evt = document.createEvent('CustomEvent');
308
+ evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
309
+ return evt;
310
+ };
311
+
312
+ CustomEvent.prototype = window.CustomEvent.prototype;
313
+
314
+ window.CustomEvent = CustomEvent;
@@ -0,0 +1 @@
1
+ document.addEventListener("click",function(t){var e,a;if(a=t.target,matches.call(a,"a[data-confirm], button[data-confirm], input[data-confirm]"))return e=a.getAttribute("data-confirm"),confirm(e)?void 0:(t.stopPropagation(),t.stopImmediatePropagation(),t.preventDefault(),!1)},!1);var CSRF={token:function(){var t=document.querySelector('meta[name="csrf-token"]');return t&&t.getAttribute("content")},param:function(){var t=document.querySelector('meta[name="csrf-param"]');return t&&t.getAttribute("content")}},sameOrigin=function(t){var e,a=document.createElement("a");return a.href=t,e=a.href.split("/",3).join("/"),0===window.location.href.indexOf(e)};window.CSRF=CSRF,document.addEventListener("ajax:before",function(t){var e=CSRF.token(),a=t.detail;e&&a.setRequestHeader("X-CSRF-Token",e)}),document.addEventListener("submit",function(t){var e=CSRF.token(),a=CSRF.param(),n=t.target;if(matches.call(n,"form")){if(matches.call(n,"form[data-remote]"))return!0;if(!n.method||"GET"==n.method.toUpperCase())return!0;if(!sameOrigin(n.action))return!0;if(a&&e&&!n.querySelector("input[name="+a+"]")){var r=document.createElement("input");r.setAttribute("type","hidden"),r.setAttribute("name",a),r.setAttribute("value",e),n.appendChild(r)}return!0}}),document.addEventListener("click",function(t){var e,a;3!=t.which&&("submit"===(a=t.target).getAttribute("type")&&null!==a.form.querySelector(":invalid")||matches.call(a,"a[data-disable-with], button[data-disable-with], input[data-disable-with]")&&(e=a.getAttribute("data-disable-with"),a.value?a.value=e:a.innerHTML=e,setTimeout(function(){a.setAttribute("disabled","disabled")},0)))},!1),document.addEventListener("submit",function(t){var e=t.target;if(matches.call(e,"form[data-remote]")){var a=e.action,n=(e.method||e.getAttribute("data-method")||"POST").toUpperCase(),r=new FormData(e);if(CSRF.param()&&CSRF.token()&&(r[CSRF.param()]=CSRF.token()),!LiteAjax.ajax({url:a,method:n,data:r,target:e}))return!0;t.preventDefault()}});var LiteAjax=function(){var LiteAjax={};return LiteAjax.options={method:"GET",url:window.location.href},LiteAjax.ajax=function(url,options){"object"==typeof url&&(options=url,url=void 0),options=options||{},options.accepts||(options.accepts="text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"),url=url||options.url||location.href||"";var data=options.data,target=options.target||document,xhr=new XMLHttpRequest;xhr.addEventListener("load",function(){var responseType=xhr.getResponseHeader("content-type");"text/javascript; charset=utf-8"===responseType&&eval(xhr.response);var event=new CustomEvent("ajax:complete",{detail:xhr,bubbles:!0});target.dispatchEvent(event)}),"function"==typeof options.success&&xhr.addEventListener("load",function(t){xhr.status>=200&&xhr.status<300&&options.success(xhr)}),"function"==typeof options.error&&(xhr.addEventListener("load",function(t){(xhr.status<200||xhr.status>=300)&&options.error(xhr)}),xhr.addEventListener("error",function(t){options.error(xhr)})),xhr.open(options.method||"GET",url),xhr.setRequestHeader("X-Requested-With","XmlHttpRequest"),xhr.setRequestHeader("Accept","*/*;q=0.5, "+options.accepts),options.json&&(xhr.setRequestHeader("Content-type","application/json"),data=JSON.stringify(data));var beforeSend=new CustomEvent("ajax:before",{detail:xhr,bubbles:!0});return target.dispatchEvent(beforeSend),xhr.send(data),xhr},LiteAjax}();document.addEventListener("click",function(t){var e,a,n,r;if(0===t.button&&(e=t.target,matches.call(e,"a[data-method]"))){if(a=e.getAttribute("href"),n=e.getAttribute("data-method").toUpperCase(),r={},CSRF.param()&&CSRF.token()&&(r[CSRF.param()]=CSRF.token()),!(matches.call(e,"a[data-remote]")?function(t){return LiteAjax.ajax(t),!0}:function(t){var e,a,n;if("GET"==t.method)return!1;for(n in(e=document.createElement("form")).method="POST",e.action=t.url,e.style.display="none",t.data)Object.prototype.hasOwnProperty.call(t.data,n)&&((a=document.createElement("input")).setAttribute("type","hidden"),a.setAttribute("name",n),a.setAttribute("value",t.data[n]),e.appendChild(a));"POST"!=t.method&&((a=document.createElement("input")).setAttribute("type","hidden"),a.setAttribute("name","_method"),a.setAttribute("value",t.method),e.appendChild(a));return document.body.appendChild(e),e.submit(),!0})({url:a,method:n,data:r,target:e}))return!0;t.preventDefault()}},!1);var matches=function(t){return t.matchesSelector||t.webkitMatchesSelector||t.mozMatchesSelector||t.oMatchesSelector||t.msMatchesSelector}(document.documentElement),CustomEvent=function(t,e){e=e||{bubbles:!1,cancelable:!1,detail:void 0};var a=document.createEvent("CustomEvent");return a.initCustomEvent(t,e.bubbles,e.cancelable,e.detail),a};CustomEvent.prototype=window.CustomEvent.prototype,window.CustomEvent=CustomEvent;
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module UJS
5
+ # @since 0.1.0
6
+ VERSION = "0.1.0.beta1"
7
+ end
8
+ end
data/lib/hanami/ujs.rb ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ module Hanami
6
+ # Unobtrusive JavaScript
7
+ #
8
+ # @since 0.1.0
9
+ module UJS
10
+ require "hanami/ujs/version"
11
+ end
12
+ end
13
+
14
+ if defined?(Hanami::Assets)
15
+ Hanami::Assets.sources << Pathname.new(__dir__).join("ujs", "assets").realpath
16
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hanami-ujs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.beta1
5
+ platform: ruby
6
+ authors:
7
+ - Luca Guidi
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-02-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '12.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '12.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.7'
55
+ description: Hanami Unobtrusive JavaScript
56
+ email:
57
+ - me@lucaguidi.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - CHANGELOG.md
63
+ - README.md
64
+ - hanami-ujs.gemspec
65
+ - lib/hanami/ujs.rb
66
+ - lib/hanami/ujs/assets/javascripts/hanami-ujs.js
67
+ - lib/hanami/ujs/assets/javascripts/hanami-ujs.min.js
68
+ - lib/hanami/ujs/version.rb
69
+ homepage: http://hanamirb.org
70
+ licenses:
71
+ - MIT
72
+ metadata:
73
+ allowed_push_host: https://rubygems.org
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">"
86
+ - !ruby/object:Gem::Version
87
+ version: 1.3.1
88
+ requirements: []
89
+ rubyforge_project:
90
+ rubygems_version: 2.7.5
91
+ signing_key:
92
+ specification_version: 4
93
+ summary: Hanami UJS
94
+ test_files: []