├── .browserslistrc
├── .dockerignore
├── .env.example
├── .github
├── dependabot.yml
└── workflows
│ └── rails.yml
├── .gitignore
├── .mergify.yml
├── .overcommit.yml
├── .rubocop.yml
├── .ruby-version
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── Gemfile
├── Gemfile.lock
├── LICENSE
├── Makefile
├── README.md
├── Rakefile
├── app.json
├── app
├── assets
│ ├── config
│ │ └── manifest.js
│ ├── images
│ │ └── .keep
│ └── stylesheets
│ │ ├── application.css
│ │ ├── appointments.scss
│ │ └── bootstrap_and_overrides.css
├── channels
│ └── application_cable
│ │ ├── channel.rb
│ │ └── connection.rb
├── controllers
│ ├── application_controller.rb
│ ├── appointments_controller.rb
│ └── concerns
│ │ └── .keep
├── helpers
│ ├── application_helper.rb
│ └── appointments_helper.rb
├── javascript
│ ├── channels
│ │ ├── consumer.js
│ │ └── index.js
│ ├── packs
│ │ └── application.js
│ └── stylesheets
│ │ └── application.scss
├── jobs
│ └── application_job.rb
├── mailers
│ └── application_mailer.rb
├── models
│ ├── application_record.rb
│ ├── appointment.rb
│ └── concerns
│ │ └── .keep
└── views
│ ├── appointments
│ ├── _form.html.erb
│ ├── edit.html.erb
│ ├── index.html.erb
│ ├── index.json.jbuilder
│ ├── new.html.erb
│ ├── show.html.erb
│ ├── show.json.jbuilder
│ └── welcome.html.erb
│ └── layouts
│ ├── application.html.erb
│ ├── mailer.html.erb
│ └── mailer.text.erb
├── babel.config.js
├── bin
├── bundle
├── delayed_job
├── rails
├── rake
├── setup
├── spring
├── update
├── webpack
├── webpack-dev-server
└── yarn
├── config.ru
├── config
├── application.rb
├── boot.rb
├── cable.yml
├── credentials.yml.enc
├── database.yml
├── environment.rb
├── environments
│ ├── development.rb
│ ├── production.rb
│ └── test.rb
├── initializers
│ ├── application_controller_renderer.rb
│ ├── assets.rb
│ ├── backtrace_silencers.rb
│ ├── content_security_policy.rb
│ ├── cookies_serializer.rb
│ ├── delayed_job_config.rb
│ ├── dotenv.rb
│ ├── filter_parameter_logging.rb
│ ├── inflections.rb
│ ├── mime_types.rb
│ └── wrap_parameters.rb
├── locales
│ ├── en.bootstrap.yml
│ └── en.yml
├── puma.rb
├── routes.rb
├── spring.rb
├── storage.yml
├── webpack
│ ├── development.js
│ ├── environment.js
│ ├── production.js
│ └── test.js
└── webpacker.yml
├── db
├── migrate
│ └── 20190121114109_create_delayed_jobs.rb
├── schema.rb
└── seeds.rb
├── docker-compose.yml
├── lib
├── assets
│ └── .keep
└── tasks
│ └── .keep
├── log
└── .keep
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
├── 404.html
├── 422.html
├── 500.html
├── apple-touch-icon-precomposed.png
├── apple-touch-icon.png
├── favicon.ico
└── robots.txt
├── storage
└── .keep
├── test
├── application_system_test_case.rb
├── controllers
│ ├── .keep
│ └── appointments_controller_test.rb
├── fixtures
│ ├── .keep
│ ├── appointments.yml
│ └── files
│ │ └── .keep
├── helpers
│ └── .keep
├── integration
│ └── .keep
├── mailers
│ └── .keep
├── models
│ ├── .keep
│ └── appointment_test.rb
├── system
│ └── .keep
└── test_helper.rb
├── tmp
└── .keep
└── yarn.lock
/.browserslistrc:
--------------------------------------------------------------------------------
1 | defaults
2 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | Gemfile.lock
2 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | # Your Twilio Account SID. Get a free account at twilio.com/try-twilio
2 | TWILIO_ACCOUNT_SID=ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
3 | # Your Twilio Auth Token. You can get it at twilio.com/console
4 | TWILIO_AUTH_TOKEN=yourAuthToken
5 | # The Twilio phone number you want to use to send SMS. Get one in the Twilio Console
6 | TWILIO_NUMBER=+12223334444
7 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: bundler
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | open-pull-requests-limit: 10
8 | ignore:
9 | - dependency-name: bootsnap
10 | versions:
11 | - 1.6.0
12 | - 1.7.3
13 | - dependency-name: twilio-ruby
14 | versions:
15 | - 5.46.1
16 |
--------------------------------------------------------------------------------
/.github/workflows/rails.yml:
--------------------------------------------------------------------------------
1 | name: Rails CI
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build:
7 | runs-on: ${{ matrix.os }}
8 | strategy:
9 | matrix:
10 | os: [ubuntu-latest, macos-latest, windows-latest]
11 | ruby: [ '2.6' ]
12 |
13 | steps:
14 | - name: Checkout code
15 | uses: actions/checkout@v2
16 | - name: Install SQLite (Ubuntu)
17 | if: runner.os == 'Linux'
18 | run: sudo apt-get update && sudo apt-get install -yqq libsqlite3-dev
19 | - name: Install SQLite (OSX)
20 | if: runner.os == 'macOs'
21 | run: brew install sqlite3
22 | - name: Install SQLite (Windows)
23 | if: runner.os == 'Windows'
24 | uses: crazy-max/ghaction-chocolatey@v1
25 | with:
26 | args: install sqlite
27 | - name: Setup Ruby ${{ matrix.ruby }}
28 | uses: actions/setup-ruby@v1
29 | with:
30 | ruby-version: ${{ matrix.ruby }}
31 | - name: Ruby gem cache
32 | uses: actions/cache@v1
33 | with:
34 | path: vendor/bundle
35 | key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
36 | restore-keys: |
37 | ${{ runner.os }}-gems-
38 | - name: Install SQLite gem (Windows)
39 | if: runner.os == 'Windows'
40 | run: gem install sqlite3 --platform=ruby -- --with-sqlite3-include=/c:/ProgramData/ProgramData/lib/SQLite/tools
41 | - name: Install gems
42 | run: |
43 | gem install bundler
44 | bundle config path vendor/bundle
45 | bundle install --jobs 4 --retry 3
46 | - name: Setup Node
47 | uses: actions/setup-node@v1
48 | with:
49 | node-version: 12.13.1
50 | - name: Install packages
51 | run: |
52 | npm install
53 | - name: Run tests
54 | run: |
55 | cp .env.example .env
56 | bundle exec rails db:setup
57 | bundle exec rails test
58 | env:
59 | RAILS_ENV: test
60 |
--------------------------------------------------------------------------------
/.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 all logfiles and tempfiles.
11 | /log/*
12 | /tmp/*
13 | !/log/.keep
14 | !/tmp/.keep
15 |
16 | # Ignore uploaded files in development
17 | /storage/*
18 | !/storage/.keep
19 |
20 | /node_modules
21 | /yarn-error.log
22 |
23 | /public/assets
24 | .byebug_history
25 |
26 | # Ignore master key for decrypting credentials and more.
27 | /config/master.key
28 |
29 | .env
30 | db/development.sqlite3
31 | db/test.sqlite3
32 | vendor/bundle
33 |
34 | .tool-versions
35 |
36 | /public/packs
37 | /public/packs-test
38 | /yarn-error.log
39 | yarn-debug.log*
40 | .yarn-integrity
41 |
--------------------------------------------------------------------------------
/.mergify.yml:
--------------------------------------------------------------------------------
1 | pull_request_rules:
2 | - name: automatic merge for Dependabot pull requests
3 | conditions:
4 | - author=dependabot-preview[bot]
5 | - status-success=build (ubuntu-latest, 2.6)
6 | - status-success=build (macos-latest, 2.6)
7 | - status-success=build (windows-latest, 2.6)
8 | actions:
9 | merge:
10 | method: squash
11 |
--------------------------------------------------------------------------------
/.overcommit.yml:
--------------------------------------------------------------------------------
1 | gemfile: 'Gemfile'
2 | PreCommit:
3 | # Style Check
4 | RuboCop:
5 | enabled: true
6 | command: ['bundle', 'exec', 'rubocop', '-a']
7 | on_warn: fail
8 |
9 | # Dependency Check
10 | BundleCheck:
11 | enabled: true
12 | on_warn: fail
13 |
14 | # Migration Check
15 | RailsSchemaUpToDate:
16 | enabled: true
17 | on_warn: fail
18 |
19 | # Checks for hard tabs in files
20 | HardTabs:
21 | enabled: true
22 | on_warn: fail
23 |
24 | PrePush:
25 | # Unit & Integration TEST
26 | RSpec:
27 | enabled: true
28 | command: ['bundle', 'exec', 'rspec']
29 | on_warn: fail
30 |
31 | BundleInstall:
32 | enabled: true
33 | on_warn: fail
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | require:
2 | - rubocop-rails
3 | - rubocop-rspec
4 |
5 | Layout/LineLength:
6 | IgnoredPatterns: ['(\A|\s)#']
7 |
8 | Style/Documentation:
9 | Enabled: false
10 |
11 | Style/ClassAndModuleChildren:
12 | Enabled: false
13 |
14 | Rails/FilePath:
15 | EnforcedStyle: arguments
16 |
17 | Rails/DynamicFindBy:
18 | Enabled: false
19 |
20 | Metrics/MethodLength:
21 | Enabled: false
22 |
23 | Metrics/BlockLength:
24 | Enabled: false
25 |
26 | Style/HashEachMethods:
27 | Enabled: true
28 |
29 | Style/HashTransformKeys:
30 | Enabled: true
31 |
32 | Style/HashTransformValues:
33 | Enabled: true
34 |
35 | RSpec/EmptyExampleGroup:
36 | Enabled: false
37 |
38 | RSpec/DescribeClass:
39 | Enabled: false
40 |
41 | RSpec/ExampleLength:
42 | Enabled: false
43 |
44 | RSpec/MultipleExpectations:
45 | Enabled: false
--------------------------------------------------------------------------------
/.ruby-version:
--------------------------------------------------------------------------------
1 | ruby-2.6.5
2 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | - Using welcoming and inclusive language
18 | - Being respectful of differing viewpoints and experiences
19 | - Gracefully accepting constructive criticism
20 | - Focusing on what is best for the community
21 | - Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | - The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | - Trolling, insulting/derogatory comments, and personal or political attacks
28 | - Public or private harassment
29 | - Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | - Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at open-source@twilio.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Twilio
2 |
3 | All third party contributors acknowledge that any contributions they provide will be made under the same open source license that the open source project is provided under.
4 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ruby:2.6
2 |
3 | WORKDIR /usr/src/app
4 |
5 | COPY Gemfile ./
6 |
7 | COPY Makefile ./
8 |
9 | COPY package.json ./
10 |
11 | # Install a Javascript environment in the container to avoid ExecJS::RuntimeUnavailable
12 | RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \
13 | && apt install -y nodejs
14 |
15 | RUN make install
16 |
17 | COPY . .
18 |
19 | RUN make database
20 |
21 | EXPOSE 3000
22 |
23 | CMD ["sh", "-c","./bin/delayed_job start && make serve"]
24 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source 'https://rubygems.org'
4 | git_source(:github) { |repo| "https://github.com/#{repo}.git" }
5 |
6 | ruby '~> 2.6'
7 |
8 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
9 | gem 'rails', '~> 6.1.4'
10 | # Use sqlite3 as the database for Active Record
11 | gem 'sqlite3', '~> 1.4'
12 | # Use Puma as the app server
13 | gem 'puma', '~> 5.4'
14 | # Use SCSS for stylesheets
15 | gem 'sass-rails', '>= 6'
16 | # Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
17 | gem 'webpacker', '~> 5.4'
18 | # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
19 | gem 'turbolinks', '~> 5'
20 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
21 | gem 'jbuilder', '~> 2.11'
22 |
23 | # Reduces boot times through caching; required in config/boot.rb
24 |
25 | # Use CoffeeScript for .coffee assets and views
26 | gem 'coffee-rails', '~> 5.0'
27 |
28 | gem 'rails-controller-testing'
29 |
30 | # Use jquery as the JavaScript library
31 | gem 'jquery-rails'
32 |
33 | gem 'bootstrap', '~> 5.0'
34 | # Use bootstrap themes
35 | gem 'twitter-bootstrap-rails', :git => 'git://github.com/seyhunak/twitter-bootstrap-rails.git'
36 |
37 | # Use delayed job for running background jobs
38 | gem 'delayed_job_active_record'
39 |
40 | # Need daemons to start delayed_job
41 | gem 'daemons'
42 |
43 | gem 'bootsnap', '>= 1.4.2', require: false
44 |
45 | group :development, :test do
46 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console
47 | gem 'byebug', platforms: %i[mri mingw x64_mingw]
48 |
49 | gem 'dotenv-rails', '~> 2.7'
50 | end
51 |
52 | group :development do
53 | # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
54 | gem 'listen', '>= 3.0.5', '< 3.7'
55 | gem 'web-console', '>= 3.3.0'
56 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
57 | gem 'spring'
58 | gem 'spring-watcher-listen', '~> 2.0.0'
59 |
60 | gem 'overcommit', '~> 0.58.0', require: false
61 | gem 'rubocop', '~> 1.18.4', require: false
62 | gem 'rubocop-rails', '~> 2.11', require: false
63 | end
64 |
65 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
66 | gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby]
67 |
68 | gem 'twilio-ruby', '~> 5.57'
69 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GIT
2 | remote: git://github.com/seyhunak/twitter-bootstrap-rails.git
3 | revision: a9a5b41735cdb37e0dcb878eb25dcdf6174412cd
4 | specs:
5 | twitter-bootstrap-rails (4.0.0)
6 | actionpack (>= 5.0, < 7.0)
7 | execjs (~> 2.7)
8 | less-rails (>= 3.0, < 5.0)
9 | railties (>= 5.0, < 7.0)
10 |
11 | GEM
12 | remote: https://rubygems.org/
13 | specs:
14 | actioncable (6.1.4)
15 | actionpack (= 6.1.4)
16 | activesupport (= 6.1.4)
17 | nio4r (~> 2.0)
18 | websocket-driver (>= 0.6.1)
19 | actionmailbox (6.1.4)
20 | actionpack (= 6.1.4)
21 | activejob (= 6.1.4)
22 | activerecord (= 6.1.4)
23 | activestorage (= 6.1.4)
24 | activesupport (= 6.1.4)
25 | mail (>= 2.7.1)
26 | actionmailer (6.1.4)
27 | actionpack (= 6.1.4)
28 | actionview (= 6.1.4)
29 | activejob (= 6.1.4)
30 | activesupport (= 6.1.4)
31 | mail (~> 2.5, >= 2.5.4)
32 | rails-dom-testing (~> 2.0)
33 | actionpack (6.1.4)
34 | actionview (= 6.1.4)
35 | activesupport (= 6.1.4)
36 | rack (~> 2.0, >= 2.0.9)
37 | rack-test (>= 0.6.3)
38 | rails-dom-testing (~> 2.0)
39 | rails-html-sanitizer (~> 1.0, >= 1.2.0)
40 | actiontext (6.1.4)
41 | actionpack (= 6.1.4)
42 | activerecord (= 6.1.4)
43 | activestorage (= 6.1.4)
44 | activesupport (= 6.1.4)
45 | nokogiri (>= 1.8.5)
46 | actionview (6.1.4)
47 | activesupport (= 6.1.4)
48 | builder (~> 3.1)
49 | erubi (~> 1.4)
50 | rails-dom-testing (~> 2.0)
51 | rails-html-sanitizer (~> 1.1, >= 1.2.0)
52 | activejob (6.1.4)
53 | activesupport (= 6.1.4)
54 | globalid (>= 0.3.6)
55 | activemodel (6.1.4)
56 | activesupport (= 6.1.4)
57 | activerecord (6.1.4)
58 | activemodel (= 6.1.4)
59 | activesupport (= 6.1.4)
60 | activestorage (6.1.4)
61 | actionpack (= 6.1.4)
62 | activejob (= 6.1.4)
63 | activerecord (= 6.1.4)
64 | activesupport (= 6.1.4)
65 | marcel (~> 1.0.0)
66 | mini_mime (>= 1.1.0)
67 | activesupport (6.1.4)
68 | concurrent-ruby (~> 1.0, >= 1.0.2)
69 | i18n (>= 1.6, < 2)
70 | minitest (>= 5.1)
71 | tzinfo (~> 2.0)
72 | zeitwerk (~> 2.3)
73 | ast (2.4.2)
74 | autoprefixer-rails (10.2.5.1)
75 | execjs (> 0)
76 | bindex (0.8.1)
77 | bootsnap (1.7.7)
78 | msgpack (~> 1.0)
79 | bootstrap (5.0.1)
80 | autoprefixer-rails (>= 9.1.0)
81 | popper_js (>= 2.9.2, < 3)
82 | sassc-rails (>= 2.0.0)
83 | builder (3.2.4)
84 | byebug (11.1.3)
85 | childprocess (4.1.0)
86 | coffee-rails (5.0.0)
87 | coffee-script (>= 2.2.0)
88 | railties (>= 5.2.0)
89 | coffee-script (2.4.1)
90 | coffee-script-source
91 | execjs
92 | coffee-script-source (1.12.2)
93 | commonjs (0.2.7)
94 | concurrent-ruby (1.1.9)
95 | crass (1.0.6)
96 | daemons (1.4.0)
97 | delayed_job (4.1.9)
98 | activesupport (>= 3.0, < 6.2)
99 | delayed_job_active_record (4.1.6)
100 | activerecord (>= 3.0, < 6.2)
101 | delayed_job (>= 3.0, < 5)
102 | dotenv (2.7.6)
103 | dotenv-rails (2.7.6)
104 | dotenv (= 2.7.6)
105 | railties (>= 3.2)
106 | erubi (1.10.0)
107 | execjs (2.8.1)
108 | faraday (1.5.1)
109 | faraday-em_http (~> 1.0)
110 | faraday-em_synchrony (~> 1.0)
111 | faraday-excon (~> 1.1)
112 | faraday-httpclient (~> 1.0.1)
113 | faraday-net_http (~> 1.0)
114 | faraday-net_http_persistent (~> 1.1)
115 | faraday-patron (~> 1.0)
116 | multipart-post (>= 1.2, < 3)
117 | ruby2_keywords (>= 0.0.4)
118 | faraday-em_http (1.0.0)
119 | faraday-em_synchrony (1.0.0)
120 | faraday-excon (1.1.0)
121 | faraday-httpclient (1.0.1)
122 | faraday-net_http (1.0.1)
123 | faraday-net_http_persistent (1.2.0)
124 | faraday-patron (1.0.0)
125 | ffi (1.15.3)
126 | globalid (0.4.2)
127 | activesupport (>= 4.2.0)
128 | i18n (1.8.10)
129 | concurrent-ruby (~> 1.0)
130 | iniparse (1.5.0)
131 | jbuilder (2.11.2)
132 | activesupport (>= 5.0.0)
133 | jquery-rails (4.4.0)
134 | rails-dom-testing (>= 1, < 3)
135 | railties (>= 4.2.0)
136 | thor (>= 0.14, < 2.0)
137 | jwt (2.2.3)
138 | less (2.6.0)
139 | commonjs (~> 0.2.7)
140 | less-rails (4.0.0)
141 | actionpack (>= 4)
142 | less (~> 2.6.0)
143 | sprockets (>= 2)
144 | listen (3.6.0)
145 | rb-fsevent (~> 0.10, >= 0.10.3)
146 | rb-inotify (~> 0.9, >= 0.9.10)
147 | loofah (2.10.0)
148 | crass (~> 1.0.2)
149 | nokogiri (>= 1.5.9)
150 | mail (2.7.1)
151 | mini_mime (>= 0.1.1)
152 | marcel (1.0.1)
153 | method_source (1.0.0)
154 | mini_mime (1.1.0)
155 | mini_portile2 (2.5.3)
156 | minitest (5.14.4)
157 | msgpack (1.4.2)
158 | multipart-post (2.1.1)
159 | nio4r (2.5.7)
160 | nokogiri (1.11.7)
161 | mini_portile2 (~> 2.5.0)
162 | racc (~> 1.4)
163 | overcommit (0.58.0)
164 | childprocess (>= 0.6.3, < 5)
165 | iniparse (~> 1.4)
166 | rexml (~> 3.2)
167 | parallel (1.20.1)
168 | parser (3.0.2.0)
169 | ast (~> 2.4.1)
170 | popper_js (2.9.2)
171 | puma (5.4.0)
172 | nio4r (~> 2.0)
173 | racc (1.5.2)
174 | rack (2.2.3)
175 | rack-proxy (0.7.0)
176 | rack
177 | rack-test (1.1.0)
178 | rack (>= 1.0, < 3)
179 | rails (6.1.4)
180 | actioncable (= 6.1.4)
181 | actionmailbox (= 6.1.4)
182 | actionmailer (= 6.1.4)
183 | actionpack (= 6.1.4)
184 | actiontext (= 6.1.4)
185 | actionview (= 6.1.4)
186 | activejob (= 6.1.4)
187 | activemodel (= 6.1.4)
188 | activerecord (= 6.1.4)
189 | activestorage (= 6.1.4)
190 | activesupport (= 6.1.4)
191 | bundler (>= 1.15.0)
192 | railties (= 6.1.4)
193 | sprockets-rails (>= 2.0.0)
194 | rails-controller-testing (1.0.5)
195 | actionpack (>= 5.0.1.rc1)
196 | actionview (>= 5.0.1.rc1)
197 | activesupport (>= 5.0.1.rc1)
198 | rails-dom-testing (2.0.3)
199 | activesupport (>= 4.2.0)
200 | nokogiri (>= 1.6)
201 | rails-html-sanitizer (1.3.0)
202 | loofah (~> 2.3)
203 | railties (6.1.4)
204 | actionpack (= 6.1.4)
205 | activesupport (= 6.1.4)
206 | method_source
207 | rake (>= 0.13)
208 | thor (~> 1.0)
209 | rainbow (3.0.0)
210 | rake (13.0.6)
211 | rb-fsevent (0.11.0)
212 | rb-inotify (0.10.1)
213 | ffi (~> 1.0)
214 | regexp_parser (2.1.1)
215 | rexml (3.2.5)
216 | rubocop (1.18.4)
217 | parallel (~> 1.10)
218 | parser (>= 3.0.0.0)
219 | rainbow (>= 2.2.2, < 4.0)
220 | regexp_parser (>= 1.8, < 3.0)
221 | rexml
222 | rubocop-ast (>= 1.8.0, < 2.0)
223 | ruby-progressbar (~> 1.7)
224 | unicode-display_width (>= 1.4.0, < 3.0)
225 | rubocop-ast (1.8.0)
226 | parser (>= 3.0.1.1)
227 | rubocop-rails (2.11.3)
228 | activesupport (>= 4.2.0)
229 | rack (>= 1.1)
230 | rubocop (>= 1.7.0, < 2.0)
231 | ruby-progressbar (1.11.0)
232 | ruby2_keywords (0.0.5)
233 | sass-rails (6.0.0)
234 | sassc-rails (~> 2.1, >= 2.1.1)
235 | sassc (2.4.0)
236 | ffi (~> 1.9)
237 | sassc-rails (2.1.2)
238 | railties (>= 4.0.0)
239 | sassc (>= 2.0)
240 | sprockets (> 3.0)
241 | sprockets-rails
242 | tilt
243 | semantic_range (3.0.0)
244 | spring (2.1.1)
245 | spring-watcher-listen (2.0.1)
246 | listen (>= 2.7, < 4.0)
247 | spring (>= 1.2, < 3.0)
248 | sprockets (4.0.2)
249 | concurrent-ruby (~> 1.0)
250 | rack (> 1, < 3)
251 | sprockets-rails (3.2.2)
252 | actionpack (>= 4.0)
253 | activesupport (>= 4.0)
254 | sprockets (>= 3.0.0)
255 | sqlite3 (1.4.2)
256 | thor (1.1.0)
257 | tilt (2.0.10)
258 | turbolinks (5.2.1)
259 | turbolinks-source (~> 5.2)
260 | turbolinks-source (5.2.0)
261 | twilio-ruby (5.57.1)
262 | faraday (>= 0.9, < 2.0)
263 | jwt (>= 1.5, <= 2.5)
264 | nokogiri (>= 1.6, < 2.0)
265 | tzinfo (2.0.4)
266 | concurrent-ruby (~> 1.0)
267 | unicode-display_width (2.0.0)
268 | web-console (4.1.0)
269 | actionview (>= 6.0.0)
270 | activemodel (>= 6.0.0)
271 | bindex (>= 0.4.0)
272 | railties (>= 6.0.0)
273 | webpacker (5.4.0)
274 | activesupport (>= 5.2)
275 | rack-proxy (>= 0.6.1)
276 | railties (>= 5.2)
277 | semantic_range (>= 2.3.0)
278 | websocket-driver (0.7.5)
279 | websocket-extensions (>= 0.1.0)
280 | websocket-extensions (0.1.5)
281 | zeitwerk (2.4.2)
282 |
283 | PLATFORMS
284 | ruby
285 |
286 | DEPENDENCIES
287 | bootsnap (>= 1.4.2)
288 | bootstrap (~> 5.0)
289 | byebug
290 | coffee-rails (~> 5.0)
291 | daemons
292 | delayed_job_active_record
293 | dotenv-rails (~> 2.7)
294 | jbuilder (~> 2.11)
295 | jquery-rails
296 | listen (>= 3.0.5, < 3.7)
297 | overcommit (~> 0.58.0)
298 | puma (~> 5.4)
299 | rails (~> 6.1.4)
300 | rails-controller-testing
301 | rubocop (~> 1.18.4)
302 | rubocop-rails (~> 2.11)
303 | sass-rails (>= 6)
304 | spring
305 | spring-watcher-listen (~> 2.0.0)
306 | sqlite3 (~> 1.4)
307 | turbolinks (~> 5)
308 | twilio-ruby (~> 5.57)
309 | twitter-bootstrap-rails!
310 | tzinfo-data
311 | web-console (>= 3.3.0)
312 | webpacker (~> 5.4)
313 |
314 | RUBY VERSION
315 | ruby 2.6.5p114
316 |
317 | BUNDLED WITH
318 | 2.1.4
319 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Twilio Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: install database serve
2 |
3 | install:
4 | bundle install; \
5 | npm install;
6 |
7 | database:
8 | bundle exec rails db:setup
9 |
10 | serve:
11 | bundle exec rails s --binding 0.0.0.0
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # Appointment Reminders with Ruby on Rails and Twilio
6 |
7 | [](https://github.com/TwilioDevEd/appointment-reminders-rails/actions)
8 |
9 | > This template is part of Twilio CodeExchange. If you encounter any issues with this code, please open an issue at [github.com/twilio-labs/code-exchange/issues](https://github.com/twilio-labs/code-exchange/issues).
10 |
11 | ## About
12 |
13 | Make sure your customers show up for their scheduled appointments with automated
14 | reminders. Deliver reminders via SMS text messages that don't get ignored like
15 | your e-mails.
16 |
17 | [Read the full tutorial here!](https://www.twilio.com/docs/tutorials/walkthrough/appointment-reminders/ruby/rails)
18 |
19 | Implementations in other languages:
20 |
21 | | .NET | Java | Python| PHP | Node |
22 | | :--- | :--- | :---- | :-- | :--- |
23 | | [Done](https://github.com/TwilioDevEd/appointment-reminders-csharp) | [Done](https://github.com/TwilioDevEd/appointment-reminders-java) | [Done](https://github.com/TwilioDevEd/appointment-reminders-django) | [Done](https://github.com/TwilioDevEd/appointment-reminders-laravel) | [Done](https://github.com/twilio-labs/sample-appointment-reminders-node) |
24 |
25 | ## Set up
26 |
27 | ### Requirements
28 | - [Ruby](https://www.ruby-lang.org/) **2.6.x** version.
29 | - [Sqlite](https://www.sqlite.org/index.html).
30 | - [Node.js](https://nodejs.org/en/) **10.x** or **12.x** version.
31 | - A Twilio account - [sign up](https://www.twilio.com/try-twilio)
32 |
33 | ### Twilio Account Settings
34 |
35 | This application should give you a ready-made starting point for writing your own application.
36 | Before we begin, we need to collect all the config values we need to run the application:
37 |
38 | | Config Value | Description |
39 | | :----------- | :---------- |
40 | | TWILIO_ACCOUNT_SID | Your primary Twilio account identifier - find this [in the Console](https://www.twilio.com/console). |
41 | | TWILIO_AUTH_TOKEN | Used to authenticate - [just like the above, you'll find this here](https://www.twilio.com/console). |
42 | | TWILIO_NUMBER | A Twilio phone number in [E.164 format](https://en.wikipedia.org/wiki/E.164) - you can [get one here](https://www.twilio.com/console/phone-numbers/incoming) |
43 |
44 | ### Local development
45 |
46 | This project is built using [Ruby on Rails](http://rubyonrails.org/) Framework.
47 |
48 | 1. First clone this repository and `cd` into it.
49 |
50 | ```bash
51 | git clone git@github.com:TwilioDevEd/appointment-reminders-rails.git
52 | cd appointment-reminders-rails
53 | ```
54 |
55 | 2. Install the dependencies, the following command will install gems and Node dependencies.
56 |
57 | ```bash
58 | make install
59 | ```
60 |
61 | 3. Copy the `.env.example` file to `.env`, and edit it including your credentials
62 | for the Twilio API (found at https://www.twilio.com/console/account/settings).
63 | You will also need a [Twilio Number](https://www.twilio.com/console/phone-numbers/incoming).
64 |
65 | ```bash
66 | cp .env.example .env
67 | ```
68 | See [Twilio Account Settings](#twilio-account-settings) to locate the necessary environment variables.
69 |
70 | 4. Create the database and run migrations.
71 |
72 | ```bash
73 | make database
74 | ```
75 |
76 | At this point you are ready to run the code:
77 |
78 | 5. First start the delayed jobs deamon, in the root execute the following command:
79 |
80 | ```bash
81 | ./bin/delayed_job start
82 | ```
83 |
84 | You can `tail` the log for this process:
85 |
86 | ```bash
87 | tail -f log/delayed_job.log
88 | ```
89 |
90 | 6. Then start the development server:
91 |
92 | ```bash
93 | make serve
94 | ```
95 |
96 | 7. Check it out at [http://localhost:3000](http://localhost:3000).
97 |
98 | ### Docker
99 |
100 | If you have [Docker](https://www.docker.com/) already installed on your machine, you can use our `docker-compose.yml` to setup your project.
101 |
102 | 1. Make sure you have the project cloned.
103 | 2. Setup the `.env` file as outlined in the [Local Development](#local-development) steps.
104 | 3. Run `docker-compose up`.
105 |
106 | ### Test
107 |
108 | You can run the tests locally by typing:
109 | ```bash
110 | $ bundle exec rails test
111 | ```
112 |
113 | ### Cloud deployment
114 |
115 | Additionally to trying out this application locally, you can deploy it to a variety of host services. Here is a small selection of them.
116 |
117 | Please be aware that some of these might charge you for the usage or might make the source code for this application visible to the public. When in doubt research the respective hosting service first.
118 |
119 | | Service | |
120 | | :-------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
121 | | [Heroku](https://www.heroku.com/) | [](https://heroku.com/deploy) |
122 |
123 | ## Resources
124 |
125 | - The CodeExchange repository can be found [here](https://github.com/twilio-labs/code-exchange/).
126 |
127 | ## Contributing
128 |
129 | This template is open source and welcomes contributions. All contributions are subject to our [Code of Conduct](https://github.com/twilio-labs/.github/blob/master/CODE_OF_CONDUCT.md).
130 |
131 | [Visit the project on GitHub](https://github.com/twilio-labs/sample-template-nodejs)
132 |
133 | ## License
134 |
135 | [MIT](http://www.opensource.org/licenses/mit-license.html)
136 |
137 | ## Disclaimer
138 |
139 | No warranty expressed or implied. Software is as is.
140 |
141 | [twilio]: https://www.twilio.com
142 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Add your own tasks in files placed in lib/tasks ending in .rake,
4 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
5 |
6 | require_relative 'config/application'
7 |
8 | Rails.application.load_tasks
9 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Appointment Reminders powered by Twilio",
3 | "description": "A template for getting started with Appointment Reminders using Twilio and Ruby on Rails.",
4 | "website": "https://twilio.com",
5 | "logo": "https://www.twilio.com/marketing/bundles/marketing/img/favicons/favicon_114.png",
6 | "success_url": "/",
7 | "addons": [],
8 | "keywords": ["rails", "twilio"],
9 | "env": {
10 | "TWILIO_ACCOUNT_SID": {
11 | "description": "Your Twilio Account SID. Get a free account at twilio.com/try-twilio",
12 | "value": ""
13 | },
14 | "TWILIO_AUTH_TOKEN": {
15 | "description": "Your Twilio Auth Token. You can get it at twilio.com/console",
16 | "value": ""
17 | },
18 | "TWILIO_NUMBER": {
19 | "description": "The Twilio phone number you want to use to send SMS. Get one in the Twilio Console",
20 | "value": "+12345678910"
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link_directory ../stylesheets .css
3 |
--------------------------------------------------------------------------------
/app/assets/images/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TwilioDevEd/appointment-reminders-rails/729741de628e8fc4b0c3decafeba2d824aeea9c3/app/assets/images/.keep
--------------------------------------------------------------------------------
/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
6 | * vendor/assets/stylesheets directory can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10 | * files in this directory. Styles in this file should be added after the last require_* statement.
11 | * It is generally better to create a new file per style scope.
12 | *
13 | *= require_tree .
14 | *= require_self
15 | */
16 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/appointments.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the Appointments controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/bootstrap_and_overrides.css:
--------------------------------------------------------------------------------
1 | /*
2 | =require twitter-bootstrap-static/bootstrap
3 |
4 | Static version of css will use Glyphicons sprites by default
5 | =require twitter-bootstrap-static/sprites
6 | */
--------------------------------------------------------------------------------
/app/channels/application_cable/channel.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ApplicationCable
4 | class Channel < ActionCable::Channel::Base
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/app/channels/application_cable/connection.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ApplicationCable
4 | class Connection < ActionCable::Connection::Base
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ApplicationController < ActionController::Base
4 | # Prevent CSRF attacks by raising an exception.
5 | # For APIs, you may want to use :null_session instead.
6 | protect_from_forgery with: :exception
7 | end
8 |
--------------------------------------------------------------------------------
/app/controllers/appointments_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AppointmentsController < ApplicationController
4 | before_action :find_appointment, only: [:show, :edit, :update, :destroy]
5 |
6 | # GET /appointments
7 | # GET /appointments.json
8 | def index
9 | @appointments = Appointment.all
10 | if @appointments.length.zero?
11 | flash[:alert] = 'You have no appointments. Create one now to get started.'
12 | end
13 | end
14 |
15 | # GET /appointments/1
16 | # GET /appointments/1.json
17 | def show
18 | end
19 |
20 | # GET /appointments/new
21 | def new
22 | @appointment = Appointment.new
23 | @min_date = DateTime.now
24 | end
25 |
26 | # GET /appointments/1/edit
27 | def edit
28 | end
29 |
30 | # POST /appointments
31 | # POST /appointments.json
32 | def create
33 | Time.zone = appointment_params[:time_zone]
34 | @appointment = Appointment.new(appointment_params)
35 |
36 | respond_to do |format|
37 | if @appointment.save
38 | format.html { redirect_to @appointment, notice: 'Appointment was successfully created.' }
39 | format.json { render :show, status: :created, location: @appointment }
40 | else
41 | format.html { render :new }
42 | format.json { render json: @appointment.errors, status: :unprocessable_entity }
43 | end
44 | end
45 | end
46 |
47 | # PATCH/PUT /appointments/1
48 | # PATCH/PUT /appointments/1.json
49 | def update
50 | respond_to do |format|
51 | if @appointment.update(appointment_params)
52 | format.html { redirect_to @appointment, notice: 'Appointment was successfully updated.' }
53 | format.json { render :show, status: :ok, location: @appointment }
54 | else
55 | format.html { render :edit }
56 | format.json { render json: @appointment.errors, status: :unprocessable_entity }
57 | end
58 | end
59 | end
60 |
61 | # DELETE /appointments/1
62 | # DELETE /appointments/1.json
63 | def destroy
64 | @appointment.destroy
65 | respond_to do |format|
66 | format.html { redirect_to appointments_url, notice: 'Appointment was successfully destroyed.' }
67 | format.json { head :no_content }
68 | end
69 | end
70 |
71 | private
72 | # Use callbacks to share common setup or constraints between actions.
73 | # See above ---> before_action :set_appointment, only: [:show, :edit, :update, :destroy]
74 | def find_appointment
75 | @appointment = Appointment.find(params[:id])
76 | end
77 |
78 | # Never trust parameters from the scary internet, only allow the white list through.
79 | def appointment_params
80 | params.require(:appointment).permit(:name, :phone_number, :time, :time_zone)
81 | end
82 | end
83 |
--------------------------------------------------------------------------------
/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TwilioDevEd/appointment-reminders-rails/729741de628e8fc4b0c3decafeba2d824aeea9c3/app/controllers/concerns/.keep
--------------------------------------------------------------------------------
/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ApplicationHelper
4 | end
5 |
--------------------------------------------------------------------------------
/app/helpers/appointments_helper.rb:
--------------------------------------------------------------------------------
1 | module AppointmentsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/javascript/channels/consumer.js:
--------------------------------------------------------------------------------
1 | // Action Cable provides the framework to deal with WebSockets in Rails.
2 | // You can generate new channels where WebSocket features live using the `rails generate channel` command.
3 |
4 | import { createConsumer } from "@rails/actioncable"
5 |
6 | export default createConsumer()
7 |
--------------------------------------------------------------------------------
/app/javascript/channels/index.js:
--------------------------------------------------------------------------------
1 | // Load all the channels within this directory and all subdirectories.
2 | // Channel files must be named *_channel.js.
3 |
4 | const channels = require.context('.', true, /_channel\.js$/)
5 | channels.keys().forEach(channels)
6 |
--------------------------------------------------------------------------------
/app/javascript/packs/application.js:
--------------------------------------------------------------------------------
1 | // This file is automatically compiled by Webpack, along with any other files
2 | // present in this directory. You're encouraged to place your actual application logic in
3 | // a relevant structure within app/javascript and only use these pack files to reference
4 | // that code so it'll be compiled.
5 |
6 | require("@rails/ujs").start()
7 | require("turbolinks").start()
8 | require("@rails/activestorage").start()
9 | require("channels")
10 | import "bootstrap"
11 | import "../stylesheets/application"
12 |
13 | // Uncomment to copy all static images under ../images to the output folder and reference
14 | // them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
15 | // or the `imagePath` JavaScript helper below.
16 | //
17 | // const images = require.context('../images', true)
18 | // const imagePath = (name) => images(name, true)
19 |
--------------------------------------------------------------------------------
/app/javascript/stylesheets/application.scss:
--------------------------------------------------------------------------------
1 | @import "bootstrap";
2 |
3 | footer {
4 | bottom: 10px;
5 | text-align: center;
6 |
7 | i {
8 | color:#ff0000;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/app/jobs/application_job.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ApplicationJob < ActiveJob::Base
4 | # Automatically retry jobs that encountered a deadlock
5 | # retry_on ActiveRecord::Deadlocked
6 |
7 | # Most jobs are safe to ignore if the underlying records are no longer available
8 | # discard_on ActiveJob::DeserializationError
9 | end
10 |
--------------------------------------------------------------------------------
/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ApplicationMailer < ActionMailer::Base
4 | default from: 'from@example.com'
5 | layout 'mailer'
6 | end
7 |
--------------------------------------------------------------------------------
/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ApplicationRecord < ActiveRecord::Base
4 | self.abstract_class = true
5 | end
6 |
--------------------------------------------------------------------------------
/app/models/appointment.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class Appointment < ActiveRecord::Base
4 | validates :name, presence: true
5 | validates :phone_number, presence: true
6 | validates :time, presence: true
7 |
8 | after_create :reminder
9 |
10 | # Notify our appointment attendee X minutes before the appointment time
11 | def reminder
12 | @twilio_number = ENV['TWILIO_NUMBER']
13 | account_sid = ENV['TWILIO_ACCOUNT_SID']
14 | @client = Twilio::REST::Client.new account_sid, ENV['TWILIO_AUTH_TOKEN']
15 | time_str = ((self.time).localtime).strftime("%I:%M%p on %b. %d, %Y")
16 | body = "Hi #{self.name}. Just a reminder that you have an appointment coming up at #{time_str}."
17 | message = @client.messages.create(
18 | :from => @twilio_number,
19 | :to => self.phone_number,
20 | :body => body,
21 | )
22 | end
23 |
24 | def when_to_run
25 | minutes_before_appointment = 30.minutes
26 | time - minutes_before_appointment
27 | end
28 |
29 | handle_asynchronously :reminder, :run_at => Proc.new { |i| i.when_to_run }
30 | end
31 |
--------------------------------------------------------------------------------
/app/models/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TwilioDevEd/appointment-reminders-rails/729741de628e8fc4b0c3decafeba2d824aeea9c3/app/models/concerns/.keep
--------------------------------------------------------------------------------
/app/views/appointments/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_for @appointment, :html => { :class => "form-horizontal appointment" } do |f| %>
2 |
3 | <% if @appointment.errors.any? %>
4 |
5 |
6 |
<%= pluralize(@appointment.errors.count, "error") %> prohibited this appointment from being saved:
7 |
8 |
9 |
10 | <% @appointment.errors.full_messages.each do |msg| %>
11 | <%= msg %>
12 | <% end %>
13 |
14 |
15 |
16 | <% end %>
17 |
18 |
25 |
32 |
46 |
47 |
54 |
55 | <% end %>
56 |
--------------------------------------------------------------------------------
/app/views/appointments/edit.html.erb:
--------------------------------------------------------------------------------
1 | <%- model_class = Appointment -%>
2 |
5 | <%= render :partial => 'form' %>
6 |
--------------------------------------------------------------------------------
/app/views/appointments/index.html.erb:
--------------------------------------------------------------------------------
1 | <%- model_class = Appointment -%>
2 |
5 |
6 |
7 |
8 | <%= model_class.human_attribute_name(:id) %>
9 | <%= model_class.human_attribute_name(:name) %>
10 | <%= model_class.human_attribute_name(:phone_number) %>
11 | <%= model_class.human_attribute_name(:time) %>
12 | <%= model_class.human_attribute_name(:created_at) %>
13 | <%=t '.actions', :default => t("helpers.actions") %>
14 |
15 |
16 |
17 | <% @appointments.each do |appointment| %>
18 |
19 | <%= link_to appointment.id, appointment_path(appointment) %>
20 | <%= appointment.name %>
21 | <%= appointment.phone_number %>
22 | <%= appointment.time %>
23 | <%=l appointment.created_at %>
24 |
25 | <%= link_to "Edit",
26 | edit_appointment_path(appointment), :class => 'btn btn-default btn-xs' %>
27 | <%= link_to "Delete",
28 | appointment_path(appointment),
29 | :method => :delete,
30 | :data => { :confirm => t('.confirm', :default => t("helpers.links.confirm", :default => 'Are you sure?')) },
31 | :class => 'btn btn-xs btn-danger' %>
32 |
33 |
34 | <% end %>
35 |
36 |
37 |
38 | <%= link_to "New",
39 | new_appointment_path,
40 | :class => 'btn btn-primary' %>
41 |
--------------------------------------------------------------------------------
/app/views/appointments/index.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.array!(@appointments) do |appointment|
2 | json.extract! appointment, :id, :name, :phone_number, :time
3 | json.url appointment_url(appointment, format: :json)
4 | end
5 |
--------------------------------------------------------------------------------
/app/views/appointments/new.html.erb:
--------------------------------------------------------------------------------
1 | <%- model_class = Appointment -%>
2 |
5 | <%= render :partial => 'form' %>
6 |
--------------------------------------------------------------------------------
/app/views/appointments/show.html.erb:
--------------------------------------------------------------------------------
1 | <%- model_class = Appointment -%>
2 |
5 |
6 |
7 | <%= model_class.human_attribute_name(:name) %>:
8 | <%= @appointment.name %>
9 | <%= model_class.human_attribute_name(:phone_number) %>:
10 | <%= @appointment.phone_number %>
11 | <%= model_class.human_attribute_name(:time) %>:
12 | <%= ((@appointment.time).in_time_zone(@appointment.time_zone)).strftime("%I:%M%p") %>
13 |
14 |
15 | <%= link_to t('.back', :default => t("helpers.links.back")),
16 | appointments_path, :class => 'btn btn-default' %>
17 | <%= link_to t('.edit', :default => t("helpers.links.edit")),
18 | edit_appointment_path(@appointment), :class => 'btn btn-default' %>
19 | <%= link_to t('.destroy', :default => t("helpers.links.destroy")),
20 | appointment_path(@appointment),
21 | :method => 'delete',
22 | :data => { :confirm => t('.confirm', :default => t("helpers.links.confirm", :default => 'Are you sure?')) },
23 | :class => 'btn btn-danger' %>
24 |
--------------------------------------------------------------------------------
/app/views/appointments/show.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.extract! @appointment, :id, :name, :phone_number, :time, :created_at, :updated_at
2 |
--------------------------------------------------------------------------------
/app/views/appointments/welcome.html.erb:
--------------------------------------------------------------------------------
1 | Appointment Reminders with Ruby on Rails
2 |
3 | This demo application will show you how to implement SMS appointment reminders with <%= link_to "Twilio", "http://twilio.com" %> in Ruby on Rails.
4 |
5 |
<%= link_to "Create a new Appointment", new_appointment_path, :class => 'btn btn-primary btn-xs' %> to see how it works!
--------------------------------------------------------------------------------
/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Example App
5 | <%= csrf_meta_tags %>
6 | <%= csp_meta_tag %>
7 |
8 | <%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
9 | <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
10 |
11 |
12 |
13 |
14 |
22 |
23 |
24 |
25 |
26 | <%= yield %>
27 |
28 |
29 |
30 |
Made with ❤️ by your pals
31 | @twilio
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/views/layouts/mailer.text.erb:
--------------------------------------------------------------------------------
1 | <%= yield %>
2 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | var validEnv = ['development', 'test', 'production']
3 | var currentEnv = api.env()
4 | var isDevelopmentEnv = api.env('development')
5 | var isProductionEnv = api.env('production')
6 | var isTestEnv = api.env('test')
7 |
8 | if (!validEnv.includes(currentEnv)) {
9 | throw new Error(
10 | 'Please specify a valid `NODE_ENV` or ' +
11 | '`BABEL_ENV` environment variables. Valid values are "development", ' +
12 | '"test", and "production". Instead, received: ' +
13 | JSON.stringify(currentEnv) +
14 | '.'
15 | )
16 | }
17 |
18 | return {
19 | presets: [
20 | isTestEnv && [
21 | '@babel/preset-env',
22 | {
23 | targets: {
24 | node: 'current'
25 | }
26 | }
27 | ],
28 | (isProductionEnv || isDevelopmentEnv) && [
29 | '@babel/preset-env',
30 | {
31 | forceAllTransforms: true,
32 | useBuiltIns: 'entry',
33 | corejs: 3,
34 | modules: false,
35 | exclude: ['transform-typeof-symbol']
36 | }
37 | ]
38 | ].filter(Boolean),
39 | plugins: [
40 | 'babel-plugin-macros',
41 | '@babel/plugin-syntax-dynamic-import',
42 | isTestEnv && 'babel-plugin-dynamic-import-node',
43 | '@babel/plugin-transform-destructuring',
44 | [
45 | '@babel/plugin-proposal-class-properties',
46 | {
47 | loose: true
48 | }
49 | ],
50 | [
51 | '@babel/plugin-proposal-object-rest-spread',
52 | {
53 | useBuiltIns: true
54 | }
55 | ],
56 | [
57 | '@babel/plugin-transform-runtime',
58 | {
59 | helpers: false,
60 | regenerator: true,
61 | corejs: false
62 | }
63 | ],
64 | [
65 | '@babel/plugin-transform-regenerator',
66 | {
67 | async: false
68 | }
69 | ]
70 | ].filter(Boolean)
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'bundle' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "rubygems"
12 |
13 | m = Module.new do
14 | module_function
15 |
16 | def invoked_as_script?
17 | File.expand_path($0) == File.expand_path(__FILE__)
18 | end
19 |
20 | def env_var_version
21 | ENV["BUNDLER_VERSION"]
22 | end
23 |
24 | def cli_arg_version
25 | return unless invoked_as_script? # don't want to hijack other binstubs
26 | return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
27 | bundler_version = nil
28 | update_index = nil
29 | ARGV.each_with_index do |a, i|
30 | if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
31 | bundler_version = a
32 | end
33 | next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
34 | bundler_version = $1
35 | update_index = i
36 | end
37 | bundler_version
38 | end
39 |
40 | def gemfile
41 | gemfile = ENV["BUNDLE_GEMFILE"]
42 | return gemfile if gemfile && !gemfile.empty?
43 |
44 | File.expand_path("../../Gemfile", __FILE__)
45 | end
46 |
47 | def lockfile
48 | lockfile =
49 | case File.basename(gemfile)
50 | when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
51 | else "#{gemfile}.lock"
52 | end
53 | File.expand_path(lockfile)
54 | end
55 |
56 | def lockfile_version
57 | return unless File.file?(lockfile)
58 | lockfile_contents = File.read(lockfile)
59 | return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
60 | Regexp.last_match(1)
61 | end
62 |
63 | def bundler_version
64 | @bundler_version ||=
65 | env_var_version || cli_arg_version ||
66 | lockfile_version
67 | end
68 |
69 | def bundler_requirement
70 | return "#{Gem::Requirement.default}.a" unless bundler_version
71 |
72 | bundler_gem_version = Gem::Version.new(bundler_version)
73 |
74 | requirement = bundler_gem_version.approximate_recommendation
75 |
76 | return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.7.0")
77 |
78 | requirement += ".a" if bundler_gem_version.prerelease?
79 |
80 | requirement
81 | end
82 |
83 | def load_bundler!
84 | ENV["BUNDLE_GEMFILE"] ||= gemfile
85 |
86 | activate_bundler
87 | end
88 |
89 | def activate_bundler
90 | gem_error = activation_error_handling do
91 | gem "bundler", bundler_requirement
92 | end
93 | return if gem_error.nil?
94 | require_error = activation_error_handling do
95 | require "bundler/version"
96 | end
97 | return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
98 | warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
99 | exit 42
100 | end
101 |
102 | def activation_error_handling
103 | yield
104 | nil
105 | rescue StandardError, LoadError => e
106 | e
107 | end
108 | end
109 |
110 | m.load_bundler!
111 |
112 | if m.invoked_as_script?
113 | load Gem.bin_path("bundler", "bundle")
114 | end
115 |
--------------------------------------------------------------------------------
/bin/delayed_job:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment'))
4 | require 'delayed/command'
5 | Delayed::Command.new(ARGV).daemonize
6 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path('../config/application', __dir__)
3 | require_relative '../config/boot'
4 | require 'rails/commands'
5 |
--------------------------------------------------------------------------------
/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative '../config/boot'
3 | require 'rake'
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'fileutils'
3 |
4 | # path to your application root.
5 | APP_ROOT = File.expand_path('..', __dir__)
6 |
7 | def system!(*args)
8 | system(*args) || abort("\n== Command #{args} failed ==")
9 | end
10 |
11 | FileUtils.chdir APP_ROOT do
12 | # This script is a starting point to setup your application.
13 | # Add necessary setup steps to this file.
14 |
15 | puts '== Installing dependencies =='
16 | system! 'gem install bundler --conservative'
17 | system('bundle check') || system!('bundle install')
18 |
19 | # Install JavaScript dependencies if using Yarn
20 | # system('bin/yarn')
21 |
22 | # puts "\n== Copying sample files =="
23 | # unless File.exist?('config/database.yml')
24 | # cp 'config/database.yml.sample', 'config/database.yml'
25 | # end
26 |
27 | puts "\n== Preparing database =="
28 | system! 'bin/rails db:setup'
29 |
30 | puts "\n== Removing old logs and tempfiles =="
31 | system! 'bin/rails log:clear tmp:clear'
32 |
33 | puts "\n== Restarting application server =="
34 | system! 'bin/rails restart'
35 | end
36 |
--------------------------------------------------------------------------------
/bin/spring:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # This file loads spring without using Bundler, in order to be fast.
4 | # It gets overwritten when you run the `spring binstub` command.
5 |
6 | unless defined?(Spring)
7 | require 'rubygems'
8 | require 'bundler'
9 |
10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read)
11 | spring = lockfile.specs.detect { |spec| spec.name == "spring" }
12 | if spring
13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
14 | gem 'spring', spring.version
15 | require 'spring/binstub'
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/bin/update:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'fileutils'
3 | include FileUtils
4 |
5 | # path to your application root.
6 | APP_ROOT = File.expand_path('..', __dir__)
7 |
8 | def system!(*args)
9 | system(*args) || abort("\n== Command #{args} failed ==")
10 | end
11 |
12 | chdir APP_ROOT do
13 | # This script is a way to update your development environment automatically.
14 | # Add necessary update steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | # Install JavaScript dependencies if using Yarn
21 | # system('bin/yarn')
22 |
23 | puts "\n== Updating database =="
24 | system! 'bin/rails db:migrate'
25 |
26 | puts "\n== Removing old logs and tempfiles =="
27 | system! 'bin/rails log:clear tmp:clear'
28 |
29 | puts "\n== Restarting application server =="
30 | system! 'bin/rails restart'
31 | end
32 |
--------------------------------------------------------------------------------
/bin/webpack:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
4 | ENV["NODE_ENV"] ||= "development"
5 |
6 | require "pathname"
7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
8 | Pathname.new(__FILE__).realpath)
9 |
10 | require "bundler/setup"
11 |
12 | require "webpacker"
13 | require "webpacker/webpack_runner"
14 |
15 | APP_ROOT = File.expand_path("..", __dir__)
16 | Dir.chdir(APP_ROOT) do
17 | Webpacker::WebpackRunner.run(ARGV)
18 | end
19 |
--------------------------------------------------------------------------------
/bin/webpack-dev-server:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
4 | ENV["NODE_ENV"] ||= "development"
5 |
6 | require "pathname"
7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
8 | Pathname.new(__FILE__).realpath)
9 |
10 | require "bundler/setup"
11 |
12 | require "webpacker"
13 | require "webpacker/dev_server_runner"
14 |
15 | APP_ROOT = File.expand_path("..", __dir__)
16 | Dir.chdir(APP_ROOT) do
17 | Webpacker::DevServerRunner.run(ARGV)
18 | end
19 |
--------------------------------------------------------------------------------
/bin/yarn:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_ROOT = File.expand_path('..', __dir__)
3 | Dir.chdir(APP_ROOT) do
4 | begin
5 | exec "yarnpkg", *ARGV
6 | rescue Errno::ENOENT
7 | $stderr.puts "Yarn executable was not detected in the system."
8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
9 | exit 1
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is used by Rack-based servers to start the application.
4 |
5 | require_relative 'config/environment'
6 |
7 | run Rails.application
8 |
--------------------------------------------------------------------------------
/config/application.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative 'boot'
4 |
5 | require 'rails/all'
6 |
7 | # Require the gems listed in Gemfile, including any gems
8 | # you've limited to :test, :development, or :production.
9 | Bundler.require(*Rails.groups)
10 |
11 | module AppointmentRemindersRails
12 | class Application < Rails::Application
13 | # Initialize configuration defaults for originally generated Rails version.
14 | config.load_defaults 6.0
15 |
16 | # Settings in config/environments/* take precedence over those specified here.
17 | # Application configuration can go into files in config/initializers
18 | # -- all .rb files in that directory are automatically loaded after loading
19 | # the framework and any gems in your application.
20 |
21 | config.active_job.queue_adapter = :delayed_job
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/config/boot.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
4 |
5 | require 'bundler/setup' # Set up gems listed in the Gemfile.
6 | require 'bootsnap/setup' # Speed up boot time by caching expensive operations.
7 |
--------------------------------------------------------------------------------
/config/cable.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: async
3 |
4 | test:
5 | adapter: test
6 |
7 | production:
8 | adapter: redis
9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
10 | channel_prefix: appointment_reminders_rails_production
11 |
--------------------------------------------------------------------------------
/config/credentials.yml.enc:
--------------------------------------------------------------------------------
1 | ECzkMFm8W848suTTcp0w+63K01teC9l14+7O3XoOjNTthBVLfmSHm4R0Nrs3TpxO2I4m0KwaK15xs9m/yvqiMs9O1afNpHRPSDWkirE2DDuopBcQydw+Q9qmnSjkdysvKu/I/mDqWjcyWgQZdBEUYon+ZbBIzluAAIBlnvap+pGwhHlVQ30r827AI2aMil9IrnPJSpA2rV9DWSGkWFrYWeqIoZXeiKimALNmKR5K2EmC9S3Y9rkhvBdw/Wc6uIOYzY801xROQLQk2h20Uqo5+ottKno3OoDIVO5asd7mxFmnZyNSQw6q2vUCwntMg5ulTdmfNyA0L4J3/EH1QbiV4alDpguPJcaaKgRljhOGcwRHtDTeKm7tVfJrmRYY16YgpnJxDorIln96HK2cw0zRkrTaRlhNLHFUwrQF--D9oGnRiJ4ahmk7PO--X3syz4XUSfFVvpBGONiZDA==
--------------------------------------------------------------------------------
/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite. Versions 3.8.0 and up are supported.
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | #
7 | default: &default
8 | adapter: sqlite3
9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
10 | timeout: 5000
11 |
12 | development:
13 | <<: *default
14 | database: db/development.sqlite3
15 |
16 | # Warning: The database defined as "test" will be erased and
17 | # re-generated from your development database when you run "rake".
18 | # Do not set this db to the same as development or production.
19 | test:
20 | <<: *default
21 | database: db/test.sqlite3
22 |
23 | production:
24 | <<: *default
25 | database: db/production.sqlite3
26 |
--------------------------------------------------------------------------------
/config/environment.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Load the Rails application.
4 | require_relative 'application'
5 |
6 | # Initialize the Rails application.
7 | Rails.application.initialize!
8 |
--------------------------------------------------------------------------------
/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Rails.application.configure do
4 | # Settings specified here will take precedence over those in config/application.rb.
5 |
6 | # In the development environment your application's code is reloaded on
7 | # every request. This slows down response time but is perfect for development
8 | # since you don't have to restart the web server when you make code changes.
9 | config.cache_classes = false
10 |
11 | # Do not eager load code on boot.
12 | config.eager_load = false
13 |
14 | # Show full error reports.
15 | config.consider_all_requests_local = true
16 |
17 | # Enable/disable caching. By default caching is disabled.
18 | # Run rails dev:cache to toggle caching.
19 | if Rails.root.join('tmp', 'caching-dev.txt').exist?
20 | config.action_controller.perform_caching = true
21 | config.action_controller.enable_fragment_cache_logging = true
22 |
23 | config.cache_store = :memory_store
24 | config.public_file_server.headers = {
25 | 'Cache-Control' => "public, max-age=#{2.days.to_i}"
26 | }
27 | else
28 | config.action_controller.perform_caching = false
29 |
30 | config.cache_store = :null_store
31 | end
32 |
33 | # Store uploaded files on the local file system (see config/storage.yml for options).
34 | config.active_storage.service = :local
35 |
36 | # Don't care if the mailer can't send.
37 | config.action_mailer.raise_delivery_errors = false
38 |
39 | config.action_mailer.perform_caching = false
40 |
41 | # Print deprecation notices to the Rails logger.
42 | config.active_support.deprecation = :log
43 |
44 | # Raise an error on page load if there are pending migrations.
45 | config.active_record.migration_error = :page_load
46 |
47 | # Highlight code that triggered database queries in logs.
48 | config.active_record.verbose_query_logs = true
49 |
50 | # Debug mode disables concatenation and preprocessing of assets.
51 | # This option may cause significant delays in view rendering with a large
52 | # number of complex assets.
53 | config.assets.debug = true
54 |
55 | # Suppress logger output for asset requests.
56 | config.assets.quiet = true
57 |
58 | # Raises error for missing translations.
59 | # config.action_view.raise_on_missing_translations = true
60 |
61 | # Use an evented file watcher to asynchronously detect changes in source code,
62 | # routes, locales, etc. This feature depends on the listen gem.
63 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker
64 |
65 | Rails.logger = Logger.new(STDOUT)
66 | Rails.logger.datetime_format = '%Y-%m-%d %H:%M:%S'
67 |
68 | # log formatter
69 | Rails.logger.formatter = proc do |severity, datetime, progname, msg|
70 | "#{datetime}, #{severity}: #{progname} #{msg} \n"
71 | end
72 | config.logger = ActiveSupport::Logger.new("log/#{Rails.env}.log")
73 | end
74 |
--------------------------------------------------------------------------------
/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Rails.application.configure do
4 | # Settings specified here will take precedence over those in config/application.rb.
5 |
6 | # Code is not reloaded between requests.
7 | config.cache_classes = true
8 |
9 | # Eager load code on boot. This eager loads most of Rails and
10 | # your application in memory, allowing both threaded web servers
11 | # and those relying on copy on write to perform better.
12 | # Rake tasks automatically ignore this option for performance.
13 | config.eager_load = true
14 |
15 | # Full error reports are disabled and caching is turned on.
16 | config.consider_all_requests_local = false
17 | config.action_controller.perform_caching = true
18 |
19 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
20 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
21 | # config.require_master_key = true
22 |
23 | # Disable serving static files from the `/public` folder by default since
24 | # Apache or NGINX already handles this.
25 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
26 |
27 | # Compress CSS using a preprocessor.
28 | # config.assets.css_compressor = :sass
29 |
30 | # Do not fallback to assets pipeline if a precompiled asset is missed.
31 | config.assets.compile = false
32 |
33 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
34 | # config.action_controller.asset_host = 'http://assets.example.com'
35 |
36 | # Specifies the header that your server uses for sending files.
37 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
38 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
39 |
40 | # Store uploaded files on the local file system (see config/storage.yml for options).
41 | config.active_storage.service = :local
42 |
43 | # Mount Action Cable outside main process or domain.
44 | # config.action_cable.mount_path = nil
45 | # config.action_cable.url = 'wss://example.com/cable'
46 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
47 |
48 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
49 | # config.force_ssl = true
50 |
51 | # Use the lowest log level to ensure availability of diagnostic information
52 | # when problems arise.
53 | config.log_level = :debug
54 |
55 | # Prepend all log lines with the following tags.
56 | config.log_tags = [:request_id]
57 |
58 | # Use a different cache store in production.
59 | # config.cache_store = :mem_cache_store
60 |
61 | # Use a real queuing backend for Active Job (and separate queues per environment).
62 | # config.active_job.queue_adapter = :resque
63 | # config.active_job.queue_name_prefix = "appointment_reminders_rails_#{Rails.env}"
64 |
65 | config.action_mailer.perform_caching = false
66 |
67 | # Ignore bad email addresses and do not raise email delivery errors.
68 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
69 | # config.action_mailer.raise_delivery_errors = false
70 |
71 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
72 | # the I18n.default_locale when a translation cannot be found).
73 | config.i18n.fallbacks = true
74 |
75 | # Send deprecation notices to registered listeners.
76 | config.active_support.deprecation = :notify
77 |
78 | # Use default logging formatter so that PID and timestamp are not suppressed.
79 | config.log_formatter = ::Logger::Formatter.new
80 |
81 | # Use a different logger for distributed setups.
82 | # require 'syslog/logger'
83 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
84 |
85 | if ENV['RAILS_LOG_TO_STDOUT'].present?
86 | logger = ActiveSupport::Logger.new(STDOUT)
87 | logger.formatter = config.log_formatter
88 | config.logger = ActiveSupport::TaggedLogging.new(logger)
89 | end
90 |
91 | # Do not dump schema after migrations.
92 | config.active_record.dump_schema_after_migration = false
93 |
94 | # Inserts middleware to perform automatic connection switching.
95 | # The `database_selector` hash is used to pass options to the DatabaseSelector
96 | # middleware. The `delay` is used to determine how long to wait after a write
97 | # to send a subsequent read to the primary.
98 | #
99 | # The `database_resolver` class is used by the middleware to determine which
100 | # database is appropriate to use based on the time delay.
101 | #
102 | # The `database_resolver_context` class is used by the middleware to set
103 | # timestamps for the last write to the primary. The resolver uses the context
104 | # class timestamps to determine how long to wait before reading from the
105 | # replica.
106 | #
107 | # By default Rails will store a last write timestamp in the session. The
108 | # DatabaseSelector middleware is designed as such you can define your own
109 | # strategy for connection switching and pass that into the middleware through
110 | # these configuration options.
111 | # config.active_record.database_selector = { delay: 2.seconds }
112 | # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
113 | # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
114 | end
115 |
--------------------------------------------------------------------------------
/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # The test environment is used exclusively to run your application's
4 | # test suite. You never need to work with it otherwise. Remember that
5 | # your test database is "scratch space" for the test suite and is wiped
6 | # and recreated between test runs. Don't rely on the data there!
7 |
8 | Rails.application.configure do
9 | # Settings specified here will take precedence over those in config/application.rb.
10 |
11 | config.cache_classes = false
12 |
13 | # Do not eager load code on boot. This avoids loading your whole application
14 | # just for the purpose of running a single test. If you are using a tool that
15 | # preloads Rails for running tests, you may have to set it to true.
16 | config.eager_load = false
17 |
18 | # Configure public file server for tests with Cache-Control for performance.
19 | config.public_file_server.enabled = true
20 | config.public_file_server.headers = {
21 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}"
22 | }
23 |
24 | # Show full error reports and disable caching.
25 | config.consider_all_requests_local = true
26 | config.action_controller.perform_caching = false
27 | config.cache_store = :null_store
28 |
29 | # Raise exceptions instead of rendering exception templates.
30 | config.action_dispatch.show_exceptions = false
31 |
32 | # Disable request forgery protection in test environment.
33 | config.action_controller.allow_forgery_protection = false
34 |
35 | # Store uploaded files on the local file system in a temporary directory.
36 | config.active_storage.service = :test
37 |
38 | config.action_mailer.perform_caching = false
39 |
40 | # Tell Action Mailer not to deliver emails to the real world.
41 | # The :test delivery method accumulates sent emails in the
42 | # ActionMailer::Base.deliveries array.
43 | config.action_mailer.delivery_method = :test
44 |
45 | # Print deprecation notices to the stderr.
46 | config.active_support.deprecation = :stderr
47 |
48 | # Raises error for missing translations.
49 | # config.action_view.raise_on_missing_translations = true
50 |
51 | Rails.logger = Logger.new(STDOUT)
52 | Rails.logger.datetime_format = '%Y-%m-%d %H:%M:%S'
53 |
54 | # log formatter
55 | Rails.logger.formatter = proc do |severity, datetime, progname, msg|
56 | "#{datetime}, #{severity}: #{progname} #{msg} \n"
57 | end
58 | end
59 |
--------------------------------------------------------------------------------
/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | # Be sure to restart your server when you modify this file.
3 |
4 | # ActiveSupport::Reloader.to_prepare do
5 | # ApplicationController.renderer.defaults.merge!(
6 | # http_host: 'example.org',
7 | # https: false
8 | # )
9 | # end
10 |
--------------------------------------------------------------------------------
/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Version of your assets, change this if you want to expire all your assets.
6 | Rails.application.config.assets.version = '1.0'
7 |
8 | # Add additional assets to the asset load path.
9 | # Rails.application.config.assets.paths << Emoji.images_path
10 | # Add Yarn node_modules folder to the asset load path.
11 | Rails.application.config.assets.paths << Rails.root.join('node_modules')
12 |
13 | # Precompile additional assets.
14 | # application.js, application.css, and all non-JS/CSS in the app/assets
15 | # folder are already added.
16 | # Rails.application.config.assets.precompile += %w( admin.js admin.css )
17 |
--------------------------------------------------------------------------------
/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | # Be sure to restart your server when you modify this file.
3 |
4 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
5 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
6 |
7 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
8 | # Rails.backtrace_cleaner.remove_silencers!
9 |
--------------------------------------------------------------------------------
/config/initializers/content_security_policy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | # Be sure to restart your server when you modify this file.
3 |
4 | # Define an application-wide content security policy
5 | # For further information see the following documentation
6 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
7 |
8 | # Rails.application.config.content_security_policy do |policy|
9 | # policy.default_src :self, :https
10 | # policy.font_src :self, :https, :data
11 | # policy.img_src :self, :https, :data
12 | # policy.object_src :none
13 | # policy.script_src :self, :https
14 | # policy.style_src :self, :https
15 | # # If you are using webpack-dev-server then specify webpack-dev-server host
16 | # policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
17 |
18 | # # Specify URI for violation reports
19 | # # policy.report_uri "/csp-violation-report-endpoint"
20 | # end
21 |
22 | # If you are using UJS then enable automatic nonce generation
23 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
24 |
25 | # Set the nonce only to specific directives
26 | # Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
27 |
28 | # Report CSP violations to a specified URI
29 | # For further information see the following documentation:
30 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
31 | # Rails.application.config.content_security_policy_report_only = true
32 |
--------------------------------------------------------------------------------
/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Specify a serializer for the signed and encrypted cookie jars.
6 | # Valid options are :json, :marshal, and :hybrid.
7 | Rails.application.config.action_dispatch.cookies_serializer = :json
8 |
--------------------------------------------------------------------------------
/config/initializers/delayed_job_config.rb:
--------------------------------------------------------------------------------
1 | # config/initializers/delayed_job_config.rb
2 | Delayed::Worker.destroy_failed_jobs = false
3 | Delayed::Worker.sleep_delay = 60
4 | Delayed::Worker.max_attempts = 3
5 | Delayed::Worker.max_run_time = 5.minutes
6 | Delayed::Worker.read_ahead = 10
7 | Delayed::Worker.default_queue_name = 'default'
8 | Delayed::Worker.delay_jobs = !Rails.env.test?
9 | Delayed::Worker.raise_signal_exceptions = :term
10 | Delayed::Worker.logger = Logger.new(File.join(Rails.root, 'log', 'delayed_job.log'))
11 |
--------------------------------------------------------------------------------
/config/initializers/dotenv.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Dotenv.require_keys(
4 | 'TWILIO_ACCOUNT_SID',
5 | 'TWILIO_AUTH_TOKEN',
6 | 'TWILIO_NUMBER'
7 | )
8 |
--------------------------------------------------------------------------------
/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Configure sensitive parameters which will be filtered from the log file.
6 | Rails.application.config.filter_parameters += [:password]
7 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | # Be sure to restart your server when you modify this file.
3 |
4 | # Add new inflection rules using the following format. Inflections
5 | # are locale specific, and you may define rules for as many different
6 | # locales as you wish. All of these examples are active by default:
7 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
8 | # inflect.plural /^(ox)$/i, '\1en'
9 | # inflect.singular /^(ox)en/i, '\1'
10 | # inflect.irregular 'person', 'people'
11 | # inflect.uncountable %w( fish sheep )
12 | # end
13 |
14 | # These inflection rules are supported but not enabled by default:
15 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
16 | # inflect.acronym 'RESTful'
17 | # end
18 |
--------------------------------------------------------------------------------
/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | # Be sure to restart your server when you modify this file.
3 |
4 | # Add new mime types for use in respond_to blocks:
5 | # Mime::Type.register "text/richtext", :rtf
6 |
--------------------------------------------------------------------------------
/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # This file contains settings for ActionController::ParamsWrapper which
6 | # is enabled by default.
7 |
8 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
9 | ActiveSupport.on_load(:action_controller) do
10 | wrap_parameters format: [:json]
11 | end
12 |
13 | # To enable root element in JSON for ActiveRecord objects.
14 | # ActiveSupport.on_load(:active_record) do
15 | # self.include_root_in_json = true
16 | # end
17 |
--------------------------------------------------------------------------------
/config/locales/en.bootstrap.yml:
--------------------------------------------------------------------------------
1 | # Sample localization file for English. Add more files in this directory for other locales.
2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3 |
4 | en:
5 | breadcrumbs:
6 | application:
7 | root: "Index"
8 | pages:
9 | pages: "Pages"
10 | helpers:
11 | actions: "Actions"
12 | links:
13 | back: "Back"
14 | cancel: "Cancel"
15 | confirm: "Are you sure?"
16 | destroy: "Delete"
17 | new: "New"
18 | edit: "Edit"
19 | titles:
20 | edit: "Edit %{model}"
21 | save: "Save %{model}"
22 | new: "New %{model}"
23 | delete: "Delete %{model}"
24 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # The following keys must be escaped otherwise they will not be retrieved by
20 | # the default I18n backend:
21 | #
22 | # true, false, on, off, yes, no
23 | #
24 | # Instead, surround them with single quotes.
25 | #
26 | # en:
27 | # 'true': 'foo'
28 | #
29 | # To learn more, please read the Rails Internationalization guide
30 | # available at https://guides.rubyonrails.org/i18n.html.
31 |
32 | en:
33 | hello: "Hello world"
34 |
--------------------------------------------------------------------------------
/config/puma.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Puma can serve each request in a thread from an internal thread pool.
4 | # The `threads` method setting takes two numbers: a minimum and maximum.
5 | # Any libraries that use thread pools should be configured to match
6 | # the maximum value specified for Puma. Default is set to 5 threads for minimum
7 | # and maximum; this matches the default thread size of Active Record.
8 | #
9 | max_threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 }
10 | min_threads_count = ENV.fetch('RAILS_MIN_THREADS') { max_threads_count }
11 | threads min_threads_count, max_threads_count
12 |
13 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
14 | #
15 | port ENV.fetch('PORT') { 3000 }
16 |
17 | # Specifies the `environment` that Puma will run in.
18 | #
19 | environment ENV.fetch('RAILS_ENV') { 'development' }
20 |
21 | # Specifies the `pidfile` that Puma will use.
22 | pidfile ENV.fetch('PIDFILE') { 'tmp/pids/server.pid' }
23 |
24 | # Specifies the number of `workers` to boot in clustered mode.
25 | # Workers are forked web server processes. If using threads and workers together
26 | # the concurrency of the application would be max `threads` * `workers`.
27 | # Workers do not work on JRuby or Windows (both of which do not support
28 | # processes).
29 | #
30 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 }
31 |
32 | # Use the `preload_app!` method when specifying a `workers` number.
33 | # This directive tells Puma to first boot the application and load code
34 | # before forking the application. This takes advantage of Copy On Write
35 | # process behavior so workers use less memory.
36 | #
37 | # preload_app!
38 |
39 | # Allow puma to be restarted by `rails restart` command.
40 | plugin :tmp_restart
41 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Rails.application.routes.draw do
4 | resources :appointments
5 | root 'appointments#welcome'
6 | end
7 |
--------------------------------------------------------------------------------
/config/spring.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Spring.watch(
4 | '.ruby-version',
5 | '.rbenv-vars',
6 | 'tmp/restart.txt',
7 | 'tmp/caching-dev.txt'
8 | )
9 |
--------------------------------------------------------------------------------
/config/storage.yml:
--------------------------------------------------------------------------------
1 | test:
2 | service: Disk
3 | root: <%= Rails.root.join("tmp/storage") %>
4 |
5 | local:
6 | service: Disk
7 | root: <%= Rails.root.join("storage") %>
8 |
9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10 | # amazon:
11 | # service: S3
12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14 | # region: us-east-1
15 | # bucket: your_own_bucket
16 |
17 | # Remember not to checkin your GCS keyfile to a repository
18 | # google:
19 | # service: GCS
20 | # project: your_project
21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
22 | # bucket: your_own_bucket
23 |
24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25 | # microsoft:
26 | # service: AzureStorage
27 | # storage_account_name: your_account_name
28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
29 | # container: your_container_name
30 |
31 | # mirror:
32 | # service: Mirror
33 | # primary: local
34 | # mirrors: [ amazon, google, microsoft ]
35 |
--------------------------------------------------------------------------------
/config/webpack/development.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/config/webpack/environment.js:
--------------------------------------------------------------------------------
1 | const { environment } = require('@rails/webpacker')
2 |
3 | const webpack = require('webpack')
4 | environment.plugins.append(
5 | 'Provide',
6 | new webpack.ProvidePlugin({
7 | $: 'jquery',
8 | jQuery: 'jquery',
9 | Popper: ['popper.js', 'default']
10 | })
11 | )
12 |
13 | module.exports = environment
14 |
--------------------------------------------------------------------------------
/config/webpack/production.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'production'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/config/webpack/test.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/config/webpacker.yml:
--------------------------------------------------------------------------------
1 | # Note: You must restart bin/webpack-dev-server for changes to take effect
2 |
3 | default: &default
4 | source_path: app/javascript
5 | source_entry_path: packs
6 | public_root_path: public
7 | public_output_path: packs
8 | cache_path: tmp/cache/webpacker
9 | check_yarn_integrity: false
10 | webpack_compile_output: true
11 |
12 | # Additional paths webpack should lookup modules
13 | # ['app/assets', 'engine/foo/app/assets']
14 | resolved_paths: []
15 |
16 | # Reload manifest.json on all requests so we reload latest compiled packs
17 | cache_manifest: false
18 |
19 | # Extract and emit a css file
20 | extract_css: false
21 |
22 | static_assets_extensions:
23 | - .jpg
24 | - .jpeg
25 | - .png
26 | - .gif
27 | - .tiff
28 | - .ico
29 | - .svg
30 | - .eot
31 | - .otf
32 | - .ttf
33 | - .woff
34 | - .woff2
35 |
36 | extensions:
37 | - .mjs
38 | - .js
39 | - .sass
40 | - .scss
41 | - .css
42 | - .module.sass
43 | - .module.scss
44 | - .module.css
45 | - .png
46 | - .svg
47 | - .gif
48 | - .jpeg
49 | - .jpg
50 |
51 | development:
52 | <<: *default
53 | compile: true
54 |
55 | # Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules
56 | check_yarn_integrity: false
57 |
58 | # Reference: https://webpack.js.org/configuration/dev-server/
59 | dev_server:
60 | https: false
61 | host: localhost
62 | port: 3035
63 | public: localhost:3035
64 | hmr: false
65 | # Inline should be set to true if using HMR
66 | inline: true
67 | overlay: true
68 | compress: true
69 | disable_host_check: true
70 | use_local_ip: false
71 | quiet: false
72 | pretty: false
73 | headers:
74 | 'Access-Control-Allow-Origin': '*'
75 | watch_options:
76 | ignored: '**/node_modules/**'
77 |
78 |
79 | test:
80 | <<: *default
81 | compile: true
82 |
83 | # Compile test packs to a separate directory
84 | public_output_path: packs-test
85 |
86 | production:
87 | <<: *default
88 |
89 | # Production depends on precompilation of packs prior to booting for performance.
90 | compile: false
91 |
92 | # Extract and emit a css file
93 | extract_css: true
94 |
95 | # Cache manifest.json for performance
96 | cache_manifest: true
97 |
--------------------------------------------------------------------------------
/db/migrate/20190121114109_create_delayed_jobs.rb:
--------------------------------------------------------------------------------
1 | class CreateDelayedJobs < ActiveRecord::Migration[6.0]
2 | def self.up
3 | create_table :delayed_jobs, force: true do |table|
4 | table.integer :priority, default: 0, null: false # Allows some jobs to jump to the front of the queue
5 | table.integer :attempts, default: 0, null: false # Provides for retries, but still fail eventually.
6 | table.text :handler, null: false # YAML-encoded string of the object that will do work
7 | table.text :last_error # reason for last failure (See Note below)
8 | table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future.
9 | table.datetime :locked_at # Set when a client is working on this object
10 | table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead)
11 | table.string :locked_by # Who is working on this object (if locked)
12 | table.string :queue # The name of the queue this job is in
13 | table.timestamps null: true
14 | end
15 |
16 | add_index :delayed_jobs, [:priority, :run_at], name: "delayed_jobs_priority"
17 | end
18 |
19 | def self.down
20 | drop_table :delayed_jobs
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/db/schema.rb:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from the current state of the database. Instead
2 | # of editing this file, please use the migrations feature of Active Record to
3 | # incrementally modify your database, and then regenerate this schema definition.
4 | #
5 | # Note that this schema.rb definition is the authoritative source for your
6 | # database schema. If you need to create the application database on another
7 | # system, you should be using db:schema:load, not running all the migrations
8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9 | # you'll amass, the slower it'll run and the greater likelihood for issues).
10 | #
11 | # It's strongly recommended that you check this file into your version control system.
12 |
13 | ActiveRecord::Schema.define(version: 2019_01_21_114109) do
14 | create_table "appointments", force: :cascade do |t|
15 | t.string "name"
16 | t.string "phone_number"
17 | t.datetime "time"
18 | t.datetime "created_at", null: false
19 | t.datetime "updated_at", null: false
20 | t.string "time_zone"
21 | end
22 |
23 | create_table "delayed_jobs", force: :cascade do |t|
24 | t.integer "priority", default: 0, null: false
25 | t.integer "attempts", default: 0, null: false
26 | t.text "handler", null: false
27 | t.text "last_error"
28 | t.datetime "run_at"
29 | t.datetime "locked_at"
30 | t.datetime "failed_at"
31 | t.string "locked_by"
32 | t.string "queue"
33 | t.datetime "created_at"
34 | t.datetime "updated_at"
35 | t.index ["priority", "run_at"], name: "delayed_jobs_priority"
36 | end
37 |
38 | end
39 |
--------------------------------------------------------------------------------
/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | # This file should contain all the record creation needed to seed the database with its default values.
3 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup).
4 | #
5 | # Examples:
6 | #
7 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
8 | # Character.create(name: 'Luke', movie: movies.first)
9 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.8"
2 | services:
3 | app:
4 | build: .
5 | ports:
6 | - "3000:3000"
7 |
--------------------------------------------------------------------------------
/lib/assets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TwilioDevEd/appointment-reminders-rails/729741de628e8fc4b0c3decafeba2d824aeea9c3/lib/assets/.keep
--------------------------------------------------------------------------------
/lib/tasks/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TwilioDevEd/appointment-reminders-rails/729741de628e8fc4b0c3decafeba2d824aeea9c3/lib/tasks/.keep
--------------------------------------------------------------------------------
/log/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TwilioDevEd/appointment-reminders-rails/729741de628e8fc4b0c3decafeba2d824aeea9c3/log/.keep
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "appointment-reminders-rails",
3 | "private": true,
4 | "dependencies": {
5 | "@rails/actioncable": "^6.0.0",
6 | "@rails/activestorage": "^6.0.0",
7 | "@rails/ujs": "^6.0.0",
8 | "@rails/webpacker": "4.2.2",
9 | "bootstrap": "^4.4.1",
10 | "jquery": "^3.4.1",
11 | "popper.js": "^1.16.1",
12 | "turbolinks": "^5.2.0"
13 | },
14 | "version": "0.1.0",
15 | "devDependencies": {
16 | "webpack-dev-server": "^3.11.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('postcss-import'),
4 | require('postcss-flexbugs-fixes'),
5 | require('postcss-preset-env')({
6 | autoprefixer: {
7 | flexbox: 'no-2009'
8 | },
9 | stage: 3
10 | })
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The page you were looking for doesn't exist.
62 |
You may have mistyped the address or the page may have moved.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The change you wanted was rejected.
62 |
Maybe you tried to change something you didn't have access to.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
We're sorry, but something went wrong.
62 |
63 |
If you are the application owner check the logs for more information.
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/public/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TwilioDevEd/appointment-reminders-rails/729741de628e8fc4b0c3decafeba2d824aeea9c3/public/apple-touch-icon-precomposed.png
--------------------------------------------------------------------------------
/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TwilioDevEd/appointment-reminders-rails/729741de628e8fc4b0c3decafeba2d824aeea9c3/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TwilioDevEd/appointment-reminders-rails/729741de628e8fc4b0c3decafeba2d824aeea9c3/public/favicon.ico
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 |
--------------------------------------------------------------------------------
/storage/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TwilioDevEd/appointment-reminders-rails/729741de628e8fc4b0c3decafeba2d824aeea9c3/storage/.keep
--------------------------------------------------------------------------------
/test/application_system_test_case.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
4 | driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
5 | end
6 |
--------------------------------------------------------------------------------
/test/controllers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TwilioDevEd/appointment-reminders-rails/729741de628e8fc4b0c3decafeba2d824aeea9c3/test/controllers/.keep
--------------------------------------------------------------------------------
/test/controllers/appointments_controller_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class AppointmentsControllerTest < ActionController::TestCase
4 | setup do
5 | @appointment = appointments(:one)
6 | end
7 |
8 | test "should get index" do
9 | get :index
10 | assert_response :success
11 | assert_not_nil assigns(:appointments)
12 | end
13 |
14 | test "should get new" do
15 | get :new
16 | assert_response :success
17 | end
18 |
19 | test "should create appointment" do
20 | assert_difference('Appointment.count') do
21 | Appointment.skip_callback(:create, :after, :reminder)
22 | post :create, params: {appointment: { name: @appointment.name, phone_number: @appointment.phone_number, time: @appointment.time }}
23 | end
24 |
25 | assert_redirected_to appointment_path(assigns(:appointment))
26 | end
27 |
28 | test "should show appointment" do
29 | get :show, params: { id: @appointment }
30 | assert_response :success
31 | end
32 |
33 | test "should get edit" do
34 | get :edit, params: { id: @appointment }
35 | assert_response :success
36 | end
37 |
38 | test "should update appointment" do
39 | patch :update, params: { id: @appointment, appointment: { name: @appointment.name, phone_number: @appointment.phone_number, time: @appointment.time } }
40 | assert_redirected_to appointment_path(assigns(:appointment))
41 | end
42 |
43 | test "should destroy appointment" do
44 | assert_difference('Appointment.count', -1) do
45 | delete :destroy, params: { id: @appointment }
46 | end
47 |
48 | assert_redirected_to appointments_path
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/test/fixtures/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TwilioDevEd/appointment-reminders-rails/729741de628e8fc4b0c3decafeba2d824aeea9c3/test/fixtures/.keep
--------------------------------------------------------------------------------
/test/fixtures/appointments.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 |
3 | one:
4 | id: 1
5 | name: TestOne
6 | phone_number: "a_phone_number"
7 | time: 2018-03-12 10:33:18
8 |
--------------------------------------------------------------------------------
/test/fixtures/files/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TwilioDevEd/appointment-reminders-rails/729741de628e8fc4b0c3decafeba2d824aeea9c3/test/fixtures/files/.keep
--------------------------------------------------------------------------------
/test/helpers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TwilioDevEd/appointment-reminders-rails/729741de628e8fc4b0c3decafeba2d824aeea9c3/test/helpers/.keep
--------------------------------------------------------------------------------
/test/integration/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TwilioDevEd/appointment-reminders-rails/729741de628e8fc4b0c3decafeba2d824aeea9c3/test/integration/.keep
--------------------------------------------------------------------------------
/test/mailers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TwilioDevEd/appointment-reminders-rails/729741de628e8fc4b0c3decafeba2d824aeea9c3/test/mailers/.keep
--------------------------------------------------------------------------------
/test/models/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TwilioDevEd/appointment-reminders-rails/729741de628e8fc4b0c3decafeba2d824aeea9c3/test/models/.keep
--------------------------------------------------------------------------------
/test/models/appointment_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class AppointmentTest < ActiveSupport::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/test/system/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TwilioDevEd/appointment-reminders-rails/729741de628e8fc4b0c3decafeba2d824aeea9c3/test/system/.keep
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | ENV['RAILS_ENV'] ||= 'test'
2 | require_relative '../config/environment'
3 | require 'rails/test_help'
4 |
5 | class ActiveSupport::TestCase
6 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
7 | fixtures :all
8 |
9 | # Add more helper methods to be used by all tests here...
10 | end
11 |
--------------------------------------------------------------------------------
/tmp/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TwilioDevEd/appointment-reminders-rails/729741de628e8fc4b0c3decafeba2d824aeea9c3/tmp/.keep
--------------------------------------------------------------------------------