requestjs-rails 0.0.5

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: e9916bd029b2135b5211b19d728b8f046ac451c69796b25aaf97e5cf003a90ca
4
+ data.tar.gz: 6ff947cb98c9692d7da177fcb99a3c2b5cc9804c7cb34b78265a1bd532077345
5
+ SHA512:
6
+ metadata.gz: 986c4734b1ce9a324dace368d029a4bb5dea55dfe9a00265cdb0ffd29aa0a53b235cf4d0b37a3110bee1795b4116aa3667cb5ffe47354bc46ee25f44496e1983
7
+ data.tar.gz: 96238d856e7b6ae9c1c27495a1ab842ca51115642dedef49895e9d87fa46376c0892daa05f79b412f202332774b7ead4bdae2a729505d618eef75e18ac7b38da
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2021 Marcelo Lauxen
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.
data/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # Request.JS for Rails
2
+
3
+ [Rails Request.JS](https://github.com/rails/request.js) encapsulates the logic to send by default some headers that are required by rails applications like the `X-CSRF-Token`.
4
+
5
+ ## Installation
6
+
7
+ 1. Add the `requestjs-rails` gem to your Gemfile: `gem 'requestjs-rails'`
8
+ 2. Run `./bin/bundle install`.
9
+ 3. Run `./bin/rails requestjs:install`
10
+
11
+ If using the asset pipeline to manage JavaScript, the last command will:
12
+
13
+ - Append import "@rails/request.js" to your app/assets/javascripts/application.js entrypoint.
14
+
15
+ Make sure you've already installed `importmap-rails` and that it's referenced before `requestjs-rails` in your Gemfile.
16
+
17
+ If using Webpacker to manage JavaScript, the last command will:
18
+
19
+ - Install the Request.JS NPM package.
20
+
21
+ ## Usage
22
+
23
+ With the installation done check the documentation in the [Rails Request.JS](https://github.com/rails/request.js#how-to-use) repository.
24
+
25
+ ## License
26
+
27
+ Request.JS for Rails is released under the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require "bundler/setup"
2
+
3
+ require "bundler/gem_tasks"
4
+
5
+ require "rake/testtask"
6
+
7
+ Rake::TestTask.new(:test) do |t|
8
+ t.libs << 'test'
9
+ t.pattern = 'test/**/*_test.rb'
10
+ t.verbose = false
11
+ end
12
+
13
+ task default: :test
@@ -0,0 +1 @@
1
+ //= require ./requestjs.js
@@ -0,0 +1,257 @@
1
+ class FetchResponse {
2
+ constructor(response) {
3
+ this.response = response;
4
+ }
5
+ get statusCode() {
6
+ return this.response.status;
7
+ }
8
+ get redirected() {
9
+ return this.response.redirected;
10
+ }
11
+ get ok() {
12
+ return this.response.ok;
13
+ }
14
+ get unauthenticated() {
15
+ return this.statusCode === 401;
16
+ }
17
+ get authenticationURL() {
18
+ return this.response.headers.get("WWW-Authenticate");
19
+ }
20
+ get contentType() {
21
+ const contentType = this.response.headers.get("Content-Type") || "";
22
+ return contentType.replace(/;.*$/, "");
23
+ }
24
+ get headers() {
25
+ return this.response.headers;
26
+ }
27
+ get html() {
28
+ if (this.contentType.match(/^(application|text)\/(html|xhtml\+xml)$/)) {
29
+ return this.text;
30
+ }
31
+ return Promise.reject(new Error(`Expected an HTML response but got "${this.contentType}" instead`));
32
+ }
33
+ get json() {
34
+ if (this.contentType.match(/^application\/json/)) {
35
+ return this.responseJson || (this.responseJson = this.response.json());
36
+ }
37
+ return Promise.reject(new Error(`Expected a JSON response but got "${this.contentType}" instead`));
38
+ }
39
+ get text() {
40
+ return this.responseText || (this.responseText = this.response.text());
41
+ }
42
+ get isTurboStream() {
43
+ return this.contentType.match(/^text\/vnd\.turbo-stream\.html/);
44
+ }
45
+ async renderTurboStream() {
46
+ if (this.isTurboStream) {
47
+ if (window.Turbo) {
48
+ window.Turbo.renderStreamMessage(await this.text);
49
+ } else {
50
+ console.warn("You must set `window.Turbo = Turbo` to automatically process Turbo Stream events with request.js");
51
+ }
52
+ } else {
53
+ return Promise.reject(new Error(`Expected a Turbo Stream response but got "${this.contentType}" instead`));
54
+ }
55
+ }
56
+ }
57
+
58
+ class RequestInterceptor {
59
+ static register(interceptor) {
60
+ this.interceptor = interceptor;
61
+ }
62
+ static get() {
63
+ return this.interceptor;
64
+ }
65
+ static reset() {
66
+ this.interceptor = undefined;
67
+ }
68
+ }
69
+
70
+ function getCookie(name) {
71
+ const cookies = document.cookie ? document.cookie.split("; ") : [];
72
+ const prefix = `${encodeURIComponent(name)}=`;
73
+ const cookie = cookies.find((cookie => cookie.startsWith(prefix)));
74
+ if (cookie) {
75
+ const value = cookie.split("=").slice(1).join("=");
76
+ if (value) {
77
+ return decodeURIComponent(value);
78
+ }
79
+ }
80
+ }
81
+
82
+ function compact(object) {
83
+ const result = {};
84
+ for (const key in object) {
85
+ const value = object[key];
86
+ if (value !== undefined) {
87
+ result[key] = value;
88
+ }
89
+ }
90
+ return result;
91
+ }
92
+
93
+ function metaContent(name) {
94
+ const element = document.head.querySelector(`meta[name="${name}"]`);
95
+ return element && element.content;
96
+ }
97
+
98
+ function stringEntriesFromFormData(formData) {
99
+ return [ ...formData ].reduce(((entries, [name, value]) => entries.concat(typeof value === "string" ? [ [ name, value ] ] : [])), []);
100
+ }
101
+
102
+ function mergeEntries(searchParams, entries) {
103
+ for (const [name, value] of entries) {
104
+ if (value instanceof window.File) continue;
105
+ if (searchParams.has(name)) {
106
+ searchParams.delete(name);
107
+ searchParams.set(name, value);
108
+ } else {
109
+ searchParams.append(name, value);
110
+ }
111
+ }
112
+ }
113
+
114
+ class FetchRequest {
115
+ constructor(method, url, options = {}) {
116
+ this.method = method;
117
+ this.options = options;
118
+ this.originalUrl = url;
119
+ }
120
+ async perform() {
121
+ try {
122
+ const requestInterceptor = RequestInterceptor.get();
123
+ if (requestInterceptor) {
124
+ await requestInterceptor(this);
125
+ }
126
+ } catch (error) {
127
+ console.error(error);
128
+ }
129
+ const response = new FetchResponse(await window.fetch(this.url, this.fetchOptions));
130
+ if (response.unauthenticated && response.authenticationURL) {
131
+ return Promise.reject(window.location.href = response.authenticationURL);
132
+ }
133
+ if (response.ok && response.isTurboStream) {
134
+ response.renderTurboStream();
135
+ }
136
+ return response;
137
+ }
138
+ addHeader(key, value) {
139
+ const headers = this.additionalHeaders;
140
+ headers[key] = value;
141
+ this.options.headers = headers;
142
+ }
143
+ get fetchOptions() {
144
+ return {
145
+ method: this.method.toUpperCase(),
146
+ headers: this.headers,
147
+ body: this.formattedBody,
148
+ signal: this.signal,
149
+ credentials: "same-origin",
150
+ redirect: this.redirect
151
+ };
152
+ }
153
+ get headers() {
154
+ return compact(Object.assign({
155
+ "X-Requested-With": "XMLHttpRequest",
156
+ "X-CSRF-Token": this.csrfToken,
157
+ "Content-Type": this.contentType,
158
+ Accept: this.accept
159
+ }, this.additionalHeaders));
160
+ }
161
+ get csrfToken() {
162
+ return getCookie(metaContent("csrf-param")) || metaContent("csrf-token");
163
+ }
164
+ get contentType() {
165
+ if (this.options.contentType) {
166
+ return this.options.contentType;
167
+ } else if (this.body == null || this.body instanceof window.FormData) {
168
+ return undefined;
169
+ } else if (this.body instanceof window.File) {
170
+ return this.body.type;
171
+ }
172
+ return "application/json";
173
+ }
174
+ get accept() {
175
+ switch (this.responseKind) {
176
+ case "html":
177
+ return "text/html, application/xhtml+xml";
178
+
179
+ case "turbo-stream":
180
+ return "text/vnd.turbo-stream.html, text/html, application/xhtml+xml";
181
+
182
+ case "json":
183
+ return "application/json";
184
+
185
+ default:
186
+ return "*/*";
187
+ }
188
+ }
189
+ get body() {
190
+ return this.options.body;
191
+ }
192
+ get query() {
193
+ const originalQuery = (this.originalUrl.split("?")[1] || "").split("#")[0];
194
+ const params = new URLSearchParams(originalQuery);
195
+ let requestQuery = this.options.query;
196
+ if (requestQuery instanceof window.FormData) {
197
+ requestQuery = stringEntriesFromFormData(requestQuery);
198
+ } else if (requestQuery instanceof window.URLSearchParams) {
199
+ requestQuery = requestQuery.entries();
200
+ } else {
201
+ requestQuery = Object.entries(requestQuery || {});
202
+ }
203
+ mergeEntries(params, requestQuery);
204
+ const query = params.toString();
205
+ return query.length > 0 ? `?${query}` : "";
206
+ }
207
+ get url() {
208
+ return this.originalUrl.split("?")[0] + this.query;
209
+ }
210
+ get responseKind() {
211
+ return this.options.responseKind || "html";
212
+ }
213
+ get signal() {
214
+ return this.options.signal;
215
+ }
216
+ get redirect() {
217
+ return this.options.redirect || "follow";
218
+ }
219
+ get additionalHeaders() {
220
+ return this.options.headers || {};
221
+ }
222
+ get formattedBody() {
223
+ const bodyIsAString = Object.prototype.toString.call(this.body) === "[object String]";
224
+ const contentTypeIsJson = this.headers["Content-Type"] === "application/json";
225
+ if (contentTypeIsJson && !bodyIsAString) {
226
+ return JSON.stringify(this.body);
227
+ }
228
+ return this.body;
229
+ }
230
+ }
231
+
232
+ async function get(url, options) {
233
+ const request = new FetchRequest("get", url, options);
234
+ return request.perform();
235
+ }
236
+
237
+ async function post(url, options) {
238
+ const request = new FetchRequest("post", url, options);
239
+ return request.perform();
240
+ }
241
+
242
+ async function put(url, options) {
243
+ const request = new FetchRequest("put", url, options);
244
+ return request.perform();
245
+ }
246
+
247
+ async function patch(url, options) {
248
+ const request = new FetchRequest("patch", url, options);
249
+ return request.perform();
250
+ }
251
+
252
+ async function destroy(url, options) {
253
+ const request = new FetchRequest("delete", url, options);
254
+ return request.perform();
255
+ }
256
+
257
+ export { FetchRequest, FetchResponse, RequestInterceptor, destroy, get, patch, post, put };
@@ -0,0 +1,4 @@
1
+ APP_JS_ROOT = Rails.root.join("app/assets/javascripts")
2
+
3
+ say "Import Request.JS in existing app/assets/javascripts/application.js"
4
+ append_to_file APP_JS_ROOT.join("application.js"), %(import "@rails/request.js"\n)
@@ -0,0 +1,2 @@
1
+ say "Installing Request.JS dependency"
2
+ run "yarn add @rails/request.js"
@@ -0,0 +1,5 @@
1
+ module Requestjs
2
+ end
3
+
4
+ require "requestjs/version"
5
+ require "requestjs/engine"
@@ -0,0 +1,17 @@
1
+ module Requestjs
2
+ class Engine < ::Rails::Engine
3
+ initializer "requestjs.assets" do
4
+ if Rails.application.config.respond_to?(:assets)
5
+ Rails.application.config.assets.precompile += %w( rails-requestjs )
6
+ end
7
+ end
8
+
9
+ initializer "requestjs.importmap" do
10
+ if Rails.application.config.respond_to?(:importmap)
11
+ Rails.application.config.importmap.paths.tap do |paths|
12
+ paths.asset "@rails/request.js", path: "rails-requestjs"
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module Requestjs
2
+ VERSION = "0.0.5"
3
+ end
@@ -0,0 +1,28 @@
1
+ def run_requestjs_install_template(path)
2
+ system "#{RbConfig.ruby} ./bin/rails app:template LOCATION=#{File.expand_path("../install/#{path}.rb", __dir__)}"
3
+ end
4
+
5
+ namespace :requestjs do
6
+ desc "Install Request.JS into the app"
7
+ task :install do
8
+ if defined?(Webpacker::Engine)
9
+ Rake::Task["requestjs:install:webpacker"].invoke
10
+ elsif defined?(Importmap)
11
+ Rake::Task["requestjs:install:asset_pipeline"].invoke
12
+ else
13
+ puts "You must either be running Webpacker or importmap-rails to use this gem."
14
+ end
15
+ end
16
+
17
+ namespace :install do
18
+ desc "Install Request.JS on the app with the asset pipeline"
19
+ task :asset_pipeline do
20
+ run_requestjs_install_template "requestjs_with_asset_pipeline"
21
+ end
22
+
23
+ desc "Install Request.JS on the app with webpacker"
24
+ task :webpacker do
25
+ run_requestjs_install_template "requestjs_with_webpacker"
26
+ end
27
+ end
28
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: requestjs-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ platform: ruby
6
+ authors:
7
+ - Marcelo Lauxen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-08-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 6.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 6.0.0
27
+ description:
28
+ email: marcelolauxen16@gmail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - MIT-LICENSE
34
+ - README.md
35
+ - Rakefile
36
+ - app/assets/javascripts/rails-requestjs.js
37
+ - app/assets/javascripts/requestjs.js
38
+ - lib/install/requestjs_with_asset_pipeline.rb
39
+ - lib/install/requestjs_with_webpacker.rb
40
+ - lib/requestjs-rails.rb
41
+ - lib/requestjs/engine.rb
42
+ - lib/requestjs/version.rb
43
+ - lib/tasks/requestjs_tasks.rake
44
+ homepage: https://github.com/marcelolx/requestjs-rails
45
+ licenses:
46
+ - MIT
47
+ metadata: {}
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubygems_version: 3.2.19
64
+ signing_key:
65
+ specification_version: 4
66
+ summary: A tiny Fetch API wrapper that allows you to make http requests without need
67
+ to handle to send the CSRF Token on every request
68
+ test_files: []