├── .canvas ├── .github └── workflows │ └── canvas-sync-html.yml ├── .gitignore ├── .rspec ├── CONTRIBUTING.md ├── Gemfile ├── Gemfile.lock ├── LICENSE.md ├── README.md ├── Rakefile ├── app ├── controllers │ └── application_controller.rb └── models │ └── .gitkeep ├── config.ru ├── config ├── database.yml └── environment.rb ├── db └── seeds.rb └── spec └── spec_helper.rb /.canvas: -------------------------------------------------------------------------------- 1 | --- 2 | :lessons: 3 | - :id: 121173 4 | :course_id: 3299 5 | :canvas_url: https://learning.flatironschool.com/courses/3299/pages/121173 6 | :type: page 7 | -------------------------------------------------------------------------------- /.github/workflows/canvas-sync-html.yml: -------------------------------------------------------------------------------- 1 | name: Sync with Canvas HTML 2 | 3 | on: 4 | push: 5 | branches: [master, main] 6 | paths: 7 | - "README.md" 8 | 9 | jobs: 10 | sync: 11 | name: Sync with Canvas 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - name: Set up Ruby 19 | uses: ruby/setup-ruby@v1 20 | with: 21 | ruby-version: 2.6 22 | 23 | - name: Install github-to-canvas 24 | run: gem install github-to-canvas 25 | 26 | # Secret stored in learn-co-curriculum Settings/Secrets 27 | - name: Sync from .canvas file 28 | run: github-to-canvas -a -lr --forkable --contains-html 29 | env: 30 | CANVAS_API_KEY: ${{ secrets.CANVAS_API_KEY }} 31 | CANVAS_API_PATH: ${{ secrets.CANVAS_API_PATH }} 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-* 13 | 14 | # Byebug 15 | .byebug_history 16 | 17 | # Canvas-specific .results.json 18 | .results.json 19 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | --format documentation 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Learn.co Curriculum 2 | 3 | We're really excited that you're about to contribute to the 4 | [open curriculum](https://learn.co/content-license) on 5 | [Learn.co](https://learn.co). If this is your first time contributing, please 6 | continue reading to learn how to make the most meaningful and useful impact 7 | possible. 8 | 9 | ## Raising an Issue to Encourage a Contribution 10 | 11 | If you notice a problem with the curriculum that you believe needs improvement 12 | but you're unable to make the change yourself, you should raise a Github issue 13 | containing a clear description of the problem. Include relevant snippets of the 14 | content and/or screenshots if applicable. Curriculum owners regularly review 15 | issue lists and your issue will be prioritized and addressed as appropriate. 16 | 17 | ## Submitting a Pull Request to Suggest an Improvement 18 | 19 | If you see an opportunity for improvement and can make the change yourself go 20 | ahead and use a typical git workflow to make it happen: 21 | 22 | - Fork this curriculum repository 23 | - Make the change on your fork, with descriptive commits in the standard format 24 | - Open a Pull Request against this repo 25 | 26 | A curriculum owner will review your change and approve or comment on it in due 27 | course. 28 | 29 | ## Why Contribute? 30 | 31 | Curriculum on Learn is publicly and freely available under Learn's 32 | [Educational Content License](https://learn.co/content-license). By embracing an 33 | open-source contribution model, our goal is for the curriculum on Learn to 34 | become, in time, the best educational content the world has ever seen. 35 | 36 | We need help from the community of Learners to maintain and improve the 37 | educational content. Everything from fixing typos, to correcting out-dated 38 | information, to improving exposition, to adding better examples, to fixing 39 | tests—all contributions to making the curriculum more effective are welcome. 40 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | # A DSL for quickly creating web applications 4 | # https://github.com/sinatra/sinatra 5 | gem "sinatra", "~> 2.1" 6 | 7 | # A fast and simple web server 8 | # https://github.com/macournoyer/thin 9 | gem "thin", "~> 1.8" 10 | 11 | # Rack middleware. Used specifically for parsing the request body into params. 12 | # https://github.com/rack/rack-contrib 13 | gem "rack-contrib", "~> 2.3" 14 | 15 | # More Rack middleware! Used to handle CORS requests 16 | # https://github.com/cyu/rack-cors 17 | gem "rack-cors", "~> 1.1" 18 | 19 | # An object-relational mapper 20 | # https://guides.rubyonrails.org/active_record_basics.html 21 | gem "activerecord", "~> 6.1" 22 | 23 | # Configures common Rake tasks for working with Active Record 24 | # https://github.com/sinatra-activerecord/sinatra-activerecord 25 | gem "sinatra-activerecord", "~> 2.0" 26 | 27 | # Run common tasks from the command line 28 | # https://github.com/ruby/rake 29 | gem "rake", "~> 13.0" 30 | 31 | # Provides functionality to interact with a SQLite3 database 32 | gem "sqlite3", "~> 1.4" 33 | 34 | # Require all files in a folder 35 | gem "require_all", "~> 3.0" 36 | 37 | # These gems will only be used when we are running the application locally 38 | group :development do 39 | gem "pry", "~> 0.14.1" 40 | 41 | # Automatically reload when there are changes 42 | # https://github.com/alexch/rerun 43 | gem "rerun" 44 | end 45 | 46 | # These gems will only be used when we are running tests 47 | group :test do 48 | gem "database_cleaner", "~> 2.0" 49 | gem "rack-test", "~> 1.1" 50 | gem "rspec", "~> 3.10" 51 | gem "rspec-json_expectations", "~> 2.2" 52 | end 53 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activemodel (6.1.4) 5 | activesupport (= 6.1.4) 6 | activerecord (6.1.4) 7 | activemodel (= 6.1.4) 8 | activesupport (= 6.1.4) 9 | activesupport (6.1.4) 10 | concurrent-ruby (~> 1.0, >= 1.0.2) 11 | i18n (>= 1.6, < 2) 12 | minitest (>= 5.1) 13 | tzinfo (~> 2.0) 14 | zeitwerk (~> 2.3) 15 | coderay (1.1.3) 16 | concurrent-ruby (1.1.9) 17 | daemons (1.4.0) 18 | database_cleaner (2.0.1) 19 | database_cleaner-active_record (~> 2.0.0) 20 | database_cleaner-active_record (2.0.1) 21 | activerecord (>= 5.a) 22 | database_cleaner-core (~> 2.0.0) 23 | database_cleaner-core (2.0.1) 24 | diff-lcs (1.4.4) 25 | eventmachine (1.2.7) 26 | ffi (1.15.3) 27 | i18n (1.8.10) 28 | concurrent-ruby (~> 1.0) 29 | listen (3.5.1) 30 | rb-fsevent (~> 0.10, >= 0.10.3) 31 | rb-inotify (~> 0.9, >= 0.9.10) 32 | method_source (1.0.0) 33 | minitest (5.14.4) 34 | mustermann (1.1.1) 35 | ruby2_keywords (~> 0.0.1) 36 | pry (0.14.1) 37 | coderay (~> 1.1) 38 | method_source (~> 1.0) 39 | rack (2.2.3) 40 | rack-contrib (2.3.0) 41 | rack (~> 2.0) 42 | rack-cors (1.1.1) 43 | rack (>= 2.0.0) 44 | rack-protection (2.1.0) 45 | rack 46 | rack-test (1.1.0) 47 | rack (>= 1.0, < 3) 48 | rake (13.0.6) 49 | rb-fsevent (0.11.0) 50 | rb-inotify (0.10.1) 51 | ffi (~> 1.0) 52 | require_all (3.0.0) 53 | rerun (0.13.1) 54 | listen (~> 3.0) 55 | rspec (3.10.0) 56 | rspec-core (~> 3.10.0) 57 | rspec-expectations (~> 3.10.0) 58 | rspec-mocks (~> 3.10.0) 59 | rspec-core (3.10.1) 60 | rspec-support (~> 3.10.0) 61 | rspec-expectations (3.10.1) 62 | diff-lcs (>= 1.2.0, < 2.0) 63 | rspec-support (~> 3.10.0) 64 | rspec-json_expectations (2.2.0) 65 | rspec-mocks (3.10.2) 66 | diff-lcs (>= 1.2.0, < 2.0) 67 | rspec-support (~> 3.10.0) 68 | rspec-support (3.10.2) 69 | ruby2_keywords (0.0.4) 70 | sinatra (2.1.0) 71 | mustermann (~> 1.0) 72 | rack (~> 2.2) 73 | rack-protection (= 2.1.0) 74 | tilt (~> 2.0) 75 | sinatra-activerecord (2.0.23) 76 | activerecord (>= 4.1) 77 | sinatra (>= 1.0) 78 | sqlite3 (1.4.2) 79 | thin (1.8.1) 80 | daemons (~> 1.0, >= 1.0.9) 81 | eventmachine (~> 1.0, >= 1.0.4) 82 | rack (>= 1, < 3) 83 | tilt (2.0.10) 84 | tzinfo (2.0.4) 85 | concurrent-ruby (~> 1.0) 86 | zeitwerk (2.4.2) 87 | 88 | PLATFORMS 89 | x86_64-darwin-19 90 | 91 | DEPENDENCIES 92 | activerecord (~> 6.1) 93 | database_cleaner (~> 2.0) 94 | pry (~> 0.14.1) 95 | rack-contrib (~> 2.3) 96 | rack-cors (~> 1.1) 97 | rack-test (~> 1.1) 98 | rake (~> 13.0) 99 | require_all (~> 3.0) 100 | rerun 101 | rspec (~> 3.10) 102 | rspec-json_expectations (~> 2.2) 103 | sinatra (~> 2.1) 104 | sinatra-activerecord (~> 2.0) 105 | sqlite3 (~> 1.4) 106 | thin (~> 1.8) 107 | 108 | BUNDLED WITH 109 | 2.2.23 110 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # Learn.co Educational Content License 2 | 3 | Copyright (c) 2021 Flatiron School, Inc 4 | 5 | The Flatiron School, Inc. owns this Educational Content. However, the Flatiron 6 | School supports the development and availability of educational materials in the 7 | public domain. Therefore, the Flatiron School grants Users of the Flatiron 8 | Educational Content set forth in this repository certain rights to reuse, build 9 | upon and share such Educational Content subject to the terms of the Educational 10 | Content License set forth [here](http://learn.co/content-license) 11 | (http://learn.co/content-license). You must read carefully the terms and 12 | conditions contained in the Educational Content License as such terms govern 13 | access to and use of the Educational Content. 14 | 15 | Flatiron School is willing to allow you access to and use of the Educational 16 | Content only on the condition that you accept all of the terms and conditions 17 | contained in the Educational Content License set forth 18 | [here](http://learn.co/content-license) (http://learn.co/content-license). By 19 | accessing and/or using the Educational Content, you are agreeing to all of the 20 | terms and conditions contained in the Educational Content License. If you do not 21 | agree to any or all of the terms of the Educational Content License, you are 22 | prohibited from accessing, reviewing or using in any way the Educational 23 | Content. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Phase 3 Project Guidelines 2 | 3 | ## Learning Goals 4 | 5 | - Build a web basic API with Sinatra and Active Record to support a React 6 | frontend 7 | 8 | ## Introduction 9 | 10 | Congrats on getting through all the material for Phase 3! Now's the time to put 11 | it all together and build something from scratch to reinforce what you know and 12 | expand your horizons. 13 | 14 | The focus of this project is **building a Sinatra API backend** that uses 15 | **Active Record** to access and persist data in a database, which will be used 16 | by a separate **React frontend** that interacts with the database via the API. 17 | 18 | ## Requirements 19 | 20 | For this project, you must: 21 | 22 | - Use Active Record to interact with a database. 23 | - Have at least two models with a one-to-many relationship. 24 | - At a minimum, set up the following API routes in Sinatra: 25 | - create and read actions for both models 26 | - full CRUD capability for one of the models: 27 | The update action should be implemented using a form that is 28 | pre-filled with existing values for the object. On submission of 29 | the form, the object should update. Note: Using a like button or 30 | similar will not meet the update requirement. 31 | - Build a separate React frontend application that interacts with the API to 32 | perform CRUD actions. 33 | - Implement proper front end state management. You should be updating state using a 34 | setState function after receiving your response from a POST, PATCH, or DELETE 35 | request. You should NOT be relying on a GET request to update state. 36 | - Use good OO design patterns. You should have separate classes for each of your 37 | models, and create instance and class methods as necessary. 38 | - Routes in your application (both client side and back end) should follow RESTful 39 | conventions. 40 | - Use your back end optimally. Pass JSON for related associations to the front 41 | end from the back end. You should use active record methods in your controller to grab 42 | the needed data from your database and provide as JSON to the front end. You 43 | should NOT be relying on filtering front end state or a separate fetch request to 44 | retrieve related data. 45 | 46 | For example, build a todo list application with a React frontend interface and a 47 | Sinatra backend API, where a user can: 48 | 49 | - **Create** a new todo 50 | - **Read** a list of all todos 51 | - **Update** an individual todo 52 | - **Delete** a todo 53 | 54 | A `Todo` can be tagged with a `Category`, so that each todo _belongs to_ a 55 | category and each category _has many_ todos. 56 | 57 | ## Getting Started 58 | 59 | ### Backend Setup 60 | 61 | This repository has all the starter code needed to get a Sinatra backend up and 62 | running. [**Fork and clone**][fork link] this repository to get started. Then, run 63 | `bundle install` to install the gems. 64 | 65 | **Important**: Be sure you fork a copy of the repo into your GitHub account 66 | before cloning it. You can do this by using the link above or by clicking the 67 | "Octocat" button at the top of this page, then clicking "Fork" in the upper 68 | right corner of the repo page. 69 | 70 | [fork link]: https://github.com/learn-co-curriculum/phase-3-sinatra-react-project/fork 71 | 72 | The `app/controllers/application_controller.rb` file has an example GET route 73 | handler. Replace this route with routes for your project. 74 | 75 | You can start your server with: 76 | 77 | ```console 78 | $ bundle exec rake server 79 | ``` 80 | 81 | This will run your server on port 82 | [http://localhost:9292](http://localhost:9292). 83 | 84 | ### Frontend Setup 85 | 86 | Your backend and your frontend should be in **two different repositories**. 87 | 88 | Create a new repository in a **separate folder** with a React app for your 89 | frontend. To do this, `cd` out of the backend project directory, and use 90 | [create-react-app][] to generate the necessary code for your React frontend: 91 | 92 | ```console 93 | $ npx create-react-app my-app-frontend 94 | ``` 95 | 96 | After creating the project locally, you should also 97 | [create a repository on GitHub][create repo] to host your repo and help 98 | collaborate, if you're working with a partner. 99 | 100 | ### Fetch Example 101 | 102 | Your React app should make fetch requests to your Sinatra backend! Here's an 103 | example: 104 | 105 | ```js 106 | fetch("http://localhost:9292/test") 107 | .then((r) => r.json()) 108 | .then((data) => console.log(data)); 109 | ``` 110 | 111 | ## Project Tips 112 | 113 | - This project is intended to focus more on the backend than the frontend, so 114 | try and keep the React side of things relatively simple. Focus on working with 115 | Active Record and performing CRUD actions. What are some interesting queries you can write? What kinds of questions can you ask of your data? 116 | - Once you have a project idea, come up with a domain model and decide what 117 | relationships exist between the models in your application. Use a tool like 118 | [dbdiagram.io][] to help visualize your models. 119 | - Decide on your API endpoints. What data should they return? What kind of CRUD 120 | action should they perform? What data do they need from the client? 121 | - Use [Postman][postman download] to test your endpoints. 122 | - Use `binding.pry` to debug your requests on the server. It's very helpful to use a 123 | `binding.pry` in your controller within a route to see what `params` are being 124 | sent. 125 | - Use the [Network Tab in the Dev Tools][network tab] in the frontend to debug 126 | your requests. 127 | 128 | ## Resources 129 | 130 | - [create-react-app][] 131 | - [dbdiagram.io][] 132 | - [Postman][postman download] 133 | 134 | [create-react-app]: https://create-react-app.dev/docs/getting-started 135 | [create repo]: https://docs.github.com/en/get-started/quickstart/create-a-repo 136 | [dbdiagram.io]: https://dbdiagram.io/ 137 | [postman download]: https://www.postman.com/downloads/ 138 | [network tab]: https://developer.chrome.com/docs/devtools/network/ 139 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require_relative "./config/environment" 2 | require "sinatra/activerecord/rake" 3 | 4 | desc "Start the server" 5 | task :server do 6 | if ActiveRecord::Base.connection.migration_context.needs_migration? 7 | puts "Migrations are pending. Make sure to run `rake db:migrate` first." 8 | return 9 | end 10 | 11 | # rackup -p PORT will run on the port specified (9292 by default) 12 | ENV["PORT"] ||= "9292" 13 | rackup = "rackup -p #{ENV['PORT']}" 14 | 15 | # rerun allows auto-reloading of server when files are updated 16 | # -b runs in the background (include it or binding.pry won't work) 17 | exec "bundle exec rerun -b '#{rackup}'" 18 | end 19 | 20 | desc "Start the console" 21 | task :console do 22 | ActiveRecord::Base.logger = Logger.new(STDOUT) 23 | Pry.start 24 | end 25 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < Sinatra::Base 2 | set :default_content_type, 'application/json' 3 | 4 | # Add your routes here 5 | get "/" do 6 | { message: "Good luck with your project!" }.to_json 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tp1845/phase-3-sinatra-react-project/24db3681e9225fe93b6c0762bfa3e1ed76d90803/app/models/.gitkeep -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require_relative "./config/environment" 2 | 3 | # Allow CORS (Cross-Origin Resource Sharing) requests 4 | use Rack::Cors do 5 | allow do 6 | origins '*' 7 | resource '*', headers: :any, methods: [:get, :post, :delete, :put, :patch, :options, :head] 8 | end 9 | end 10 | 11 | # Parse JSON from the request body into the params hash 12 | use Rack::JSONBodyParser 13 | 14 | # Our application 15 | run ApplicationController 16 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | adapter: sqlite3 3 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 4 | timeout: 5000 5 | 6 | development: 7 | <<: *default 8 | database: db/development.sqlite3 9 | 10 | test: 11 | <<: *default 12 | database: db/test.sqlite3 13 | 14 | production: 15 | <<: *default 16 | database: db/production.sqlite3 17 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # This is an _environment variable_ that is used by some of the Rake tasks to determine 2 | # if our application is running locally in development, in a test environment, or in production 3 | ENV['RACK_ENV'] ||= "development" 4 | 5 | # Require in Gems 6 | require 'bundler/setup' 7 | Bundler.require(:default, ENV['RACK_ENV']) 8 | 9 | # Require in all files in 'app' directory 10 | require_all 'app' 11 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | puts "🌱 Seeding spices..." 2 | 3 | # Seed your database here 4 | 5 | puts "✅ Done seeding!" 6 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RACK_ENV'] ||= 'test' 2 | require_relative "../config/environment" 3 | require "sinatra/activerecord/rake" 4 | 5 | RSpec.configure do |config| 6 | config.include Rack::Test::Methods 7 | 8 | # Database setup 9 | if ActiveRecord::Base.connection.migration_context.needs_migration? 10 | # Run migrations for test environment 11 | Rake::Task["db:migrate"].execute 12 | end 13 | 14 | config.before(:suite) do 15 | DatabaseCleaner.clean_with(:truncation) 16 | end 17 | 18 | config.before do 19 | DatabaseCleaner.strategy = :transaction 20 | end 21 | 22 | config.before(:each, js: true) do 23 | DatabaseCleaner.strategy = :truncation 24 | end 25 | 26 | config.before do 27 | DatabaseCleaner.start 28 | end 29 | 30 | config.after do 31 | DatabaseCleaner.clean 32 | end 33 | 34 | config.expect_with :rspec do |expectations| 35 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 36 | end 37 | 38 | config.mock_with :rspec do |mocks| 39 | mocks.verify_partial_doubles = true 40 | end 41 | 42 | config.shared_context_metadata_behavior = :apply_to_host_groups 43 | end 44 | 45 | # Rack::Test::Methods needs this to run our controller 46 | def app 47 | Rack::Builder.parse_file('config.ru').first 48 | end 49 | --------------------------------------------------------------------------------