diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..70cb8ed --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,32 @@ +# The behavior of RuboCop can be controlled via the .rubocop.yml +# configuration file. It makes it possible to enable/disable +# certain cops (checks) and to alter their behavior if they accept +# any parameters. The file can be placed either in your home +# directory or in some project directory. +# +# RuboCop will start looking for the configuration file in the directory +# where the inspected file is and continue its way up to the root directory. +# +# See https://github.com/rubocop-hq/rubocop/blob/master/manual/configuration.md +# +require: + - rubocop-rails + - rubocop-rspec + + +AllCops: + NewCops: enable + Exclude: + - 'bin/*' + - 'db/migrate/*' + - 'db/schema.rb' + - 'vendor/bundle/**/*' + - 'node_modules/**/*' + + +Layout/LineLength: + AllowHeredoc: true + Max: 150 + +Style/Documentation: + Enabled: false diff --git a/Gemfile b/Gemfile index 2293fd7..032d01e 100644 --- a/Gemfile +++ b/Gemfile @@ -1,15 +1,17 @@ +# frozen_string_literal: true + source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } ruby '2.6.1' -gem 'rails' +gem 'jbuilder' gem 'pg' gem 'puma' +gem 'rails' gem 'sass-rails' -gem 'webpacker' gem 'turbolinks' -gem 'jbuilder' +gem 'webpacker' # Use Redis adapter to run Action Cable in production # gem 'redis', '~> 4.0' # Use Active Model has_secure_password @@ -23,8 +25,8 @@ gem 'bootsnap', '>= 1.4.2', require: false gem 'lockbox' -gem 'aws-sdk-s3', require: false gem 'airrecord' +gem 'aws-sdk-s3', require: false gem 'google-api-client' gem 'rack-cors' gem 'sentry-raven' @@ -34,8 +36,11 @@ gem 'pagy' group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console - gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] + gem 'byebug', platforms: %i[mri mingw x64_mingw] gem 'dotenv-rails' + gem 'rubocop', require: false + gem 'rubocop-rails', require: false + gem 'rubocop-rspec', require: false end group :development do @@ -57,4 +62,4 @@ group :test do end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] +gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby] diff --git a/Gemfile.lock b/Gemfile.lock index 3dca72a..afd19f8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -61,6 +61,7 @@ GEM airrecord (1.0.5) faraday (>= 0.10, < 2.0) net-http-persistent (>= 2.9) + ast (2.4.0) aws-eventstream (1.0.3) aws-partitions (1.263.0) aws-sdk-core (3.89.1) @@ -115,6 +116,7 @@ GEM httpclient (2.8.3) i18n (1.8.2) concurrent-ruby (~> 1.0) + jaro_winkler (1.5.4) jbuilder (2.10.0) activesupport (>= 5.0.0) jmespath (1.4.0) @@ -154,6 +156,9 @@ GEM rack (>= 1.2, < 3) os (1.1.0) pagy (3.8.0) + parallel (1.19.1) + parser (2.7.1.1) + ast (~> 2.4.0) pg (1.2.3) public_suffix (4.0.4) puma (4.3.3) @@ -191,6 +196,7 @@ GEM method_source rake (>= 0.8.7) thor (>= 0.20.3, < 2.0) + rainbow (3.0.0) rake (13.0.1) rb-fsevent (0.10.3) rb-inotify (0.10.1) @@ -200,6 +206,22 @@ GEM declarative-option (< 0.2.0) uber (< 0.2.0) retriable (3.1.2) + rexml (3.2.4) + rubocop (0.82.0) + jaro_winkler (~> 1.5.1) + parallel (~> 1.10) + parser (>= 2.7.0.1) + rainbow (>= 2.2.2, < 4.0) + rexml + ruby-progressbar (~> 1.7) + unicode-display_width (>= 1.4.0, < 2.0) + rubocop-rails (2.5.2) + activesupport + rack (>= 1.1) + rubocop (>= 0.72.0) + rubocop-rspec (1.38.1) + rubocop (>= 0.68.1) + ruby-progressbar (1.10.1) sass-rails (6.0.0) sassc-rails (~> 2.1, >= 2.1.1) sassc (2.2.1) @@ -246,6 +268,7 @@ GEM tzinfo (1.2.7) thread_safe (~> 0.1) uber (0.1.0) + unicode-display_width (1.7.0) webpacker (5.0.1) activesupport (>= 5.2) rack-proxy (>= 0.6.1) @@ -274,6 +297,9 @@ DEPENDENCIES puma rack-cors rails + rubocop + rubocop-rails + rubocop-rspec sass-rails sentry-raven sequenced diff --git a/Rakefile b/Rakefile index e85f913..488c551 100644 --- a/Rakefile +++ b/Rakefile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index c19aef5..e13e405 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -28,7 +28,7 @@ $weight-bold: 400; @import "checkmark-icon"; @import 'demo'; -@import 'highlight'; +@import 'prism'; .field_with_errors input { border-color: $red; diff --git a/app/assets/stylesheets/highlight.css.scss b/app/assets/stylesheets/highlight.css.scss deleted file mode 100644 index 87e256f..0000000 --- a/app/assets/stylesheets/highlight.css.scss +++ /dev/null @@ -1,5 +0,0 @@ -code.html .hljs-tag:first-child .hljs-attr, code.html .hljs-tag:first-child .hljs-string { - font-weight: bold; - font-size: 1.2em; -} - diff --git a/app/assets/stylesheets/prism.css.scss b/app/assets/stylesheets/prism.css.scss new file mode 100644 index 0000000..00a575a --- /dev/null +++ b/app/assets/stylesheets/prism.css.scss @@ -0,0 +1,131 @@ +/* PrismJS 1.20.0 +https://prismjs.com/download.html#themes=prism-okaidia&languages=markup+css+clike+javascript&plugins=custom-class */ +/** + * okaidia theme for JavaScript, CSS and HTML + * Loosely based on Monokai textmate theme by http://www.monokai.nl/ + * @author ocodia + */ + +code.language-html > .prism-tag:first-child { + font-weight: bold; + font-size: 1.1em; +} + +code[class*="language-"], +pre[class*="language-"] { + color: #f8f8f2; + background: none; + text-shadow: 0 1px rgba(0, 0, 0, 0.3); + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + font-size: 1em; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; + border-radius: 0.3em; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #272822; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.prism-token.prism-comment, +.prism-token.prism-prolog, +.prism-token.prism-doctype, +.prism-token.prism-cdata { + color: slategray; +} + +.prism-token.prism-punctuation { + color: #f8f8f2; +} + +.prism-token.prism-namespace { + opacity: .7; +} + +.prism-token.prism-property, +.prism-token.prism-tag, +.prism-token.prism-constant, +.prism-token.prism-symbol, +.prism-token.prism-deleted { + color: #f92672; +} + +.prism-token.prism-boolean, +.prism-token.prism-number { + color: #ae81ff; +} + +.prism-token.prism-selector, +.prism-token.prism-attr-name, +.prism-token.prism-string, +.prism-token.prism-char, +.prism-token.prism-builtin, +.prism-token.prism-inserted { + color: #a6e22e; +} + +.prism-token.prism-operator, +.prism-token.prism-entity, +.prism-token.prism-url, +.prism-language-css .prism-token.prism-string, +.prism-style .prism-token.prism-string, +.prism-token.prism-variable { + color: #f8f8f2; +} + +.prism-token.prism-atrule, +.prism-token.prism-attr-value, +.prism-token.prism-function, +.prism-token.prism-class-name { + color: #e6db74; +} + +.prism-token.prism-keyword { + color: #66d9ef; +} + +.prism-token.prism-regex, +.token.important { + color: #fd971f; +} + +.prism-token.prism-important, +.prism-token.prism-bold { + font-weight: bold; +} +.prism-token.prism-italic { + font-style: italic; +} + +.prism-token.prism-entity { + cursor: help; +} + diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb index d672697..9aec230 100644 --- a/app/channels/application_cable/channel.rb +++ b/app/channels/application_cable/channel.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ApplicationCable class Channel < ActionCable::Channel::Base end diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb index 0ff5442..8d6c2a1 100644 --- a/app/channels/application_cable/connection.rb +++ b/app/channels/application_cable/connection.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ApplicationCable class Connection < ActionCable::Connection::Base end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 569d38a..dcb873a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + class ApplicationController < ActionController::Base helper_method :current_user, :logged_in? include Pagy::Backend def require_login - redirect_to login_url unless current_user.present? + redirect_to login_url if current_user.blank? end def current_user diff --git a/app/controllers/file_uploads_controller.rb b/app/controllers/file_uploads_controller.rb index ad487ca..c333313 100644 --- a/app/controllers/file_uploads_controller.rb +++ b/app/controllers/file_uploads_controller.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class FileUploadsController < ApplicationController def show @form = Form.find_by!(token: params[:form_id]) diff --git a/app/controllers/forms_controller.rb b/app/controllers/forms_controller.rb index 7cd7f96..07fb203 100644 --- a/app/controllers/forms_controller.rb +++ b/app/controllers/forms_controller.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'google/apis/sheets_v4' require 'google/api_client/client_secrets' class FormsController < ApplicationController diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index eb9edfa..1577715 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -1,5 +1,6 @@ -class HomeController < ApplicationController +# frozen_string_literal: true +class HomeController < ApplicationController def index redirect_to forms_url if logged_in? end diff --git a/app/controllers/oauths_controller.rb b/app/controllers/oauths_controller.rb index a7e7f23..7cbffc5 100644 --- a/app/controllers/oauths_controller.rb +++ b/app/controllers/oauths_controller.rb @@ -1,5 +1,6 @@ -class OauthsController < ApplicationController +# frozen_string_literal: true +class OauthsController < ApplicationController # Sends the user on a trip to the provider, # and after authorizing there back to the callback url. def oauth @@ -9,26 +10,25 @@ class OauthsController < ApplicationController def callback provider = params[:provider] if @user = login_from(provider) - redirect_to root_path, :notice => "Logged in from #{provider.titleize}!" + redirect_to root_path, notice: "Logged in from #{provider.titleize}!" else begin @user = create_from(provider) if authentication = @user.authentications.find_by(provider: provider) authentication.update({ - access_token: @access_token.token, - refresh_token: @access_token.refresh_token, - expires_at: Time.at(@access_token.expires_at) - }) + access_token: @access_token.token, + refresh_token: @access_token.refresh_token, + expires_at: Time.zone.at(@access_token.expires_at) + }) end reset_session auto_login(@user) - redirect_to root_path, :notice => "Logged in from #{provider.titleize}!" - rescue + redirect_to root_path, notice: "Logged in from #{provider.titleize}!" + rescue StandardError Rails.logger.error("Failed to login from #{provider}") - redirect_to root_path, :alert => "Failed to login from #{provider.titleize}!" + redirect_to root_path, alert: "Failed to login from #{provider.titleize}!" end end end - end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 9265f5a..ebb987b 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,5 +1,6 @@ -class SessionsController < ApplicationController +# frozen_string_literal: true +class SessionsController < ApplicationController def new reset_session end @@ -8,5 +9,4 @@ class SessionsController < ApplicationController reset_session redirect_to root_url end - end diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index 262ac9b..3c7f792 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'google/apis/sheets_v4' class SubmissionsController < ApplicationController skip_before_action :verify_authenticity_token @@ -14,10 +16,14 @@ class SubmissionsController < ApplicationController respond_to do |format| if @submission.save - format.html { redirect_to(@form.thank_you_url) if @form.thank_you_url.present? } + format.html do + redirect_to(@form.thank_you_url) if @form.thank_you_url.present? + end format.json { render(json: { success: true, submission: @submission.data }) } else - format.html { redirect_to(@form.thank_you_url) if @form.thank_you_url.present? } + format.html do + redirect_to(@form.thank_you_url) if @form.thank_you_url.present? + end format.json { render(json: { error: @submission.errors }, status: 422) } end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 41244f6..c2ebd8c 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ApplicationHelper include Pagy::Frontend end diff --git a/app/javascript/highlight.js b/app/javascript/highlight.js deleted file mode 100644 index e48cb54..0000000 --- a/app/javascript/highlight.js +++ /dev/null @@ -1,7 +0,0 @@ -document.addEventListener("turbolinks:load", function () { - document.querySelectorAll('pre code').forEach(function (block) { - if (window.hljs) { - hljs.highlightBlock(block); - } - }); -}); diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index 65acde0..c461d69 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -13,7 +13,7 @@ require('tinyforms'); require('demo'); require('backend_typed'); require('tabs'); -require('highlight'); +require('prism'); // Uncomment to copy all static images under ../images to the output folder and reference // them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>) diff --git a/app/javascript/prism.js b/app/javascript/prism.js new file mode 100644 index 0000000..4b7d153 --- /dev/null +++ b/app/javascript/prism.js @@ -0,0 +1,17 @@ +window.Prism = window.Prism || {}; +window.Prism.manual = true; +document.addEventListener("turbolinks:load", function () { + Prism.plugins.customClass.prefix('prism-'); + document.querySelectorAll('pre code').forEach(function (el) { + Prism.highlightElement(el); + }); +}); + +/* PrismJS 1.20.0 +https://prismjs.com/download.html#themes=prism-okaidia&languages=markup+css+clike+javascript&plugins=custom-class */ +var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(u){var c=/\blang(?:uage)?-([\w-]+)\b/i,n=0,C={manual:u.Prism&&u.Prism.manual,disableWorkerMessageHandler:u.Prism&&u.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof _?new _(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/n.length)return;if(!(b instanceof _)){var x=1;if(d&&y!=t.tail.prev){g.lastIndex=k;var w=g.exec(n);if(!w)break;var A=w.index+(h&&w[1]?w[1].length:0),P=w.index+w[0].length,S=k;for(S+=y.value.length;S<=A;)y=y.next,S+=y.value.length;if(S-=y.value.length,k=S,y.value instanceof _)continue;for(var O=y;O!==t.tail&&(S"+a.content+""},!u.document)return u.addEventListener&&(C.disableWorkerMessageHandler||u.addEventListener("message",function(e){var n=JSON.parse(e.data),t=n.language,r=n.code,a=n.immediateClose;u.postMessage(C.highlight(r,C.languages[t],t)),a&&u.close()},!1)),C;var e=C.util.currentScript();function t(){C.manual||C.highlightAll()}if(e&&(C.filename=e.src,e.hasAttribute("data-manual")&&(C.manual=!0)),!C.manual){var r=document.readyState;"loading"===r||"interactive"===r&&e&&e.defer?document.addEventListener("DOMContentLoaded",t):window.requestAnimationFrame?window.requestAnimationFrame(t):window.setTimeout(t,16)}return C}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); +Prism.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0},cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/i,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/i,inside:{punctuation:[/^=/,{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),Object.defineProperty(Prism.languages.markup.tag,"addInlined",{value:function(a,e){var s={};s["language-"+e]={pattern:/(^$)/i,lookbehind:!0,inside:Prism.languages[e]},s.cdata=/^$/i;var n={"included-cdata":{pattern://i,inside:s}};n["language-"+e]={pattern:/[\s\S]+/,inside:Prism.languages[e]};var t={};t[a]={pattern:RegExp("(<__[^]*?>)(?:))*\\]\\]>|(?!)".replace(/__/g,function(){return a}),"i"),lookbehind:!0,greedy:!0,inside:n},Prism.languages.insertBefore("markup","cdata",t)}}),Prism.languages.xml=Prism.languages.extend("markup",{}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup; +!function(s){var e=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;s.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+[\s\S]*?(?:;|(?=\s*\{))/,inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\((?!\s*\))\s*)(?:[^()]|\((?:[^()]|\([^()]*\))*\))+?(?=\s*\))/,lookbehind:!0,alias:"selector"}}},url:{pattern:RegExp("url\\((?:"+e.source+"|[^\n\r()]*)\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/}},selector:RegExp("[^{}\\s](?:[^{};\"']|"+e.source+")*?(?=\\s*\\{)"),string:{pattern:e,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:,]/},s.languages.css.atrule.inside.rest=s.languages.css;var t=s.languages.markup;t&&(t.tag.addInlined("style","css"),s.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:t.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:s.languages.css}},alias:"language-css"}},t.tag))}(Prism); +Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|interface|extends|implements|trait|instanceof|new)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/}; +Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])[_$A-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\.(?:prototype|constructor))/,lookbehind:!0}],keyword:[{pattern:/((?:^|})\s*)(?:catch|finally)\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],number:/\b(?:(?:0[xX](?:[\dA-Fa-f](?:_[\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\d(?:_\d)?)+n|NaN|Infinity)\b|(?:\b(?:\d(?:_\d)?)+\.?(?:\d(?:_\d)?)*|\B\.(?:\d(?:_\d)?)+)(?:[Ee][+-]?(?:\d(?:_\d)?)+)?/,function:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,operator:/--|\+\+|\*\*=?|=>|&&|\|\||[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?[.?]?|[~:]/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[gimyus]{0,6}(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,lookbehind:!0,greedy:!0},"function-variable":{pattern:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)?\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=>)/i,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*)\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}|(?!\${)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.js=Prism.languages.javascript; +!function(){if("undefined"!=typeof self&&self.Prism||"undefined"!=typeof global&&global.Prism){var a,e,t="";Prism.plugins.customClass={add:function(n){a=n},map:function(s){e="function"==typeof s?s:function(n){return s[n]||n}},prefix:function(n){t=n||""}},Prism.hooks.add("wrap",function(s){if(a){var n=a({content:s.content,type:s.type,language:s.language});Array.isArray(n)?s.classes.push.apply(s.classes,n):n&&s.classes.push(n)}(e||t)&&(s.classes=s.classes.map(function(n){return t+(e?e(n,s.language):n)}))})}}(); diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb index d394c3d..bef3959 100644 --- a/app/jobs/application_job.rb +++ b/app/jobs/application_job.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ApplicationJob < ActiveJob::Base # Automatically retry jobs that encountered a deadlock # retry_on ActiveRecord::Deadlocked diff --git a/app/jobs/submission_append_job.rb b/app/jobs/submission_append_job.rb index 096e5b0..7bfc68e 100644 --- a/app/jobs/submission_append_job.rb +++ b/app/jobs/submission_append_job.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + class SubmissionAppendJob < ApplicationJob queue_as :default - rescue_from(Signet::AuthorizationError, Google::Apis::AuthorizationError) do |exception| - submission_id = self.arguments.first + rescue_from(Signet::AuthorizationError, Google::Apis::AuthorizationError) do |_exception| + submission_id = arguments.first Rails.logger.error("AuthorizationError during SubmissionAppend: submission_id=#{submission_id}") submission = Submission.find(submission_id) submission.form.deactivate!('AuthorizationError') diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 286b223..d84cb6e 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ApplicationMailer < ActionMailer::Base default from: 'from@example.com' layout 'mailer' diff --git a/app/models/application_record.rb b/app/models/application_record.rb index 10a4cba..71fbba5 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ApplicationRecord < ActiveRecord::Base self.abstract_class = true end diff --git a/app/models/authentication.rb b/app/models/authentication.rb index 6da1943..e74ac3f 100644 --- a/app/models/authentication.rb +++ b/app/models/authentication.rb @@ -1,7 +1,9 @@ +# frozen_string_literal: true + class Authentication < ApplicationRecord belongs_to :user - scope :for, -> (provider) { where(provider: provider) } + scope :for, ->(provider) { where(provider: provider) } encrypts :access_token encrypts :refresh_token @@ -10,19 +12,24 @@ class Authentication < ApplicationRecord expires_at <= Time.current end + def refresh_from(client_secret) + client_secret.refresh! + self.access_token = client_secret.access_token if client_secret.access_token.present? + self.refresh_token = client_secret.refresh_token if client_secret.refresh_token.present? + self.expires_at = Time.zone.at(client_secret.expires_at) if client_secret.expires_at.present? + save + end + def google_authorization return nil unless provider == 'google' + @google_authorization ||= CLIENT_SECRETS.to_authorization.tap do |c| - c.access_token = self.access_token - c.refresh_token = self.refresh_token - c.expires_at = self.expires_at + c.access_token = access_token + c.refresh_token = refresh_token + c.expires_at = expires_at if expires_at < 1.minute.from_now - c.refresh! - self.access_token = c.access_token if c.access_token.present? - self.refresh_token = c.refresh_token if c.refresh_token.present? - self.expires_at = Time.at(c.expires_at) if c.expires_at.present? + refresh_from(c) end end end - end diff --git a/app/models/form.rb b/app/models/form.rb index 6ad6d99..b51c6f2 100644 --- a/app/models/form.rb +++ b/app/models/form.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Form < ApplicationRecord belongs_to :user has_many :submissions, dependent: :destroy @@ -10,12 +12,12 @@ class Form < ApplicationRecord encrypts :airtable_api_key encrypts :airtable_app_key - validates_presence_of :title - validates_inclusion_of :backend_name, in: ['google_sheets', 'airtable'] + validates :title, presence: true + validates :backend_name, inclusion: { in: %w[google_sheets airtable] } # Airtable validations - validates_presence_of :airtable_api_key, if: :airtable? - validates_presence_of :airtable_app_key, if: :airtable? - validates_presence_of :airtable_table, if: :airtable? + validates :airtable_api_key, presence: { if: :airtable? } + validates :airtable_app_key, presence: { if: :airtable? } + validates :airtable_table, presence: { if: :airtable? } # TODO: use counter_cache option on association def submissions_count @@ -27,12 +29,10 @@ class Form < ApplicationRecord end def deactivate!(reason = nil) - self.user.deactivate!(reason) + user.deactivate!(reason) end - def active? - self.user.active? - end + delegate :active?, to: :user def airtable? backend_name == 'airtable' diff --git a/app/models/submission.rb b/app/models/submission.rb index 5a404d8..3b7426d 100644 --- a/app/models/submission.rb +++ b/app/models/submission.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + class Submission < ApplicationRecord belongs_to :form has_many_attached :files acts_as_sequenced scope: :form_id - validates_presence_of :data, if: :appended_at? + validates :data, presence: { if: :appended_at? } def process_data(submitted_data) processed_data = {} @@ -12,7 +14,7 @@ class Submission < ApplicationRecord processed_data[key] = submission_value_for(value) end update_attribute(:data, processed_data) - SubmissionAppendJob.perform_later(self.id) + SubmissionAppendJob.perform_later(id) end def submission_value_for(value) @@ -37,7 +39,8 @@ class Submission < ApplicationRecord attachment = ActiveStorage::Attachment.new(record: self, name: 'files', blob: create_one.blob) attachment.save # return the URL that we use to show in the Spreadsheet - Rails.application.routes.url_helpers.file_upload_url(form_id: form, submission_id: self, id: attachment.token, host: DEFAULT_HOST, filename: attachment.blob.filename) + Rails.application.routes.url_helpers.file_upload_url(form_id: form, submission_id: self, id: attachment.token, + host: DEFAULT_HOST, filename: attachment.blob.filename) else value.to_s end diff --git a/app/models/user.rb b/app/models/user.rb index 094cf4f..f089218 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + class User < ApplicationRecord authenticates_with_sorcery! has_many :authentications, dependent: :destroy has_many :forms, dependent: :destroy - def deactivate!(reason = nil) + def deactivate!(_reason = nil) # currently we only use deactivate if we get an authentication exception appending data to a spreadsheet authentications.last&.update(expires_at: Time.current) end diff --git a/app/views/forms/_form_example.html.erb b/app/views/forms/_form_example.html.erb index fed070c..eb0b51f 100644 --- a/app/views/forms/_form_example.html.erb +++ b/app/views/forms/_form_example.html.erb @@ -6,6 +6,16 @@ <%= text_field_tag header %>

<% end %> + <% if form.spreadsheet_headers.blank? %> +

+ + +

+

+ + +

+ <% end %> ... diff --git a/app/views/forms/_form_example_js.html.erb b/app/views/forms/_form_example_js.html.erb index 15311f0..b291ddd 100644 --- a/app/views/forms/_form_example_js.html.erb +++ b/app/views/forms/_form_example_js.html.erb @@ -7,5 +7,4 @@

<% end %> ... - diff --git a/app/views/forms/setup.html.erb b/app/views/forms/setup.html.erb index aebf6fa..cbdd1b0 100644 --- a/app/views/forms/setup.html.erb +++ b/app/views/forms/setup.html.erb @@ -42,7 +42,7 @@

Please set your form action as in the example below. Make sure to use proper name attributes.

-


+      

 <%= html_escape_once render('form_example', form: @form) %>
       
@@ -50,12 +50,9 @@

Learn more about our JavaScript library and our API in your help section.

-

+      

 <%= html_escape_once render('form_example_js', form: @form) %>
       
- - - diff --git a/app/views/forms/show.html.erb b/app/views/forms/show.html.erb index df66cd8..e61786c 100644 --- a/app/views/forms/show.html.erb +++ b/app/views/forms/show.html.erb @@ -24,22 +24,23 @@
-

Setup your online form:

+

One more step:

- Your form submission URL: <%= submission_url(@form) %> -

-

- Set the action of your online form to this URL and make sure your fields have proper name attributes. + Set the action of your online form to <%= submission_url(@form) %> and make sure your fields have proper name attributes.

Example:

-

+        

 <%= html_escape_once render('form_example', form: @form) %>
         
- -

- Let us know if you need <%= link_to 'help', help_url %> - we also build the form for you! + If you prefer to submit the form using JavaScript/AJAX have a look at our JavaScript library and our API. +

+

+ If you need help, <%= link_to "we're here!", help_url %> +

+

+ And if you do not want to deal with your form at all, have a look at our <%= link_to 'form building service', form_building_service_url %>.

diff --git a/app/views/sessions/new.html.erb b/app/views/sessions/new.html.erb index 70ed4e8..f17ce3c 100644 --- a/app/views/sessions/new.html.erb +++ b/app/views/sessions/new.html.erb @@ -14,7 +14,7 @@ <% end %>

- Simply login with your existing Google account. + Login with your existing Google account, no new credentials needed.
Do you need help? <%= link_to 'let us know', help_url %>.

diff --git a/bin/bundle b/bin/bundle index a71368e..4719508 100755 --- a/bin/bundle +++ b/bin/bundle @@ -8,46 +8,48 @@ # this file is here to facilitate running it. # -require "rubygems" +require 'rubygems' m = Module.new do module_function def invoked_as_script? - File.expand_path($0) == File.expand_path(__FILE__) + File.expand_path($PROGRAM_NAME) == File.expand_path(__FILE__) end def env_var_version - ENV["BUNDLER_VERSION"] + ENV['BUNDLER_VERSION'] end def cli_arg_version return unless invoked_as_script? # don't want to hijack other binstubs - return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` + unless 'update'.start_with?(ARGV.first || ' ') + return + end # must be running `bundle update` + bundler_version = nil update_index = nil ARGV.each_with_index do |a, i| - if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN - bundler_version = a - end + bundler_version = a if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ - bundler_version = $1 + + bundler_version = Regexp.last_match(1) update_index = i end bundler_version end def gemfile - gemfile = ENV["BUNDLE_GEMFILE"] + gemfile = ENV['BUNDLE_GEMFILE'] return gemfile if gemfile && !gemfile.empty? - File.expand_path("../../Gemfile", __FILE__) + File.expand_path('../Gemfile', __dir__) end def lockfile lockfile = case File.basename(gemfile) - when "gems.rb" then gemfile.sub(/\.rb$/, gemfile) + when 'gems.rb' then gemfile.sub(/\.rb$/, gemfile) else "#{gemfile}.lock" end File.expand_path(lockfile) @@ -55,15 +57,17 @@ m = Module.new do def lockfile_version return unless File.file?(lockfile) + lockfile_contents = File.read(lockfile) return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ + Regexp.last_match(1) end def bundler_version @bundler_version ||= env_var_version || cli_arg_version || - lockfile_version + lockfile_version end def bundler_requirement @@ -73,28 +77,30 @@ m = Module.new do requirement = bundler_gem_version.approximate_recommendation - return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.7.0") + return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new('2.7.0') - requirement += ".a" if bundler_gem_version.prerelease? + requirement += '.a' if bundler_gem_version.prerelease? requirement end def load_bundler! - ENV["BUNDLE_GEMFILE"] ||= gemfile + ENV['BUNDLE_GEMFILE'] ||= gemfile activate_bundler end def activate_bundler gem_error = activation_error_handling do - gem "bundler", bundler_requirement + gem 'bundler', bundler_requirement end return if gem_error.nil? + require_error = activation_error_handling do - require "bundler/version" + require 'bundler/version' end return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) + warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`" exit 42 end @@ -109,6 +115,4 @@ end m.load_bundler! -if m.invoked_as_script? - load Gem.bin_path("bundler", "bundle") -end +load Gem.bin_path('bundler', 'bundle') if m.invoked_as_script? diff --git a/bin/rails b/bin/rails index 5badb2f..3504c3f 100755 --- a/bin/rails +++ b/bin/rails @@ -1,6 +1,8 @@ #!/usr/bin/env ruby +# frozen_string_literal: true + begin - load File.expand_path('../spring', __FILE__) + load File.expand_path('spring', __dir__) rescue LoadError => e raise unless e.message.include?('spring') end diff --git a/bin/rake b/bin/rake index d87d5f5..1fe6cf0 100755 --- a/bin/rake +++ b/bin/rake @@ -1,6 +1,8 @@ #!/usr/bin/env ruby +# frozen_string_literal: true + begin - load File.expand_path('../spring', __FILE__) + load File.expand_path('spring', __dir__) rescue LoadError => e raise unless e.message.include?('spring') end diff --git a/bin/setup b/bin/setup index 5853b5e..ac9f624 100755 --- a/bin/setup +++ b/bin/setup @@ -1,4 +1,6 @@ #!/usr/bin/env ruby +# frozen_string_literal: true + require 'fileutils' # path to your application root. diff --git a/bin/spring b/bin/spring index d89ee49..1c6eabf 100755 --- a/bin/spring +++ b/bin/spring @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # This file loads Spring without using Bundler, in order to be fast. # It gets overwritten when you run the `spring binstub` command. diff --git a/bin/webpack b/bin/webpack index 1031168..62114ba 100755 --- a/bin/webpack +++ b/bin/webpack @@ -1,18 +1,19 @@ #!/usr/bin/env ruby +# frozen_string_literal: true -ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" -ENV["NODE_ENV"] ||= "development" +ENV['RAILS_ENV'] ||= ENV['RACK_ENV'] || 'development' +ENV['NODE_ENV'] ||= 'development' -require "pathname" -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", - Pathname.new(__FILE__).realpath) +require 'pathname' +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', + Pathname.new(__FILE__).realpath) -require "bundler/setup" +require 'bundler/setup' -require "webpacker" -require "webpacker/webpack_runner" +require 'webpacker' +require 'webpacker/webpack_runner' -APP_ROOT = File.expand_path("..", __dir__) +APP_ROOT = File.expand_path('..', __dir__) Dir.chdir(APP_ROOT) do Webpacker::WebpackRunner.run(ARGV) end diff --git a/bin/webpack-dev-server b/bin/webpack-dev-server index dd96627..e4f41cc 100755 --- a/bin/webpack-dev-server +++ b/bin/webpack-dev-server @@ -1,18 +1,19 @@ #!/usr/bin/env ruby +# frozen_string_literal: true -ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" -ENV["NODE_ENV"] ||= "development" +ENV['RAILS_ENV'] ||= ENV['RACK_ENV'] || 'development' +ENV['NODE_ENV'] ||= 'development' -require "pathname" -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", - Pathname.new(__FILE__).realpath) +require 'pathname' +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', + Pathname.new(__FILE__).realpath) -require "bundler/setup" +require 'bundler/setup' -require "webpacker" -require "webpacker/dev_server_runner" +require 'webpacker' +require 'webpacker/dev_server_runner' -APP_ROOT = File.expand_path("..", __dir__) +APP_ROOT = File.expand_path('..', __dir__) Dir.chdir(APP_ROOT) do Webpacker::DevServerRunner.run(ARGV) end diff --git a/bin/yarn b/bin/yarn index 460dd56..4cac416 100755 --- a/bin/yarn +++ b/bin/yarn @@ -1,11 +1,11 @@ #!/usr/bin/env ruby +# frozen_string_literal: true + APP_ROOT = File.expand_path('..', __dir__) Dir.chdir(APP_ROOT) do - begin - exec "yarnpkg", *ARGV - rescue Errno::ENOENT - $stderr.puts "Yarn executable was not detected in the system." - $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" - exit 1 - end + exec 'yarnpkg', *ARGV +rescue Errno::ENOENT + warn 'Yarn executable was not detected in the system.' + warn 'Download Yarn at https://yarnpkg.com/en/docs/install' + exit 1 end diff --git a/config.ru b/config.ru index f7ba0b5..842bccc 100644 --- a/config.ru +++ b/config.ru @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This file is used by Rack-based servers to start the application. require_relative 'config/environment' diff --git a/config/application.rb b/config/application.rb index ffe45c8..2fb51f1 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative 'boot' require 'rails/all' @@ -29,7 +31,7 @@ module Tinyform config.middleware.insert_before 0, Rack::Cors do allow do origins '*' - resource '/s/*', headers: :any, methods: [:post, :put, :options] + resource '/s/*', headers: :any, methods: %i[post put options] end end end diff --git a/config/boot.rb b/config/boot.rb index b9e460c..c04863f 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) require 'bundler/setup' # Set up gems listed in the Gemfile. diff --git a/config/environment.rb b/config/environment.rb index 426333b..d5abe55 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Load the Rails application. require_relative 'application' diff --git a/config/environments/development.rb b/config/environments/development.rb index 66df51f..e8fdb5a 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. @@ -14,7 +16,7 @@ Rails.application.configure do # Enable/disable caching. By default caching is disabled. # Run rails dev:cache to toggle caching. - if Rails.root.join('tmp', 'caching-dev.txt').exist? + if Rails.root.join('tmp/caching-dev.txt').exist? config.action_controller.perform_caching = true config.action_controller.enable_fragment_cache_logging = true diff --git a/config/environments/production.rb b/config/environments/production.rb index 51f2525..af5c9a1 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. @@ -51,7 +53,7 @@ Rails.application.configure do config.log_level = :info # Prepend all log lines with the following tags. - config.log_tags = [ :request_id ] + config.log_tags = [:request_id] # Use a different cache store in production. # config.cache_store = :mem_cache_store @@ -80,7 +82,7 @@ Rails.application.configure do # require 'syslog/logger' # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') - if ENV["RAILS_LOG_TO_STDOUT"].present? + if ENV['RAILS_LOG_TO_STDOUT'].present? logger = ActiveSupport::Logger.new(STDOUT) logger.formatter = config.log_formatter config.logger = ActiveSupport::TaggedLogging.new(logger) diff --git a/config/environments/test.rb b/config/environments/test.rb index 1d62e91..e5cbfe5 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # The test environment is used exclusively to run your application's # test suite. You never need to work with it otherwise. Remember that # your test database is "scratch space" for the test suite and is wiped diff --git a/config/initializers/application_controller_renderer.rb b/config/initializers/application_controller_renderer.rb index 89d2efa..f4556db 100644 --- a/config/initializers/application_controller_renderer.rb +++ b/config/initializers/application_controller_renderer.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true # Be sure to restart your server when you modify this file. # ActiveSupport::Reloader.to_prepare do diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 4b828e8..a9b0d0f 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Version of your assets, change this if you want to expire all your assets. diff --git a/config/initializers/attachment_tokens.rb b/config/initializers/attachment_tokens.rb index a73e466..53bd4cd 100644 --- a/config/initializers/attachment_tokens.rb +++ b/config/initializers/attachment_tokens.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Rails.configuration.to_prepare do ActiveStorage::Attachment.send(:has_secure_token) end diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb index 59385cd..d0f0d3b 100644 --- a/config/initializers/backtrace_silencers.rb +++ b/config/initializers/backtrace_silencers.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true # Be sure to restart your server when you modify this file. # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index 35d0f26..f3bcce5 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true # Be sure to restart your server when you modify this file. # Define an application-wide content security policy diff --git a/config/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb index 5a6a32d..ee8dff9 100644 --- a/config/initializers/cookies_serializer.rb +++ b/config/initializers/cookies_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Specify a serializer for the signed and encrypted cookie jars. diff --git a/config/initializers/demo.rb b/config/initializers/demo.rb index 4947248..de6fb3b 100644 --- a/config/initializers/demo.rb +++ b/config/initializers/demo.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + GOOGLE_DEMO_FORM = Form.find_by(id: ENV['GOOGLE_DEMO_FORM_ID']) AIRTABLE_DEMO_FORM = Form.find_by(id: ENV['AIRTABLE_DEMO_FORM_ID']) AIRTABLE_DEMO_EMBED_URL = ENV['AIRTABLE_DEMO_EMBED_URL'] diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index 4a994e1..7a4f47b 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Configure sensitive parameters which will be filtered from the log file. diff --git a/config/initializers/google.rb b/config/initializers/google.rb index 5acb04c..6b15c5e 100644 --- a/config/initializers/google.rb +++ b/config/initializers/google.rb @@ -1,13 +1,15 @@ +# frozen_string_literal: true + require 'google/api_client/client_secrets' require 'google/apis' secrets_options = { - "client_id" => ENV['GOOGLE_CLIENT_ID'], - "project_id" => ENV['GOOGLE_PROJECT_ID'], - "client_secret" => ENV['GOOGLE_CLIENT_SECRET'], - "auth_uri" => "https://accounts.google.com/o/oauth2/auth", - "token_uri" => "https://oauth2.googleapis.com/token", - "auth_provider_x509_cert_url" => "https://www.googleapis.com/oauth2/v1/certs" + 'client_id' => ENV['GOOGLE_CLIENT_ID'], + 'project_id' => ENV['GOOGLE_PROJECT_ID'], + 'client_secret' => ENV['GOOGLE_CLIENT_SECRET'], + 'auth_uri' => 'https://accounts.google.com/o/oauth2/auth', + 'token_uri' => 'https://oauth2.googleapis.com/token', + 'auth_provider_x509_cert_url' => 'https://www.googleapis.com/oauth2/v1/certs' } -CLIENT_SECRETS = Google::APIClient::ClientSecrets.new("web" => secrets_options) +CLIENT_SECRETS = Google::APIClient::ClientSecrets.new('web' => secrets_options) Google::Apis.logger = ::Rails.logger diff --git a/config/initializers/host.rb b/config/initializers/host.rb index e4d2eaa..b27031c 100644 --- a/config/initializers/host.rb +++ b/config/initializers/host.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + DEFAULT_HOST = ENV['DEFAULT_HOST'] || 'localhost:3000' diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index ac033bf..aa7435f 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true # Be sure to restart your server when you modify this file. # Add new inflection rules using the following format. Inflections diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb index dc18996..6e1d16f 100644 --- a/config/initializers/mime_types.rb +++ b/config/initializers/mime_types.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true # Be sure to restart your server when you modify this file. # Add new mime types for use in respond_to blocks: diff --git a/config/initializers/sorcery.rb b/config/initializers/sorcery.rb index 3e76ca2..a03a61f 100644 --- a/config/initializers/sorcery.rb +++ b/config/initializers/sorcery.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + # The first thing you need to configure is which modules you need in your app. # The default is nothing which will include only core features (password encryption, login/logout). # # Available submodules are: :user_activation, :http_basic_auth, :remember_me, # :reset_password, :session_timeout, :brute_force_protection, :activity_logging, # :magic_login, :external -Rails.application.config.sorcery.submodules = [:reset_password, :external, :magic_login] +Rails.application.config.sorcery.submodules = %i[reset_password external magic_login] # Here you can configure each submodule's features. Rails.application.config.sorcery.configure do |config| @@ -152,9 +154,9 @@ Rails.application.config.sorcery.configure do |config| # config.google.key = ENV['GOOGLE_CLIENT_ID'] config.google.secret = ENV['GOOGLE_CLIENT_SECRET'] - config.google.callback_url = (ENV['GOOGLE_AUTH_CALLBACK_URL'] || "http://localhost:3000/oauth/callback?provider=google") - config.google.user_info_mapping = {:email => "email", :name => "name", :google_id => "id"} - config.google.scope = "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/spreadsheets" + config.google.callback_url = (ENV['GOOGLE_AUTH_CALLBACK_URL'] || 'http://localhost:3000/oauth/callback?provider=google') + config.google.user_info_mapping = { email: 'email', name: 'name', google_id: 'id' } + config.google.scope = 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/spreadsheets' config.google.auth_url = '/o/oauth2/auth?access_type=offline&include_granted_scopes=true' # # For Microsoft Graph, the key will be your App ID, and the secret will be your app password/public key. @@ -407,7 +409,7 @@ Rails.application.config.sorcery.configure do |config| # # user.magic_login_token_expires_at_attribute_name = - # When was magic login email sent — used for hammering protection. + # When was magic login email sent - for hammering protection. # Default: `:magic_login_email_sent_at` # # user.magic_login_email_sent_at_attribute_name = @@ -528,5 +530,5 @@ Rails.application.config.sorcery.configure do |config| # This line must come after the 'user config' block. # Define which model authenticates with sorcery. - config.user_class = "User" + config.user_class = 'User' end diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb index bbfc396..2f3c0db 100644 --- a/config/initializers/wrap_parameters.rb +++ b/config/initializers/wrap_parameters.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # This file contains settings for ActionController::ParamsWrapper which diff --git a/config/puma.rb b/config/puma.rb index 5ed4437..6d84069 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -1,23 +1,25 @@ +# frozen_string_literal: true + # Puma can serve each request in a thread from an internal thread pool. # The `threads` method setting takes two numbers: a minimum and maximum. # Any libraries that use thread pools should be configured to match # the maximum value specified for Puma. Default is set to 5 threads for minimum # and maximum; this matches the default thread size of Active Record. # -max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } -min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } +max_threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 } +min_threads_count = ENV.fetch('RAILS_MIN_THREADS') { max_threads_count } threads min_threads_count, max_threads_count # Specifies the `port` that Puma will listen on to receive requests; default is 3000. # -port ENV.fetch("PORT") { 3000 } +port ENV.fetch('PORT') { 3000 } # Specifies the `environment` that Puma will run in. # -environment ENV.fetch("RAILS_ENV") { "development" } +environment ENV.fetch('RAILS_ENV') { 'development' } # Specifies the `pidfile` that Puma will use. -pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } +pidfile ENV.fetch('PIDFILE') { 'tmp/pids/server.pid' } # Specifies the number of `workers` to boot in clustered mode. # Workers are forked web server processes. If using threads and workers together diff --git a/config/routes.rb b/config/routes.rb index 832d680..0278048 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Rails.application.routes.draw do # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html @@ -11,25 +13,25 @@ Rails.application.routes.draw do # short link for submission file uploads # we add the filename as part of the URL which allows e.g. Airtable to identify and name the file properly # the constraint makes sure that a . (dot) can be in the filename. e.g. cat.jpg - get '/s/:form_id/:submission_id/:id(/:filename)' => 'file_uploads#show', as: :file_upload, constraints: { filename: /[^\/]+/ } + get '/s/:form_id/:submission_id/:id(/:filename)' => 'file_uploads#show', :as => :file_upload, :constraints => { filename: %r{[^/]+} } # form post url to save new submissions - post '/s/:form_id' => 'submissions#create', as: :submission + post '/s/:form_id' => 'submissions#create', :as => :submission # short URL for form page - get '/s/:id/form' => 'forms#form', as: :form_submitter + get '/s/:id/form' => 'forms#form', :as => :form_submitter get 'oauth/callback', to: 'oauths#callback' get 'oauth/:provider', to: 'oauths#oauth', as: :auth_at_provider - get '/signup' => 'sessions#new', as: :signup # TODO: add proper signup page - get '/login' => 'sessions#new', as: :login - get '/logout' => 'sessions#destroy', as: :logout - get '/auth' => 'sessions#auth', as: :auth + get '/signup' => 'sessions#new', :as => :signup # TODO: add proper signup page + get '/login' => 'sessions#new', :as => :login + get '/logout' => 'sessions#destroy', :as => :logout + get '/auth' => 'sessions#auth', :as => :auth - get '/demo(/:backend)' => 'home#demo', as: :demo - get '/contact' => 'home#contact', as: :contact + get '/demo(/:backend)' => 'home#demo', :as => :demo + get '/contact' => 'home#contact', :as => :contact get '/help', to: redirect('https://www.notion.so/Tinyforms-Help-Center-04f13b5908bc46cfb4283079a3cb1149') - + get '/form-building-service', to: redirect('https://www.notion.so/Tinyforms-Help-Center-04f13b5908bc46cfb4283079a3cb1149') root 'home#index' end diff --git a/config/spring.rb b/config/spring.rb index db5bf13..93cd0ff 100644 --- a/config/spring.rb +++ b/config/spring.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + Spring.watch( - ".ruby-version", - ".rbenv-vars", - "tmp/restart.txt", - "tmp/caching-dev.txt" + '.ruby-version', + '.rbenv-vars', + 'tmp/restart.txt', + 'tmp/caching-dev.txt' ) diff --git a/db/migrate/20200405112929_create_authentications.rb b/db/migrate/20200405112929_create_authentications.rb index 09c5add..b09607a 100644 --- a/db/migrate/20200405112929_create_authentications.rb +++ b/db/migrate/20200405112929_create_authentications.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreateAuthentications < ActiveRecord::Migration[6.0] def change create_table :authentications do |t| diff --git a/db/migrate/20200405114101_create_users.rb b/db/migrate/20200405114101_create_users.rb index b3d94b7..92725df 100644 --- a/db/migrate/20200405114101_create_users.rb +++ b/db/migrate/20200405114101_create_users.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreateUsers < ActiveRecord::Migration[6.0] def change create_table :users do |t| diff --git a/db/migrate/20200405121558_create_forms.rb b/db/migrate/20200405121558_create_forms.rb index 94457a6..553ac2d 100644 --- a/db/migrate/20200405121558_create_forms.rb +++ b/db/migrate/20200405121558_create_forms.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreateForms < ActiveRecord::Migration[6.0] def change create_table :forms do |t| @@ -7,7 +9,6 @@ class CreateForms < ActiveRecord::Migration[6.0] t.string :token t.string :thank_you_url - t.timestamps end end diff --git a/db/migrate/20200405161905_create_submissions.rb b/db/migrate/20200405161905_create_submissions.rb index 6c06b3c..21fd596 100644 --- a/db/migrate/20200405161905_create_submissions.rb +++ b/db/migrate/20200405161905_create_submissions.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreateSubmissions < ActiveRecord::Migration[6.0] def change create_table :submissions do |t| diff --git a/db/migrate/20200406221804_create_active_storage_tables.active_storage.rb b/db/migrate/20200406221804_create_active_storage_tables.active_storage.rb index 0b2ce25..98317c9 100644 --- a/db/migrate/20200406221804_create_active_storage_tables.active_storage.rb +++ b/db/migrate/20200406221804_create_active_storage_tables.active_storage.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This migration comes from active_storage (originally 20170806125915) class CreateActiveStorageTables < ActiveRecord::Migration[5.2] def change @@ -10,7 +12,7 @@ class CreateActiveStorageTables < ActiveRecord::Migration[5.2] t.string :checksum, null: false t.datetime :created_at, null: false - t.index [ :key ], unique: true + t.index [:key], unique: true end create_table :active_storage_attachments do |t| @@ -20,7 +22,7 @@ class CreateActiveStorageTables < ActiveRecord::Migration[5.2] t.datetime :created_at, null: false - t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true + t.index %i[record_type record_id name blob_id], name: 'index_active_storage_attachments_uniqueness', unique: true t.foreign_key :active_storage_blobs, column: :blob_id end end diff --git a/db/migrate/20200408212150_add_lockbox_columns.rb b/db/migrate/20200408212150_add_lockbox_columns.rb index 05d0e49..e20b84b 100644 --- a/db/migrate/20200408212150_add_lockbox_columns.rb +++ b/db/migrate/20200408212150_add_lockbox_columns.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AddLockboxColumns < ActiveRecord::Migration[6.0] def change add_column :authentications, :access_token_ciphertext, :text diff --git a/db/migrate/20200409001610_add_metadata_to_submissions.rb b/db/migrate/20200409001610_add_metadata_to_submissions.rb index cb9faf4..40ae7e2 100644 --- a/db/migrate/20200409001610_add_metadata_to_submissions.rb +++ b/db/migrate/20200409001610_add_metadata_to_submissions.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AddMetadataToSubmissions < ActiveRecord::Migration[6.0] def change add_column :submissions, :remote_ip, :string diff --git a/db/migrate/20200412165834_add_sequential_id_to_submissions.rb b/db/migrate/20200412165834_add_sequential_id_to_submissions.rb index 5b9c899..291d945 100644 --- a/db/migrate/20200412165834_add_sequential_id_to_submissions.rb +++ b/db/migrate/20200412165834_add_sequential_id_to_submissions.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AddSequentialIdToSubmissions < ActiveRecord::Migration[6.0] def change add_column :submissions, :sequential_id, :integer diff --git a/db/migrate/20200412214304_add_token_to_attachments.rb b/db/migrate/20200412214304_add_token_to_attachments.rb index 76922bc..dd2d9dc 100644 --- a/db/migrate/20200412214304_add_token_to_attachments.rb +++ b/db/migrate/20200412214304_add_token_to_attachments.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AddTokenToAttachments < ActiveRecord::Migration[6.0] def change add_column :active_storage_attachments, :token, :string diff --git a/db/migrate/20200413101920_sorcery_core.rb b/db/migrate/20200413101920_sorcery_core.rb index 9100c7e..1c983a4 100644 --- a/db/migrate/20200413101920_sorcery_core.rb +++ b/db/migrate/20200413101920_sorcery_core.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SorceryCore < ActiveRecord::Migration[6.0] def change add_column :users, :crypted_password, :string diff --git a/db/migrate/20200413152532_add_airtable_support.rb b/db/migrate/20200413152532_add_airtable_support.rb index a5465dc..05ba84b 100644 --- a/db/migrate/20200413152532_add_airtable_support.rb +++ b/db/migrate/20200413152532_add_airtable_support.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AddAirtableSupport < ActiveRecord::Migration[6.0] def change add_column :forms, :airtable_app_key_ciphertext, :string diff --git a/db/schema.rb b/db/schema.rb index 0c9af99..3f72713 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. @@ -10,84 +12,83 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2020_04_13_152532) do - +ActiveRecord::Schema.define(version: 20_200_413_152_532) do # These are extensions that must be enabled in order to support this database - enable_extension "plpgsql" + enable_extension 'plpgsql' - create_table "active_storage_attachments", force: :cascade do |t| - t.string "name", null: false - t.string "record_type", null: false - t.bigint "record_id", null: false - t.bigint "blob_id", null: false - t.datetime "created_at", null: false - t.string "token" - t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id" - t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true - t.index ["token"], name: "index_active_storage_attachments_on_token", unique: true + create_table 'active_storage_attachments', force: :cascade do |t| + t.string 'name', null: false + t.string 'record_type', null: false + t.bigint 'record_id', null: false + t.bigint 'blob_id', null: false + t.datetime 'created_at', null: false + t.string 'token' + t.index ['blob_id'], name: 'index_active_storage_attachments_on_blob_id' + t.index %w[record_type record_id name blob_id], name: 'index_active_storage_attachments_uniqueness', unique: true + t.index ['token'], name: 'index_active_storage_attachments_on_token', unique: true end - create_table "active_storage_blobs", force: :cascade do |t| - t.string "key", null: false - t.string "filename", null: false - t.string "content_type" - t.text "metadata" - t.bigint "byte_size", null: false - t.string "checksum", null: false - t.datetime "created_at", null: false - t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true + create_table 'active_storage_blobs', force: :cascade do |t| + t.string 'key', null: false + t.string 'filename', null: false + t.string 'content_type' + t.text 'metadata' + t.bigint 'byte_size', null: false + t.string 'checksum', null: false + t.datetime 'created_at', null: false + t.index ['key'], name: 'index_active_storage_blobs_on_key', unique: true end - create_table "authentications", force: :cascade do |t| - t.integer "user_id" - t.datetime "expires_at" - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false - t.text "access_token_ciphertext" - t.text "refresh_token_ciphertext" - t.string "provider" - t.string "uid" + create_table 'authentications', force: :cascade do |t| + t.integer 'user_id' + t.datetime 'expires_at' + t.datetime 'created_at', precision: 6, null: false + t.datetime 'updated_at', precision: 6, null: false + t.text 'access_token_ciphertext' + t.text 'refresh_token_ciphertext' + t.string 'provider' + t.string 'uid' end - create_table "forms", force: :cascade do |t| - t.integer "user_id" - t.string "google_spreadsheet_id" - t.string "title" - t.string "token" - t.string "thank_you_url" - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false - t.string "airtable_app_key_ciphertext" - t.string "airtable_api_key_ciphertext" - t.string "airtable_table" - t.string "backend_name" + create_table 'forms', force: :cascade do |t| + t.integer 'user_id' + t.string 'google_spreadsheet_id' + t.string 'title' + t.string 'token' + t.string 'thank_you_url' + t.datetime 'created_at', precision: 6, null: false + t.datetime 'updated_at', precision: 6, null: false + t.string 'airtable_app_key_ciphertext' + t.string 'airtable_api_key_ciphertext' + t.string 'airtable_table' + t.string 'backend_name' end - create_table "submissions", force: :cascade do |t| - t.integer "form_id" - t.json "data", default: {} - t.datetime "appended_at" - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false - t.string "remote_ip" - t.string "referrer" - t.integer "sequential_id" + create_table 'submissions', force: :cascade do |t| + t.integer 'form_id' + t.json 'data', default: {} + t.datetime 'appended_at' + t.datetime 'created_at', precision: 6, null: false + t.datetime 'updated_at', precision: 6, null: false + t.string 'remote_ip' + t.string 'referrer' + t.integer 'sequential_id' end - create_table "users", force: :cascade do |t| - t.string "email" - t.string "name" - t.string "google_id" - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false - t.string "crypted_password" - t.string "salt" - t.string "magic_login_token" - t.datetime "magic_login_token_expires_at" - t.datetime "magic_login_email_sent_at" - t.index ["email"], name: "index_users_on_email", unique: true - t.index ["magic_login_token"], name: "index_users_on_magic_login_token" + create_table 'users', force: :cascade do |t| + t.string 'email' + t.string 'name' + t.string 'google_id' + t.datetime 'created_at', precision: 6, null: false + t.datetime 'updated_at', precision: 6, null: false + t.string 'crypted_password' + t.string 'salt' + t.string 'magic_login_token' + t.datetime 'magic_login_token_expires_at' + t.datetime 'magic_login_email_sent_at' + t.index ['email'], name: 'index_users_on_email', unique: true + t.index ['magic_login_token'], name: 'index_users_on_magic_login_token' end - add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" + add_foreign_key 'active_storage_attachments', 'active_storage_blobs', column: 'blob_id' end diff --git a/db/seeds.rb b/db/seeds.rb index 1beea2a..8744e3c 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true # This file should contain all the record creation needed to seed the database with its default values. # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). # diff --git a/lib/spreadsheet_backends/airtable.rb b/lib/spreadsheet_backends/airtable.rb index 047a8f8..1b0d244 100644 --- a/lib/spreadsheet_backends/airtable.rb +++ b/lib/spreadsheet_backends/airtable.rb @@ -1,7 +1,8 @@ +# frozen_string_literal: true + require 'airrecord' module SpreadsheetBackends class Airtable - attr_accessor :form, :user def initialize(form) @@ -16,8 +17,8 @@ module SpreadsheetBackends def append(data) result = table.create(data, typecast: true) result.id.present? - rescue Airrecord::Error => e - return false + rescue Airrecord::Error + false end def create diff --git a/lib/spreadsheet_backends/google_sheets.rb b/lib/spreadsheet_backends/google_sheets.rb index d2a6ab3..d5f92d1 100644 --- a/lib/spreadsheet_backends/google_sheets.rb +++ b/lib/spreadsheet_backends/google_sheets.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + require 'google/apis/sheets_v4' module SpreadsheetBackends class GoogleSheets # Hash to translate an index to a A1 notation. e.g. 1 => 'B', 27 => 'AA' - COLUMN_INDEX_TO_LETTER = Hash.new {|hash,key| hash[key] = hash[key - 1].next }.merge({0 => "A"}) + COLUMN_INDEX_TO_LETTER = Hash.new { |hash, key| hash[key] = hash[key - 1].next }.merge({ 0 => 'A' }) attr_accessor :form, :user @@ -30,17 +32,17 @@ module SpreadsheetBackends def create sheets = Google::Apis::SheetsV4::SheetsService.new sheets.authorization = user.google_authorization - create_object = Google::Apis::SheetsV4::Spreadsheet.new(properties: { title: form.title}) + create_object = Google::Apis::SheetsV4::Spreadsheet.new(properties: { title: form.title }) spreadsheet = sheets.create_spreadsheet(create_object) form.update(google_spreadsheet_id: spreadsheet.spreadsheet_id) end def headers @headers ||= begin - values = spreadsheet_service.get_spreadsheet_values(form.google_spreadsheet_id, 'A1:An').values + values = spreadsheet_service.get_spreadsheet_values(form.google_spreadsheet_id, 'A1:An').values # if there are no headers yet, return an empty array if values - values[0].map(&:strip) + values[0].map(&:strip) else [] end @@ -66,6 +68,5 @@ module SpreadsheetBackends spreadsheet_service.update_spreadsheet_value(form.google_spreadsheet_id, range, value_range, value_input_option: 'USER_ENTERED') @headers = nil # reset header values to refresh memoization on next access end - end end