├── .gitignore ├── .travis.yml ├── CODEOWNERS ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── authenticating-users ├── Gemfile ├── README.md ├── app.rb └── app.yaml ├── bookshelf ├── .gitignore ├── Gemfile ├── README.md ├── Rakefile ├── app.yaml ├── app │ ├── assets │ │ ├── config │ │ │ └── manifest.js │ │ ├── javascripts │ │ │ └── application.js │ │ └── stylesheets │ │ │ └── application.css │ ├── controllers │ │ ├── application_controller.rb │ │ ├── books_controller.rb │ │ └── stackdriver_controller.rb │ ├── models │ │ └── book.rb │ └── views │ │ ├── books │ │ ├── _form.html.erb │ │ ├── edit.html.erb │ │ ├── index.html.erb │ │ ├── new.html.erb │ │ └── show.html.erb │ │ └── layouts │ │ └── application.html.erb ├── bin │ ├── bundle │ ├── rails │ ├── rake │ └── setup ├── config.ru ├── config │ ├── application.rb │ ├── boot.rb │ ├── environment.rb │ ├── environments │ │ ├── development.rb │ │ ├── production.rb │ │ └── test.rb │ ├── initializers │ │ ├── cookies_serializer.rb │ │ ├── filter_parameter_logging.rb │ │ ├── inflections.rb │ │ ├── session_store.rb │ │ └── wrap_parameters.rb │ ├── routes.rb │ └── secrets.yml ├── images │ └── moby-dick.jpg ├── public │ ├── 404.html │ ├── 422.html │ ├── 500.html │ ├── favicon.ico │ └── robots.txt └── spec │ ├── book_extensions.rb │ ├── e2e_spec_helper.rb │ ├── features │ ├── book_management_e2e_spec.rb │ └── book_management_spec.rb │ ├── models │ └── book_spec.rb │ ├── setup.sh │ └── spec_helper.rb ├── credentials.json.enc ├── gce ├── Gemfile ├── README.md ├── app.rb ├── deploy.sh ├── rubyapp.conf ├── startup-script.sh └── teardown.sh └── sessions ├── Gemfile ├── README.md ├── Rakefile ├── acceptance └── app_test.rb ├── app.rb ├── app.yaml ├── firestore_session.rb └── test ├── app_test.rb └── test_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | log/ 2 | tmp/ 3 | db/*.sqlite3 4 | client_secrets.json 5 | Gemfile.lock 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Google, Inc 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | sudo: false 16 | language: ruby 17 | branches: 18 | only: [master] 19 | rvm: 20 | - 2.5 21 | - 2.6 22 | 23 | cache: 24 | directories: 25 | # We cache the SDK so we don't have to download it again on subsequent builds. 26 | - $HOME/google-cloud-sdk 27 | 28 | env: 29 | global: 30 | - PATH=$PATH:$HOME/google-cloud-sdk/bin 31 | - GOOGLE_APPLICATION_CREDENTIALS=$TRAVIS_BUILD_DIR/credentials.json 32 | - BUILD_ID=$TRAVIS_BUILD_ID 33 | 34 | before_install: 35 | #ENCRYPT YOUR PRIVATE KEY (If you need authentication) 36 | # 1. Install and login to the Travis CLI: 37 | # $ gem install travis 38 | # $ travis login 39 | # 2. Move your json private key to credentials.json 40 | # 3. Run: 41 | # $ travis encrypt-file credentials.json --add 42 | # 4. Commit changes: 43 | # $ git add credentials.json.enc 44 | # $ git commit credentials.json.enc .travis.yml 45 | - openssl aes-256-cbc -K $encrypted_990eecf37889_key -iv $encrypted_990eecf37889_iv 46 | -in credentials.json.enc -out credentials.json -d 47 | - gcloud auth activate-service-account --key-file credentials.json 48 | # Update to Bundler 2.0 49 | - gem update --system 50 | - gem install bundler 51 | 52 | install: 53 | - pushd bookshelf 54 | - bundle install --jobs=3 --retry=3 55 | - bash spec/setup.sh 56 | - popd 57 | - pushd sessions 58 | - bundle install --jobs=3 --retry=3 59 | - popd 60 | 61 | script: 62 | - pushd bookshelf 63 | - bundle exec rspec spec --backtrace --format documentation 64 | - popd 65 | - pushd sessions 66 | - bundle exec rake test 67 | - bundle exec rake acceptance 68 | - popd 69 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Code owners file. 2 | # This file controls who is tagged for review for any given pull request. 3 | # 4 | # For syntax help see: 5 | # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax 6 | 7 | * @TheRoyalTnetennba @dazuma 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to become a contributor and submit your own code 2 | 3 | ## Contributor License Agreements 4 | 5 | We'd love to accept your patches! Before we can take them, we 6 | have to jump a couple of legal hurdles. 7 | 8 | Please fill out either the individual or corporate Contributor License Agreement 9 | (CLA). 10 | 11 | * If you are an individual writing original source code and you're sure you 12 | own the intellectual property, then you'll need to sign an [individual CLA] 13 | (https://developers.google.com/open-source/cla/individual). 14 | * If you work for a company that wants to allow you to contribute your work, 15 | then you'll need to sign a [corporate CLA] 16 | (https://developers.google.com/open-source/cla/corporate). 17 | 18 | Follow either of the two links above to access the appropriate CLA and 19 | instructions for how to sign and return it. Once we receive it, we'll be able to 20 | accept your pull requests. 21 | 22 | ## Contributing A Patch 23 | 24 | 1. Submit an issue describing your proposed change to the repo in question. 25 | 1. The repo owner will respond to your issue promptly. 26 | 1. If your proposed change is accepted, and you haven't already done so, sign a 27 | Contributor License Agreement (see details above). 28 | 1. Fork the desired repo, develop and test your code changes. 29 | 1. Ensure that your code adheres to the existing style in the sample to which 30 | you are contributing. Refer to the 31 | [Google Cloud Platform Samples Style Guide] 32 | (https://github.com/GoogleCloudPlatform/Template/wiki/style.html) for the 33 | recommended coding standards for this organization. 34 | 1. Ensure that your code has an appropriate set of unit tests which all pass. 35 | 1. Submit a pull request. 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Getting Started Ruby 2 | 3 | [![Travis Build Status](https://travis-ci.org/GoogleCloudPlatform/getting-started-ruby.svg)](https://travis-ci.org/GoogleCloudPlatform/getting-started-ruby) 4 | 5 | Checkout folders to view particular steps of this sample application. 6 | 7 | - [`bookshelf/`](bookshelf/) 8 | - Code for the [Getting started with Ruby][getting-started] tutorial. 9 | 10 | [Ruby on Rails][ror] web application on [Google App Engine][gae]. 11 | 12 | ### Run 13 | 14 | To run the application, first install dependencies: 15 | 16 | $ bundle install 17 | 18 | And then run the Rails web server: 19 | 20 | $ rails server 21 | 22 | ### To run the tests 23 | 24 | $ bundle install 25 | $ bundle exec rspec 26 | 27 | ### To deploy to App Engine 28 | 29 | Install the [gcloud CLI](https://cloud.google.com/cli): https://cloud.google.com/sdk/docs/install-sdk 30 | 31 | And then deploy the application: 32 | 33 | $ gcloud app deploy 34 | 35 | ## Contributing changes 36 | 37 | * See [CONTRIBUTING.md](CONTRIBUTING.md) 38 | 39 | ## Licensing 40 | 41 | * See [LICENSE](LICENSE) 42 | 43 | [getting-started]: http://cloud.google.com/ruby/getting-started/ 44 | [ror]: http://rubyonrails.org/ 45 | [gae]: http://cloud.google.com/appengine/docs/standard/ruby 46 | -------------------------------------------------------------------------------- /authenticating-users/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 6 | 7 | # [START getting_started_requirements] 8 | gem "jwt", "~> 2.2" 9 | gem "sinatra", "~> 2.0" 10 | # [END getting_started_requirements] 11 | -------------------------------------------------------------------------------- /authenticating-users/README.md: -------------------------------------------------------------------------------- 1 | # Authenticate Users with IAP 2 | 3 | * A short App Engine app, which uses [Cloud Identity-Aware Proxy](https://cloud.google.com/iap/) to serve a webpage which says `"Hello #{user_email_address}"`, where `user_email_address` is the authenticated user's email address. 4 | * To run, follow Ruby's [Authenticating Users with IAP](https://cloud.google.com/ruby/getting-started/authenticate-users-with-iap) tutorial. 5 | 6 | ## Contributing changes 7 | 8 | * See [CONTRIBUTING.md](../CONTRIBUTING.md) 9 | 10 | ## Licensing 11 | 12 | * See [LICENSE](../LICENSE) 13 | -------------------------------------------------------------------------------- /authenticating-users/app.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # [START getting_started_auth_all] 16 | require "base64" 17 | require "json" 18 | require "jwt" 19 | require "net/http" 20 | require "openssl" 21 | require "sinatra" 22 | require "uri" 23 | 24 | # [START getting_started_auth_certs] 25 | def certificates 26 | uri = URI.parse "https://www.gstatic.com/iap/verify/public_key" 27 | response = Net::HTTP.get_response uri 28 | JSON.parse response.body 29 | end 30 | 31 | set :certificates, certificates 32 | # [END getting_started_auth_certs] 33 | 34 | # [START getting_started_auth_metadata] 35 | def get_metadata item_name 36 | endpoint = "http://metadata.google.internal" 37 | path = "/computeMetadata/v1/project/#{item_name}" 38 | uri = URI.parse endpoint + path 39 | 40 | http = Net::HTTP.new uri.host, uri.port 41 | request = Net::HTTP::Get.new uri.request_uri 42 | request["Metadata-Flavor"] = "Google" 43 | response = http.request request 44 | response.body 45 | end 46 | # [END getting_started_auth_metadata] 47 | 48 | # [START getting_started_auth_audience] 49 | def audience 50 | project_number = get_metadata "numeric-project-id" 51 | project_id = get_metadata "project-id" 52 | "/projects/#{project_number}/apps/#{project_id}" 53 | end 54 | 55 | set :audience, audience 56 | # [END getting_started_auth_audience] 57 | 58 | # [START getting_started_auth_validate_assertion] 59 | def validate_assertion assertion 60 | a_header = Base64.decode64 assertion.split(".")[0] 61 | key_id = JSON.parse(a_header)["kid"] 62 | cert = OpenSSL::PKey::EC.new settings.certificates[key_id] 63 | info = JWT.decode assertion, cert, true, algorithm: "ES256", audience: settings.audience 64 | return info[0]["email"], info[0]["sub"] 65 | 66 | rescue StandardError => e 67 | puts "Failed to validate assertion: #{e}" 68 | [nil, nil] 69 | end 70 | # [END getting_started_auth_validate_assertion] 71 | 72 | # [START getting_started_auth_front_controller] 73 | get "/" do 74 | assertion = request.env["HTTP_X_GOOG_IAP_JWT_ASSERTION"] 75 | email, _user_id = validate_assertion assertion 76 | "

Hello #{email}

" 77 | end 78 | # [END getting_started_auth_front_controller] 79 | # [END getting_started_auth_all] 80 | -------------------------------------------------------------------------------- /authenticating-users/app.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # [START getting_started_app_yaml] 16 | runtime: ruby25 17 | entrypoint: bundle exec ruby app.rb 18 | # [END getting_started_app_yaml] 19 | -------------------------------------------------------------------------------- /bookshelf/.gitignore: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | /.bundle 15 | /log/* 16 | /tmp 17 | /public/assets/ 18 | /config/database.yml 19 | /gcd_dataset_directory/ 20 | *.sqlite3 21 | 22 | # Ignore master key for decrypting credentials and more. 23 | /config/master.key 24 | -------------------------------------------------------------------------------- /bookshelf/Gemfile: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | source "https://rubygems.org" 15 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 16 | 17 | ruby '>= 2.5' 18 | 19 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 20 | gem 'rails', '~> 5.2.3' 21 | gem "google-cloud-firestore", "~> 1.0" 22 | gem "google-cloud-storage", "~> 1.10" 23 | gem "google-cloud-error_reporting" 24 | 25 | gem "jquery-rails" 26 | 27 | group :development, :test do 28 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 29 | gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] 30 | end 31 | 32 | group :development do 33 | # Access an interactive console on exception pages or by calling 'console' anywhere in the code. 34 | gem 'web-console', '>= 3.3.0' 35 | gem 'listen', '>= 3.0.5', '< 3.2' 36 | end 37 | 38 | 39 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 40 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 41 | 42 | group :test do 43 | gem "rspec-rails" 44 | gem "rspec-retry" 45 | gem "rack-test" 46 | gem "capybara" 47 | gem "poltergeist" 48 | gem 'phantomjs', :require => 'phantomjs/poltergeist' 49 | end 50 | -------------------------------------------------------------------------------- /bookshelf/README.md: -------------------------------------------------------------------------------- 1 | # Ruby Getting Started 2 | 3 | Follow the [Getting started with Ruby][ruby-tutorial] tutorial to run this app. 4 | 5 | [ruby-tutorial]: https://cloud.google.com/ruby/getting-started 6 | 7 | ## Contributing changes 8 | 9 | * See [CONTRIBUTING.md](../CONTRIBUTING.md) 10 | 11 | ## Licensing 12 | 13 | * See [LICENSE](../LICENSE) 14 | -------------------------------------------------------------------------------- /bookshelf/Rakefile: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | # Add your own tasks in files placed in lib/tasks ending in .rake, 15 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 16 | 17 | require_relative 'config/application' 18 | 19 | Rails.application.load_tasks 20 | -------------------------------------------------------------------------------- /bookshelf/app.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | # [START bookshelf_app_yaml] 15 | runtime: ruby25 16 | entrypoint: bundle exec rackup -p $PORT 17 | instance_class: F2 18 | # [END bookshelf_app_yaml] 19 | -------------------------------------------------------------------------------- /bookshelf/app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_directory ../javascripts .js 2 | //= link_directory ../stylesheets .css 3 | -------------------------------------------------------------------------------- /bookshelf/app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // This is a manifest file that'll be compiled into application.js, which will include all the files 15 | // listed below. 16 | // 17 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 18 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. 19 | // 20 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 21 | // compiled file. 22 | // 23 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 24 | // about supported directives. 25 | // 26 | //= require jquery 27 | //= require jquery_ujs 28 | //= require_tree . -------------------------------------------------------------------------------- /bookshelf/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Google LLC. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | /* 17 | * This is a manifest file that'll be compiled into application.css, which will include all the files 18 | * listed below. 19 | * 20 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's 21 | * vendor/assets/stylesheets directory can be referenced here using a relative path. 22 | * 23 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 24 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 25 | * files in this directory. Styles in this file should be added after the last require_* statement. 26 | * It is generally better to create a new file per style scope. 27 | * 28 | *= require_tree . 29 | *= require_self 30 | */ 31 | -------------------------------------------------------------------------------- /bookshelf/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | class ApplicationController < ActionController::Base 15 | # Prevent CSRF attacks by raising an exception. 16 | # For APIs, you may want to use :null_session instead. 17 | protect_from_forgery with: :exception 18 | end 19 | -------------------------------------------------------------------------------- /bookshelf/app/controllers/books_controller.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | class BooksController < ApplicationController 15 | 16 | PER_PAGE = 10 17 | 18 | def index 19 | @books = Book.query limit: PER_PAGE, last_title: params[:last_title] 20 | @last_title = @books.last&.title 21 | end 22 | 23 | def new 24 | @book = Book.new 25 | end 26 | 27 | def edit 28 | @book = Book.find params[:id] 29 | end 30 | 31 | def show 32 | @book = Book.find params[:id] 33 | end 34 | 35 | def destroy 36 | @book = Book.find params[:id] 37 | @book.destroy 38 | redirect_to books_path 39 | end 40 | 41 | def update 42 | @book = Book.find params[:id] 43 | 44 | if @book.update book_params 45 | flash[:success] = "Updated Book" 46 | redirect_to book_path(@book) 47 | else 48 | render :edit 49 | end 50 | end 51 | 52 | def create 53 | @book = Book.new book_params 54 | 55 | if @book.create 56 | flash[:success] = "Added Book" 57 | redirect_to book_path(@book) 58 | else 59 | render :new 60 | end 61 | end 62 | 63 | private 64 | 65 | def book_params 66 | params.require(:book).permit :title, :author, :published_on, :description, 67 | :cover_image 68 | end 69 | 70 | end 71 | -------------------------------------------------------------------------------- /bookshelf/app/controllers/stackdriver_controller.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019, Google, LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | class StackdriverController < ApplicationController 15 | def logs 16 | Rails.logger.add(Logger::INFO, "Hey, you triggered a custom log entry. Good job!") 17 | redirect_to "/books", flash: { success: "Log message sent" } 18 | end 19 | 20 | def errors 21 | raise "This is an intentional exception." 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /bookshelf/app/models/book.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | class Book 15 | # Add Active Model support. 16 | # Provides constructor that takes a Hash of attribute values. 17 | include ActiveModel::Model 18 | 19 | # Add Active Model validation support to Book class. 20 | include ActiveModel::Validations 21 | 22 | validates :title, presence: true 23 | 24 | attr_accessor :id, :title, :author, :published_on, :description, :image_url, :cover_image 25 | 26 | # Return a Google::Cloud::Firestore::Dataset for the configured collection. 27 | # The collection is used to create, read, update, and delete entity objects. 28 | def self.collection 29 | project_id = ENV["GOOGLE_CLOUD_PROJECT"] 30 | raise "Set the GOOGLE_CLOUD_PROJECT environment variable" if project_id.nil? 31 | 32 | # [START bookshelf_firestore_client] 33 | require "google/cloud/firestore" 34 | firestore = Google::Cloud::Firestore.new project_id: project_id 35 | @collection = firestore.col "books" 36 | # [END bookshelf_firestore_client] 37 | end 38 | 39 | def self.storage_bucket 40 | project_id = ENV["GOOGLE_CLOUD_PROJECT"] 41 | raise "Set the GOOGLE_CLOUD_PROJECT environment variable" if project_id.nil? 42 | 43 | @storage_bucket = begin 44 | config = Rails.application.config.x.settings 45 | # [START bookshelf_cloud_storage_client] 46 | require "google/cloud/storage" 47 | bucket_id = project_id + "_bucket" 48 | storage = Google::Cloud::Storage.new project_id: config["project_id"], 49 | credentials: config["keyfile"] 50 | bucket = storage.bucket bucket_id 51 | # [END bookshelf_cloud_storage_client] 52 | raise "bucket does not exist" if bucket.nil? 53 | bucket 54 | end 55 | end 56 | 57 | # Query Book entities from Cloud Firestore. 58 | # 59 | # returns an array of Book query results and the last book title 60 | # that can be used to query for additional results. 61 | def self.query options = {} 62 | query = collection.order :title 63 | query = query.limit options[:limit] if options[:limit] 64 | query = query.start_after options[:last_title] if options[:last_title] 65 | 66 | books = [] 67 | begin 68 | query.get do |book| 69 | books << Book.from_snapspot(book) 70 | end 71 | rescue 72 | end 73 | books 74 | end 75 | 76 | def self.requires_pagination last_title 77 | if last_title 78 | collection 79 | .order(:title) 80 | .limit(1) 81 | .start_after(last_title) 82 | .get.count > 0 83 | end 84 | end 85 | 86 | def self.from_snapspot book_snapshot 87 | book = Book.new 88 | book.id = book_snapshot.document_id 89 | book_snapshot.data.each do |name, value| 90 | book.send "#{name}=", value if book.respond_to? "#{name}=" 91 | end 92 | book 93 | end 94 | 95 | # Lookup Book by ID. Returns Book or nil. 96 | def self.find id 97 | # [START bookshelf_firestore_client_get_book] 98 | book_snapshot = collection.doc(id).get 99 | Book.from_snapspot book_snapshot if book_snapshot.data 100 | # [END bookshelf_firestore_client_get_book] 101 | end 102 | 103 | # Save the book to Firestore. 104 | # @return true if valid and saved successfully, otherwise false. 105 | def save 106 | if valid? 107 | book_ref = Book.collection.doc id 108 | book_ref.set \ 109 | title: title, 110 | author: author, 111 | published_on: published_on, 112 | description: description, 113 | image_url: image_url 114 | self.id = book_ref.document_id 115 | true 116 | else 117 | false 118 | end 119 | end 120 | 121 | def create 122 | upload_image if cover_image 123 | save 124 | end 125 | 126 | # Set attribute values from provided Hash and save to Firestore. 127 | def update attributes 128 | attributes.each do |name, value| 129 | send "#{name}=", value if respond_to? "#{name}=" 130 | end 131 | update_image if cover_image 132 | save 133 | end 134 | 135 | def update_image 136 | delete_image if image_url 137 | upload_image 138 | end 139 | 140 | def upload_image 141 | file = Book.storage_bucket.create_file \ 142 | cover_image.tempfile, 143 | "cover_images/#{id}/#{cover_image.original_filename}", 144 | content_type: cover_image.content_type, 145 | acl: "public" 146 | @image_url = file.public_url 147 | end 148 | 149 | def destroy 150 | delete_image if image_url 151 | book_ref = Book.collection.doc id 152 | book_ref.delete if book_ref 153 | end 154 | 155 | def delete_image 156 | image_uri = URI.parse image_url 157 | 158 | if image_uri.host == "#{Book.storage_bucket.name}.storage.googleapis.com" 159 | # Remove leading forward slash from image path 160 | # The result will be the image key, eg. "cover_images/:id/:filename" 161 | image_path = image_uri.path.sub "/", "" 162 | 163 | file = Book.storage_bucket.file image_path 164 | file.delete 165 | end 166 | end 167 | 168 | ################## 169 | 170 | def persisted? 171 | id.present? 172 | end 173 | end 174 | -------------------------------------------------------------------------------- /bookshelf/app/views/books/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%# 2 | Copyright 2019 Google LLC. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | %> 15 | 16 |

Book

17 | 18 | <% if @book.errors.any? %> 19 |
20 | <% @book.errors.full_messages.each do |message| %> 21 |
<%= message %>
22 | <% end %> 23 |
24 | <% end %> 25 | 26 | <%= form_for @book, :html => { :multipart => true } do |f| %> 27 |
28 | <%= f.label :title %> 29 | <%= f.text_field :title %> 30 |
31 |
32 | <%= f.label :author %> 33 | <%= f.text_field :author %> 34 |
35 |
36 | <%= f.label :published_on, "Date Published" %> 37 | <%= f.date_field :published_on %> 38 |
39 |
40 | <%= f.label :description %> 41 | <%= f.text_area :description %> 42 |
43 |
44 | <%= f.label :cover_image %> 45 | <%= f.file_field :cover_image %> 46 |
47 | 48 | <% end %> 49 | -------------------------------------------------------------------------------- /bookshelf/app/views/books/edit.html.erb: -------------------------------------------------------------------------------- 1 | <%# 2 | Copyright 2019 Google LLC. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | %> 15 | 16 | <%= render partial: "form" %> 17 | -------------------------------------------------------------------------------- /bookshelf/app/views/books/index.html.erb: -------------------------------------------------------------------------------- 1 | <%# 2 | Copyright 2019 Google LLC. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | %> 15 | 16 |

Books

17 | 18 | <%= link_to new_book_path, class: "btn btn-success btn-sm" do %> 19 | 20 | Add Book 21 | <% end %> 22 | 23 | <% @books.each do |book| %> 24 |
25 | <%= link_to book_path(book) do %> 26 |
27 |

<%= book.title %>

28 |

<%= book.author %>

29 |
30 | <% end %> 31 |
32 | <% end %> 33 | 34 | <% if Book.requires_pagination @last_title %> 35 | 40 | <% end %> 41 | 42 | <% if @books.none? %> 43 |

No books found.

44 | <% end %> 45 | -------------------------------------------------------------------------------- /bookshelf/app/views/books/new.html.erb: -------------------------------------------------------------------------------- 1 | <%# 2 | Copyright 2019 Google LLC. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | %> 15 | 16 | <%= render partial: "form" %> 17 | -------------------------------------------------------------------------------- /bookshelf/app/views/books/show.html.erb: -------------------------------------------------------------------------------- 1 | <%# 2 | Copyright 2019 Google LLC. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | %> 15 | 16 |

Book

17 | 18 |
19 | <%= link_to edit_book_path(@book), class: "btn btn-primary btn-sm" do %> 20 | 21 | Edit Book 22 | <% end %> 23 | <%= link_to book_path(@book), class: "btn btn-danger btn-sm", method: :delete do %> 24 | 25 | Delete Book 26 | <% end %> 27 |
28 | 29 |
30 |
31 | 32 |
33 |
34 |

<%= @book.title %> |   <%= @book.published_on %>

35 |
By <%= @book.author || "unknown" %>
36 |

<%= @book.description %>

37 |
38 |
39 | -------------------------------------------------------------------------------- /bookshelf/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | <%# 2 | Copyright 2019 Google LLC. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | %> 15 | 16 | 17 | 18 | 19 | Bookshelf - Ruby on Google Cloud Platform 20 | 21 | 22 | <%= stylesheet_link_tag "//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" %> 23 | <%= javascript_include_tag "application" %> 24 | <%= csrf_meta_tags %> 25 | 26 | 27 | 37 |
38 | <% if flash.any? %> 39 | <% flash.each do |type, message| %> 40 |
<%= message %>
41 | <% end %> 42 | <% end %> 43 | <%= yield %> 44 |
45 | 46 | 47 | -------------------------------------------------------------------------------- /bookshelf/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # Copyright 2019 Google LLC. 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 17 | load Gem.bin_path('bundler', 'bundle') 18 | -------------------------------------------------------------------------------- /bookshelf/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # Copyright 2019 Google LLC. 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | APP_PATH = File.expand_path('../config/application', __dir__) 17 | require_relative '../config/boot' 18 | require 'rails/commands' 19 | -------------------------------------------------------------------------------- /bookshelf/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # Copyright 2019 Google LLC. 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require_relative '../config/boot' 17 | require 'rake' 18 | Rake.application.run 19 | -------------------------------------------------------------------------------- /bookshelf/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # Copyright 2019 Google LLC. 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require 'fileutils' 17 | include FileUtils 18 | 19 | # path to your application root. 20 | APP_ROOT = File.expand_path('..', __dir__) 21 | 22 | def system!(*args) 23 | system(*args) || abort("\n== Command #{args} failed ==") 24 | end 25 | 26 | chdir APP_ROOT do 27 | # This script is a starting point to setup your application. 28 | # Add necessary setup steps to this file. 29 | 30 | puts '== Installing dependencies ==' 31 | system! 'gem install bundler --conservative' 32 | system('bundle check') || system!('bundle install') 33 | 34 | # puts "\n== Copying sample files ==" 35 | # unless File.exist?('config/database.yml') 36 | # cp 'config/database.yml.sample', 'config/database.yml' 37 | # end 38 | 39 | puts "\n== Preparing database ==" 40 | system! 'bin/rails db:setup' 41 | 42 | puts "\n== Removing old logs and tempfiles ==" 43 | system! 'bin/rails log:clear tmp:clear' 44 | 45 | puts "\n== Restarting application server ==" 46 | system! 'bin/rails restart' 47 | end 48 | -------------------------------------------------------------------------------- /bookshelf/config.ru: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | # This file is used by Rack-based servers to start the application. 15 | 16 | require_relative 'config/environment' 17 | 18 | run Rails.application 19 | -------------------------------------------------------------------------------- /bookshelf/config/application.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | require_relative 'boot' 15 | 16 | require "rails" 17 | # Pick the frameworks you want: 18 | require "active_model/railtie" 19 | require "active_job/railtie" 20 | require "active_storage/engine" 21 | require "action_controller/railtie" 22 | require "action_view/railtie" 23 | require "sprockets/railtie" 24 | require "google/cloud/error_reporting/rails" 25 | 26 | # Require the gems listed in Gemfile, including any gems 27 | # you've limited to :test, :development, or :production. 28 | Bundler.require(*Rails.groups) 29 | 30 | module Bookshelf 31 | class Application < Rails::Application 32 | # Initialize configuration defaults for originally generated Rails version. 33 | config.load_defaults 5.2 34 | 35 | # Settings in config/environments/* take precedence over those specified here. 36 | # Application configuration can go into files in config/initializers 37 | # -- all .rb files in that directory are automatically loaded after loading 38 | # the framework and any gems in your application. 39 | 40 | # Don't generate system test files. 41 | config.generators.system_tests = nil 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /bookshelf/config/boot.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 15 | 16 | require 'bundler/setup' # Set up gems listed in the Gemfile. 17 | -------------------------------------------------------------------------------- /bookshelf/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | # Load the Rails application. 15 | require_relative 'application' 16 | 17 | # Initialize the Rails application. 18 | Rails.application.initialize! 19 | -------------------------------------------------------------------------------- /bookshelf/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | Rails.application.configure do 15 | # Settings specified here will take precedence over those in config/application.rb. 16 | 17 | # In the development environment your application's code is reloaded on 18 | # every request. This slows down response time but is perfect for development 19 | # since you don't have to restart the web server when you make code changes. 20 | config.cache_classes = false 21 | 22 | # Do not eager load code on boot. 23 | config.eager_load = false 24 | 25 | # Show full error reports. 26 | config.consider_all_requests_local = true 27 | 28 | # Enable/disable caching. By default caching is disabled. 29 | # Run rails dev:cache to toggle caching. 30 | if Rails.root.join('tmp', 'caching-dev.txt').exist? 31 | config.action_controller.perform_caching = true 32 | 33 | config.cache_store = :memory_store 34 | config.public_file_server.headers = { 35 | 'Cache-Control' => "public, max-age=#{2.days.to_i}" 36 | } 37 | else 38 | config.action_controller.perform_caching = false 39 | 40 | config.cache_store = :null_store 41 | end 42 | 43 | # Store uploaded files on the local file system (see config/storage.yml for options) 44 | # config.active_storage.service = :local 45 | 46 | # Don't care if the mailer can't send. 47 | # config.action_mailer.raise_delivery_errors = false 48 | 49 | # config.action_mailer.perform_caching = false 50 | 51 | # Print deprecation notices to the Rails logger. 52 | config.active_support.deprecation = :log 53 | 54 | # Raise an error on page load if there are pending migrations. 55 | # config.active_record.migration_error = :page_load 56 | 57 | # Highlight code that triggered database queries in logs. 58 | # config.active_record.verbose_query_logs = true 59 | 60 | 61 | # Raises error for missing translations 62 | # config.action_view.raise_on_missing_translations = true 63 | 64 | # Use an evented file watcher to asynchronously detect changes in source code, 65 | # routes, locales, etc. This feature depends on the listen gem. 66 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 67 | end 68 | -------------------------------------------------------------------------------- /bookshelf/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | Rails.application.configure do 15 | # Settings specified here will take precedence over those in config/application.rb. 16 | 17 | # Code is not reloaded between requests. 18 | config.cache_classes = true 19 | 20 | # Eager load code on boot. This eager loads most of Rails and 21 | # your application in memory, allowing both threaded web servers 22 | # and those relying on copy on write to perform better. 23 | # Rake tasks automatically ignore this option for performance. 24 | config.eager_load = true 25 | 26 | # Full error reports are disabled and caching is turned on. 27 | config.consider_all_requests_local = false 28 | config.action_controller.perform_caching = true 29 | 30 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] 31 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). 32 | # config.require_master_key = true 33 | 34 | # Disable serving static files from the `/public` folder by default since 35 | # Apache or NGINX already handles this. 36 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 37 | 38 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 39 | # config.action_controller.asset_host = 'http://assets.example.com' 40 | 41 | # Specifies the header that your server uses for sending files. 42 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 43 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 44 | 45 | # Store uploaded files on the local file system (see config/storage.yml for options) 46 | # config.active_storage.service = :local 47 | 48 | # Mount Action Cable outside main process or domain 49 | # config.action_cable.mount_path = nil 50 | # config.action_cable.url = 'wss://example.com/cable' 51 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 52 | 53 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 54 | # config.force_ssl = true 55 | 56 | # Use the lowest log level to ensure availability of diagnostic information 57 | # when problems arise. 58 | config.log_level = :debug 59 | 60 | # Prepend all log lines with the following tags. 61 | config.log_tags = [ :request_id ] 62 | 63 | # Use a different cache store in production. 64 | # config.cache_store = :mem_cache_store 65 | 66 | # Use a real queuing backend for Active Job (and separate queues per environment) 67 | # config.active_job.queue_adapter = :resque 68 | # config.active_job.queue_name_prefix = "bookshelf_#{Rails.env}" 69 | 70 | # config.action_mailer.perform_caching = false 71 | 72 | # Ignore bad email addresses and do not raise email delivery errors. 73 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 74 | # config.action_mailer.raise_delivery_errors = false 75 | 76 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 77 | # the I18n.default_locale when a translation cannot be found). 78 | config.i18n.fallbacks = true 79 | 80 | # Send deprecation notices to registered listeners. 81 | config.active_support.deprecation = :notify 82 | 83 | # Use default logging formatter so that PID and timestamp are not suppressed. 84 | config.log_formatter = ::Logger::Formatter.new 85 | 86 | # Use a different logger for distributed setups. 87 | # require 'syslog/logger' 88 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 89 | 90 | if ENV["RAILS_LOG_TO_STDOUT"].present? 91 | logger = ActiveSupport::Logger.new(STDOUT) 92 | logger.formatter = config.log_formatter 93 | config.logger = ActiveSupport::TaggedLogging.new(logger) 94 | end 95 | 96 | # Do not dump schema after migrations. 97 | # config.active_record.dump_schema_after_migration = false 98 | end 99 | -------------------------------------------------------------------------------- /bookshelf/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | Rails.application.configure do 15 | # Settings specified here will take precedence over those in config/application.rb. 16 | 17 | # The test environment is used exclusively to run your application's 18 | # test suite. You never need to work with it otherwise. Remember that 19 | # your test database is "scratch space" for the test suite and is wiped 20 | # and recreated between test runs. Don't rely on the data there! 21 | config.cache_classes = true 22 | 23 | # Do not eager load code on boot. This avoids loading your whole application 24 | # just for the purpose of running a single test. If you are using a tool that 25 | # preloads Rails for running tests, you may have to set it to true. 26 | config.eager_load = false 27 | 28 | # Configure public file server for tests with Cache-Control for performance. 29 | config.public_file_server.enabled = true 30 | config.public_file_server.headers = { 31 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}" 32 | } 33 | 34 | # Show full error reports and disable caching. 35 | config.consider_all_requests_local = true 36 | config.action_controller.perform_caching = false 37 | 38 | # Raise exceptions instead of rendering exception templates. 39 | config.action_dispatch.show_exceptions = false 40 | 41 | # Disable request forgery protection in test environment. 42 | config.action_controller.allow_forgery_protection = false 43 | 44 | # Store uploaded files on the local file system in a temporary directory 45 | config.active_storage.service = :test 46 | 47 | # config.action_mailer.perform_caching = false 48 | 49 | # Tell Action Mailer not to deliver emails to the real world. 50 | # The :test delivery method accumulates sent emails in the 51 | # ActionMailer::Base.deliveries array. 52 | # config.action_mailer.delivery_method = :test 53 | 54 | # Print deprecation notices to the stderr. 55 | config.active_support.deprecation = :stderr 56 | 57 | # Raises error for missing translations 58 | # config.action_view.raise_on_missing_translations = true 59 | end 60 | -------------------------------------------------------------------------------- /bookshelf/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | # Be sure to restart your server when you modify this file. 15 | 16 | # Specify a serializer for the signed and encrypted cookie jars. 17 | # Valid options are :json, :marshal, and :hybrid. 18 | Rails.application.config.action_dispatch.cookies_serializer = :json 19 | -------------------------------------------------------------------------------- /bookshelf/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | # Be sure to restart your server when you modify this file. 15 | 16 | # Configure sensitive parameters which will be filtered from the log file. 17 | Rails.application.config.filter_parameters += [:password] 18 | -------------------------------------------------------------------------------- /bookshelf/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | # Be sure to restart your server when you modify this file. 15 | 16 | # Add new inflection rules using the following format. Inflections 17 | # are locale specific, and you may define rules for as many different 18 | # locales as you wish. All of these examples are active by default: 19 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 20 | # inflect.plural /^(ox)$/i, '\1en' 21 | # inflect.singular /^(ox)en/i, '\1' 22 | # inflect.irregular 'person', 'people' 23 | # inflect.uncountable %w( fish sheep ) 24 | # end 25 | 26 | # These inflection rules are supported but not enabled by default: 27 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 28 | # inflect.acronym 'RESTful' 29 | # end 30 | -------------------------------------------------------------------------------- /bookshelf/config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | # Be sure to restart your server when you modify this file. 15 | 16 | Rails.application.config.session_store :cookie_store, key: '_Bookshelf_session' 17 | -------------------------------------------------------------------------------- /bookshelf/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | # Be sure to restart your server when you modify this file. 15 | 16 | # This file contains settings for ActionController::ParamsWrapper which 17 | # is enabled by default. 18 | 19 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 20 | ActiveSupport.on_load(:action_controller) do 21 | wrap_parameters format: [:json] 22 | end 23 | 24 | # To enable root element in JSON for ActiveRecord objects. 25 | # ActiveSupport.on_load(:active_record) do 26 | # self.include_root_in_json = true 27 | # end 28 | -------------------------------------------------------------------------------- /bookshelf/config/routes.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | Rails.application.routes.draw do 15 | 16 | # Route root of application to BooksController#index action 17 | root "books#index" 18 | 19 | # Restful routes for BooksController 20 | resources :books 21 | 22 | get "/logs", to: "stackdriver#logs" 23 | get "/errors", to: "stackdriver#errors" 24 | 25 | end 26 | -------------------------------------------------------------------------------- /bookshelf/config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | # Be sure to restart your server when you modify this file. 15 | 16 | # Your secret key is used for verifying the integrity of signed cookies. 17 | # If you change this key, all old signed cookies will become invalid! 18 | 19 | # Make sure the secret is at least 30 characters and all random, 20 | # no regular words or you'll be exposed to dictionary attacks. 21 | # You can use `rake secret` to generate a secure secret key. 22 | 23 | # Make sure the secrets in this file are kept private 24 | # if you're sharing your code publicly. 25 | 26 | development: 27 | secret_key_base: db5c071ac181d90e429e9b3b9178f6ff2fbf6a9a68576dd666d85faa0aa9e3e4db59991e24a2a723524e42c1dbc61f0bf1a77451d43569d46f0e3c16b59170e3 28 | 29 | test: 30 | secret_key_base: c2b72729ddf4d841a9e2225b3233f6eb3b42fc8d2585db157161ed44e7760fbec64a694434573b5e5c7fbb20ae5969cfb13198d3dec19242e7d2eb3750406b24 31 | 32 | production: 33 | secret_key_base: 3659d9ac716f22a88f4235cae7c33e9a994492fb0dd5704350863c8bb8cc34571e568651578784185f2ea94a660769e586e72008fb801127df045b4aa4abb0ba 34 | # NOTE You should not keep production secrets in the repository, 35 | # instead read values from the environment: 36 | # secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 37 | -------------------------------------------------------------------------------- /bookshelf/images/moby-dick.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/getting-started-ruby/c3fd0fec8b680de32bf1cee16c5106665cbcd710/bookshelf/images/moby-dick.jpg -------------------------------------------------------------------------------- /bookshelf/public/404.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | The page you were looking for doesn't exist (404) 20 | 21 | 70 | 71 | 72 | 73 | 74 |
75 |
76 |

The page you were looking for doesn't exist.

77 |

You may have mistyped the address or the page may have moved.

78 |
79 |

If you are the application owner check the logs for more information.

80 |
81 | 82 | 83 | -------------------------------------------------------------------------------- /bookshelf/public/422.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | The change you wanted was rejected (422) 20 | 21 | 70 | 71 | 72 | 73 | 74 |
75 |
76 |

The change you wanted was rejected.

77 |

Maybe you tried to change something you didn't have access to.

78 |
79 |

If you are the application owner check the logs for more information.

80 |
81 | 82 | 83 | -------------------------------------------------------------------------------- /bookshelf/public/500.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | We're sorry, but something went wrong (500) 20 | 21 | 70 | 71 | 72 | 73 | 74 |
75 |
76 |

We're sorry, but something went wrong.

77 |
78 |

If you are the application owner check the logs for more information.

79 |
80 | 81 | 82 | -------------------------------------------------------------------------------- /bookshelf/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/getting-started-ruby/c3fd0fec8b680de32bf1cee16c5106665cbcd710/bookshelf/public/favicon.ico -------------------------------------------------------------------------------- /bookshelf/public/robots.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 15 | -------------------------------------------------------------------------------- /bookshelf/spec/book_extensions.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | # Additional methods added to the Book class for testing only. 15 | module BookExtensions 16 | 17 | def all 18 | collection.get 19 | end 20 | 21 | def first 22 | from_snapspot(all.first) 23 | end 24 | 25 | def count 26 | all.count 27 | end 28 | 29 | def delete_all 30 | collection.get do |book_snapshot| 31 | book_snapshot.ref.delete 32 | end 33 | end 34 | 35 | def exists? id 36 | find(id).present? 37 | end 38 | 39 | def create attributes = nil 40 | book = Book.new attributes 41 | book.save 42 | book 43 | end 44 | 45 | def create! attributes = nil 46 | book = Book.new attributes 47 | raise "Book save failed" unless book.save 48 | book 49 | end 50 | 51 | end 52 | -------------------------------------------------------------------------------- /bookshelf/spec/e2e_spec_helper.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | require "spec_helper" 15 | require "capybara/poltergeist" 16 | 17 | # use the poltergeist (phantomjs) driver for the test 18 | Capybara.current_driver = :poltergeist 19 | Capybara.server = :webrick 20 | 21 | RSpec.configure do |config| 22 | config.after :suite do 23 | if E2E.url? and ENV["E2E_URL"].nil? 24 | E2E.cleanup 25 | end 26 | end 27 | end 28 | 29 | class E2E 30 | @@sample_dir = "" 31 | 32 | class << self 33 | attr_accessor :sample_dir 34 | 35 | def url? 36 | not @url.nil? 37 | end 38 | 39 | def url 40 | deploy 41 | @url 42 | end 43 | 44 | def deploy 45 | if url? || @url = ENV["E2E_URL"] 46 | return 47 | end 48 | 49 | build_id = ENV["BUILD_ID"] || "test" 50 | 51 | version = "#{@sample_dir}-#{build_id}" 52 | 53 | # read in our credentials file 54 | project_id = ENV["GOOGLE_CLOUD_PROJECT"]; 55 | 56 | # deploy this sample to gcloud 57 | # try 3 times in case of intermittent deploy error 58 | for attempt in 0..3 59 | exec "gcloud app deploy --version=#{version} -q --no-promote" 60 | break if $?.to_i == 0 61 | end 62 | 63 | # if status is not 0, we tried 3 times and failed 64 | if $?.to_i != 0 65 | puts "Failed to deploy to gcloud" 66 | return $?.to_i 67 | end 68 | 69 | # sleeping 1 to ensure URL is callable 70 | sleep 1 71 | 72 | # run the specs for the step, but use the remote URL 73 | @url = "https://#{version}-dot-#{project_id}.appspot.com" 74 | 75 | # return 0, no errors 76 | return 0 77 | end 78 | 79 | def cleanup() 80 | # determine build number 81 | version = @url.match(/https:\/\/(.+)-dot-(.+).appspot.com/) 82 | unless version 83 | puts "you must pass a build ID or define ENV[\"BUILD_ID\"]" 84 | return 1 85 | end 86 | 87 | # run gcloud command 88 | exec "gcloud app versions delete #{version[1]} -q" 89 | 90 | # return the result of the gcloud delete command 91 | if $?.to_i != 0 92 | puts "Failed to delete e2e version" 93 | return $?.to_i 94 | end 95 | 96 | # return 0, no errors 97 | return 0 98 | end 99 | 100 | def exec(cmd) 101 | puts "> #{cmd}" 102 | puts `#{cmd}` 103 | end 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /bookshelf/spec/features/book_management_e2e_spec.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | require "e2e_spec_helper" 15 | 16 | E2E.sample_dir = "bookshelf" 17 | 18 | feature "Managing Books (e2e)" do 19 | 20 | scenario "Adding a book with missing fields (e2e)", :e2e do 21 | visit E2E.url 22 | click_link "Add Book" 23 | within "form.new_book" do 24 | click_button "Save" 25 | end 26 | 27 | expect(page).to have_content "Title can't be blank" 28 | end 29 | 30 | scenario "Adding a book (e2e)", :e2e do 31 | visit E2E.url 32 | 33 | click_link "Add Book" 34 | within "form.new_book" do 35 | fill_in "Title", with: "A Tale of Two Cities" 36 | fill_in "Author", with: "Charles Dickens" 37 | fill_in "Date Published", with: "1859-04-01" 38 | fill_in "Description", with: "A novel by Charles Dickens" 39 | click_button "Save" 40 | end 41 | 42 | expect(page).to have_content "Added Book" 43 | expect(page).to have_content "A Tale of Two Cities" 44 | end 45 | 46 | scenario "Listing all books (e2e)", :e2e do 47 | visit E2E.url 48 | 49 | expect(page).to have_content "A Tale of Two Cities" 50 | expect(page).to have_content "Charles Dickens" 51 | end 52 | 53 | scenario "Deleting a book (e2e)", :e2e do 54 | visit E2E.url 55 | first(:link , "A Tale of Two Cities").click 56 | click_link "Delete Book" 57 | 58 | expect(current_path).to eq '/books' 59 | end 60 | 61 | end 62 | -------------------------------------------------------------------------------- /bookshelf/spec/features/book_management_spec.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | require "spec_helper" 15 | 16 | feature "Managing Books" do 17 | 18 | scenario "No books have been added" do 19 | visit "/" 20 | 21 | expect(page).to have_content "No books found" 22 | end 23 | 24 | scenario "Listing all books" do 25 | Book.create! title: "A Tale of Two Cities", author: "Charles Dickens" 26 | 27 | visit "/" 28 | 29 | expect(page).to have_content "A Tale of Two Cities" 30 | expect(page).to have_content "Charles Dickens" 31 | end 32 | 33 | scenario "Paginating through list of books" do 34 | Book.delete_all 35 | Book.create! title: "Book 1" 36 | Book.create! title: "Book 2" 37 | Book.create! title: "Book 3" 38 | Book.create! title: "Book 4" 39 | Book.create! title: "Book 5" 40 | 41 | stub_const "BooksController::PER_PAGE", 2 42 | 43 | visit "/" 44 | expect(all(".book").length).to eq 2 45 | 46 | click_link "More" 47 | expect(all(".book").length).to eq 2 48 | 49 | click_link "More" 50 | expect(all(".book").length).to eq 1 51 | 52 | expect(page).not_to have_link "More" 53 | end 54 | 55 | scenario "Adding a book" do 56 | Book.delete_all 57 | 58 | visit "/" 59 | click_link "Add Book" 60 | within "form.new_book" do 61 | fill_in "Title", with: "A Tale of Two Cities" 62 | fill_in "Author", with: "Charles Dickens" 63 | fill_in "Date Published", with: "1859-04-01" 64 | fill_in "Description", with: "A novel by Charles Dickens" 65 | click_button "Save" 66 | end 67 | 68 | expect(page).to have_content "Added Book" 69 | 70 | book = Book.first 71 | expect(book.title).to eq "A Tale of Two Cities" 72 | expect(book.author).to eq "Charles Dickens" 73 | expect(book.published_on).to eq "1859-04-01" 74 | expect(book.description).to eq "A novel by Charles Dickens" 75 | end 76 | 77 | scenario "Adding a book with missing fields" do 78 | Book.delete_all 79 | visit "/" 80 | click_link "Add Book" 81 | within "form.new_book" do 82 | click_button "Save" 83 | end 84 | 85 | expect(page).to have_content "Title can't be blank" 86 | 87 | within "form.new_book" do 88 | fill_in "Title", with: "A Tale of Two Cities" 89 | click_button "Save" 90 | end 91 | 92 | expect(Book.first.title).to eq "A Tale of Two Cities" 93 | end 94 | 95 | scenario "Editing a book" do 96 | Book.delete_all 97 | book = Book.create! title: "A Tale of Two Cities", author: "Charles Dickens" 98 | 99 | visit "/" 100 | click_link "A Tale of Two Cities" 101 | click_link "Edit Book" 102 | fill_in "Title", with: "CHANGED!" 103 | click_button "Save" 104 | 105 | expect(page).to have_content "Updated Book" 106 | 107 | book = Book.find book.id 108 | expect(book.title).to eq "CHANGED!" 109 | expect(book.author).to eq "Charles Dickens" 110 | end 111 | 112 | scenario "Editing a book with missing fields" do 113 | Book.delete_all 114 | book = Book.create! title: "A Tale of Two Cities" 115 | 116 | visit "/" 117 | click_link "A Tale of Two Cities" 118 | click_link "Edit Book" 119 | fill_in "Title", with: "" 120 | click_button "Save" 121 | 122 | expect(page).to have_content "Title can't be blank" 123 | book = Book.find book.id 124 | expect(book.title).to eq "A Tale of Two Cities" 125 | 126 | within "form.edit_book" do 127 | fill_in "Title", with: "CHANGED!" 128 | click_button "Save" 129 | end 130 | 131 | book = Book.find book.id 132 | expect(book.title).to eq "CHANGED!" 133 | end 134 | 135 | scenario "Deleting a book" do 136 | Book.delete_all 137 | book = Book.create! title: "A Tale of Two Cities", author: "Charles Dickens" 138 | expect(Book.exists? book.id).to be true 139 | 140 | visit "/" 141 | click_link "A Tale of Two Cities" 142 | click_link "Delete Book" 143 | 144 | expect(Book.exists? book.id).to be false 145 | end 146 | 147 | end 148 | -------------------------------------------------------------------------------- /bookshelf/spec/models/book_spec.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | require "spec_helper" 15 | 16 | RSpec.describe Book do 17 | 18 | it "requires a title" do 19 | expect(Book.new title: nil).not_to be_valid 20 | expect(Book.new title: "title").to be_valid 21 | end 22 | 23 | end 24 | -------------------------------------------------------------------------------- /bookshelf/spec/setup.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # Copyright 2015 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # download cloud-datastore-emulator testing tool 17 | wget -q https://storage.googleapis.com/gcd/tools/cloud-datastore-emulator-1.1.1.zip -O cloud-datastore-emulator.zip 18 | unzip -o cloud-datastore-emulator.zip 19 | 20 | # start cloud-datastore-emulator test server 21 | cloud-datastore-emulator/cloud_datastore_emulator create gcd-test-dataset-directory 22 | cloud-datastore-emulator/cloud_datastore_emulator start --testing ./gcd-test-dataset-directory/ & 23 | 24 | # compile assets directory 25 | RAILS_ENV=test bundle exec rake --rakefile=Rakefile assets:precompile 26 | -------------------------------------------------------------------------------- /bookshelf/spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | ENV["RAILS_ENV"] ||= "test" 15 | 16 | require File.expand_path("../../config/environment", __FILE__) 17 | 18 | require "capybara/rspec" 19 | require "capybara/rails" 20 | require "book_extensions" 21 | 22 | Book.send :extend, BookExtensions 23 | 24 | RSpec.configure do |config| 25 | # Retry setup 26 | # show retry status in spec process 27 | config.verbose_retry = true 28 | # show exception that triggers a retry if verbose_retry is set to true 29 | config.display_try_failure_messages = true 30 | 31 | # set retry count and retry sleep interval to 10 seconds 32 | config.default_retry_count = 3 33 | config.default_sleep_interval = 3 34 | 35 | config.expect_with :rspec do |expectations| 36 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 37 | end 38 | 39 | config.before :all do |example| 40 | Book.delete_all 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /credentials.json.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/getting-started-ruby/c3fd0fec8b680de32bf1cee16c5106665cbcd710/credentials.json.enc -------------------------------------------------------------------------------- /gce/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 6 | 7 | gem "sinatra", "~> 2.0" 8 | -------------------------------------------------------------------------------- /gce/README.md: -------------------------------------------------------------------------------- 1 | # Running Ruby apps on Google Compute Engine 2 | 3 | * Creates a Google Compute instance which runs a simple, hello-world sinatra app behind nginx. 4 | 5 | * To run, follow Ruby's [Getting started on Compute Engine](https://cloud.google.com/ruby/tutorials/getting-started-on-compute-engine) tutorial. 6 | 7 | ## Contributing changes 8 | 9 | * See [CONTRIBUTING.md](../CONTRIBUTING.md) 10 | 11 | ## Licensing 12 | 13 | * See [LICENSE](../LICENSE) 14 | -------------------------------------------------------------------------------- /gce/app.rb: -------------------------------------------------------------------------------- 1 | require "sinatra" 2 | 3 | get "/" do 4 | "

Hello World

" 5 | end 6 | -------------------------------------------------------------------------------- /gce/deploy.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # [START getting_started_gce_create_instance] 16 | MY_INSTANCE_NAME="my-app-instance" 17 | ZONE=us-central1-a 18 | 19 | gcloud compute instances create $MY_INSTANCE_NAME \ 20 | --image-family=debian-9 \ 21 | --image-project=debian-cloud \ 22 | --machine-type=g1-small \ 23 | --scopes userinfo-email,cloud-platform \ 24 | --metadata-from-file startup-script=startup-script.sh \ 25 | --zone $ZONE \ 26 | --tags http-server 27 | # [END getting_started_gce_create_instance] 28 | 29 | # [START getting_started_gce_allow_port] 30 | gcloud compute firewall-rules create default-allow-http-80 \ 31 | --allow tcp:80 \ 32 | --source-ranges 0.0.0.0/0 \ 33 | --target-tags http-server \ 34 | --description "Allow port 80 access to http-server" 35 | # [END getting_started_gce_allow_port] 36 | -------------------------------------------------------------------------------- /gce/rubyapp.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | listen [::]:80 default_server; 4 | 5 | location / { 6 | proxy_pass http://localhost:4567; 7 | proxy_set_header Host $host; 8 | proxy_set_header X-Real-IP $remote_addr; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /gce/startup-script.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # [START getting_started_gce_startup_script] 16 | # Install Stackdriver logging agent 17 | curl -sSO https://dl.google.com/cloudagents/install-logging-agent.sh 18 | bash install-logging-agent.sh 19 | 20 | # Install dependencies 21 | apt-get update && apt-get -y upgrade && apt-get install -y autoconf bison \ 22 | build-essential git libssl-dev libyaml-dev libreadline6-dev zlib1g-dev \ 23 | libncurses5-dev libffi-dev libgdbm3 libgdbm-dev nginx supervisor 24 | 25 | # Account to own server process 26 | useradd -m -d /home/rubyapp rubyapp 27 | 28 | # Install Ruby and Bundler 29 | mkdir /home/rubyapp/.ruby 30 | git clone https://github.com/rbenv/ruby-build.git /home/rubyapp/.ruby-build 31 | /home/rubyapp/.ruby-build/bin/ruby-build 2.6.5 /home/rubyapp/.ruby 32 | 33 | chown -R rubyapp:rubyapp /home/rubyapp 34 | 35 | cat >/home/rubyapp/.profile << EOF 36 | export PATH="/home/rubyapp/.ruby/bin:$PATH" 37 | EOF 38 | 39 | su -l rubyapp -c "gem install bundler" 40 | 41 | # Fetch source code 42 | git clone https://github.com/GoogleCloudPlatform/getting-started-ruby.git /opt/app 43 | 44 | # Set ownership to newly created account 45 | chown -R rubyapp:rubyapp /opt/app 46 | 47 | # Install ruby dependencies 48 | su -l rubyapp -c "cd /opt/app/gce && bundle install" 49 | 50 | # Disable the default NGINX configuration 51 | rm /etc/nginx/sites-enabled/default 52 | 53 | # Enable our NGINX configuration 54 | cp /opt/app/gce/rubyapp.conf /etc/nginx/sites-available/rubyapp.conf 55 | ln -s /etc/nginx/sites-available/rubyapp.conf /etc/nginx/sites-enabled/rubyapp.conf 56 | 57 | # Start NGINX 58 | systemctl restart nginx.service 59 | 60 | # Configure supervisor to run the ruby app 61 | cat >/etc/supervisor/conf.d/rubyapp.conf << EOF 62 | [program:rubyapp] 63 | directory=/opt/app/gce 64 | command=bash -lc "bundle exec ruby app.rb" 65 | autostart=true 66 | autorestart=true 67 | user=rubyapp 68 | environment=HOME="/home/rubyapp",USER="rubyapp" 69 | stdout_logfile=syslog 70 | stderr_logfile=syslog 71 | EOF 72 | 73 | supervisorctl reread 74 | supervisorctl update 75 | 76 | # Application should now be running under supervisor 77 | # [END getting_started_gce_startup_script] 78 | -------------------------------------------------------------------------------- /gce/teardown.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # Copyright 2019 Google LLC All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -x 18 | 19 | # [START getting_started_gce_teardown] 20 | MY_INSTANCE_NAME="my-app-instance" 21 | ZONE=us-central1-a 22 | 23 | gcloud compute instances delete $MY_INSTANCE_NAME \ 24 | --zone=$ZONE --delete-disks=all 25 | 26 | gcloud compute firewall-rules delete default-allow-http-80 27 | # [END getting_started_gce_teardown] 28 | -------------------------------------------------------------------------------- /sessions/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } 6 | 7 | # [START getting_started_sessions_requirements] 8 | gem "google-cloud-firestore", "~> 1.2" 9 | gem "sinatra", "~> 2.0" 10 | # [END getting_started_sessions_requirements] 11 | 12 | group :development do 13 | gem "minitest", "~> 5.13" 14 | gem "rack-test", "~> 1.1" 15 | gem "rake", "~> 13.0" 16 | end 17 | -------------------------------------------------------------------------------- /sessions/README.md: -------------------------------------------------------------------------------- 1 | # Session handling with Firestore 2 | 3 | * A short App Engine app, which uses [Firestore](https://cloud.google.com/firestore/) to serve a webpage saying `"#{session['views']} views for #{session['greeting']}"`. Here, `session["greeting"]` is one of `["Hello World", "Hallo Welt", "Ciao Mondo", "Salut le Monde", "Hola Mundo"]` which has been randomly assigned to the viewer, and `session["views"]` is the number of times that viewer has loaded the page. 4 | * To run, follow Ruby's [Handling sessions with Cloud Firestore](https://cloud.google.com/ruby/getting-started/session-handling-with-firestore) tutorial. 5 | 6 | ## Contributing changes 7 | 8 | * See [CONTRIBUTING.md](../CONTRIBUTING.md) 9 | 10 | ## Licensing 11 | 12 | * See [LICENSE](../LICENSE) 13 | -------------------------------------------------------------------------------- /sessions/Rakefile: -------------------------------------------------------------------------------- 1 | require "rake/testtask" 2 | 3 | desc "Run tests." 4 | Rake::TestTask.new :test do |t| 5 | t.test_files = FileList["test/**/*_test.rb"] 6 | t.warning = false 7 | end 8 | 9 | Rake::TestTask.new :acceptance do |t| 10 | t.test_files = FileList["acceptance/**/*_test.rb"] 11 | t.warning = false 12 | end 13 | -------------------------------------------------------------------------------- /sessions/acceptance/app_test.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path "../../test/test_helper.rb", __FILE__ 2 | 3 | include Rack::Test::Methods 4 | 5 | def app 6 | Sinatra::Application 7 | end 8 | 9 | def firestore 10 | firestore = Google::Cloud::Firestore.new 11 | end 12 | 13 | def col 14 | col = firestore.col "sessions" 15 | end 16 | 17 | def docs 18 | docs = col.list_documents 19 | end 20 | 21 | def delete_all_sessions 22 | docs.each do |doc| 23 | firestore.batch do |b| 24 | b.delete doc.document_path 25 | end 26 | end 27 | end 28 | 29 | describe "app" do 30 | before do 31 | delete_all_sessions 32 | end 33 | 34 | after do 35 | delete_all_sessions 36 | end 37 | 38 | it "should display the number of views" do 39 | get "/" 40 | assert_match /\d+ views for /, last_response.body 41 | end 42 | 43 | it "should increment the number of views on successive views" do 44 | get "/" 45 | view_count = last_response.body.match(/(\d+)\s/)[1].to_i 46 | get "/" 47 | updated_view_count = last_response.body.match(/(\d+)\s/)[1].to_i 48 | assert_equal view_count + 1, updated_view_count 49 | end 50 | 51 | it "should not change the greeting on successive views" do 52 | get "/" 53 | greeting = last_response.body.match(/(\d|\s|\w)*(".*")/)[2] 54 | get "/" 55 | updated_greeting = last_response.body.match(/(\d|\s|\w)*(".*")/)[2] 56 | assert_equal greeting, updated_greeting 57 | end 58 | 59 | it "should store the data in firestore" do 60 | 3.times do 61 | get "/" 62 | view_count = last_response.body.match(/(\d+)\s/)[1].to_i 63 | greeting = last_response.body.match(/(\d|\s|\w)*"(.*)"/)[2] 64 | doc = col.doc(docs.first.document_id).get 65 | assert_equal doc.fields[:greeting], greeting 66 | assert_equal doc.fields[:views], view_count 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /sessions/app.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019, Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | # [START getting_started_session_app] 15 | require "sinatra" 16 | 17 | require_relative "firestore_session" 18 | 19 | use Rack::Session::FirestoreSession 20 | 21 | set :greetings, ["Hello World", "Hallo Welt", "Ciao Mondo", "Salut le Monde", "Hola Mundo"] 22 | 23 | get "/" do 24 | session[:greeting] ||= settings.greetings.sample 25 | session[:views] ||= 0 26 | session[:views] += 1 27 | "

#{session[:views]} views for \"#{session[:greeting]}\"

" 28 | end 29 | # [END getting_started_session_app] 30 | -------------------------------------------------------------------------------- /sessions/app.yaml: -------------------------------------------------------------------------------- 1 | # [START getting_started_sessions_runtime] 2 | runtime: ruby25 3 | entrypoint: bundle exec ruby app.rb 4 | # [END getting_started_sessions_runtime] 5 | -------------------------------------------------------------------------------- /sessions/firestore_session.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019, Google LLC. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | # [START getting_started_session] 15 | require "google/cloud/firestore" 16 | require "rack/session/abstract/id" 17 | 18 | module Rack 19 | module Session 20 | class FirestoreSession < Abstract::Persisted 21 | def initialize app, options = {} 22 | super 23 | 24 | @firestore = Google::Cloud::Firestore.new 25 | @col = @firestore.col "sessions" 26 | end 27 | 28 | def find_session _req, session_id 29 | return [generate_sid, {}] if session_id.nil? 30 | 31 | doc = @col.doc session_id 32 | fields = doc.get.fields || {} 33 | [session_id, stringify_keys(fields)] 34 | end 35 | 36 | def write_session _req, session_id, new_session, _opts 37 | doc = @col.doc session_id 38 | doc.set new_session, merge: true 39 | session_id 40 | end 41 | 42 | def delete_session _req, session_id, _opts 43 | doc = @col.doc session_id 44 | doc.delete 45 | generate_sid 46 | end 47 | 48 | def stringify_keys hash 49 | new_hash = {} 50 | hash.each do |k, v| 51 | new_hash[k.to_s] = 52 | if v.is_a? Hash 53 | stringify_keys v 54 | else 55 | v 56 | end 57 | end 58 | new_hash 59 | end 60 | end 61 | end 62 | end 63 | # [END getting_started_session] 64 | -------------------------------------------------------------------------------- /sessions/test/app_test.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path "../test_helper.rb", __FILE__ 2 | 3 | include Rack::Test::Methods 4 | 5 | def app 6 | Sinatra::Application 7 | end 8 | 9 | class FirestoreSessionMock < Rack::Session::Abstract::Persisted 10 | def initialize app, options = {} 11 | super 12 | @data = {} 13 | end 14 | 15 | def find_session _req, session_id 16 | return [generate_sid, {}] if session_id.nil? 17 | return session_id, @data[session_id] 18 | end 19 | 20 | def write_session _req, session_id, new_session, _opts 21 | @data[session_id] = new_session 22 | session_id 23 | end 24 | 25 | def delete_session _req, session_id, _opts 26 | @data.delete[session_id] 27 | generate_sid 28 | end 29 | end 30 | 31 | describe "app" do 32 | before do 33 | @mock_session = lambda do |app, options = {}| 34 | FirestoreSessionMock.new app, {} 35 | end 36 | end 37 | 38 | it "should display a greeting" do 39 | Rack::Session::FirestoreSession.stub :new, @mock_session do 40 | get "/" 41 | greetings = /"Hello World"|"Hallo Welt"|"Ciao Mondo"|"Salut le Monde"|"Hola Mundo"/ 42 | assert_match greetings, last_response.body 43 | end 44 | 45 | end 46 | 47 | it "should display the number of views" do 48 | Rack::Session::FirestoreSession.stub :new, @mock_session do 49 | get "/" 50 | assert_match /\d+ views for /, last_response.body 51 | end 52 | end 53 | 54 | it "should increment the number of views on successive views" do 55 | Rack::Session::FirestoreSession.stub :new, @mock_session do 56 | get "/" 57 | view_count = last_response.body.match(/(\d+)\s/)[1].to_i 58 | get "/" 59 | updated_view_count = last_response.body.match(/(\d+)\s/)[1].to_i 60 | assert_equal view_count + 1, updated_view_count 61 | end 62 | end 63 | 64 | it "should not change the greeting on successive views" do 65 | Rack::Session::FirestoreSession.stub :new, @mock_session do 66 | get "/" 67 | greeting = last_response.body.match(/(\d|\s|\w)*(".*")/)[2] 68 | 4.times do 69 | get "/" 70 | updated_greeting = last_response.body.match(/(\d|\s|\w)*(".*")/)[2] 71 | assert_equal greeting, updated_greeting 72 | end 73 | end 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /sessions/test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV["RACK_ENV"] = "test" 2 | require "minitest/autorun" 3 | require "rack/test" 4 | 5 | require File.expand_path "../../app.rb", __FILE__ 6 | --------------------------------------------------------------------------------