├── .gitignore ├── .rspec ├── .ruby-version ├── Gemfile ├── Gemfile.lock ├── Guardfile ├── LICENSE.md ├── Procfile ├── README.md ├── Rakefile ├── app ├── assets │ ├── config │ │ └── manifest.js │ ├── images │ │ ├── .keep │ │ ├── kitten.jpg │ │ └── rails.png │ ├── javascripts │ │ ├── account_activations.coffee │ │ ├── application.js │ │ ├── cable.js │ │ ├── channels │ │ │ └── .keep │ │ ├── microposts.coffee │ │ ├── password_resets.coffee │ │ ├── relationships.coffee │ │ ├── sessions.coffee │ │ ├── static_pages.coffee │ │ └── users.coffee │ └── stylesheets │ │ ├── account_activations.scss │ │ ├── application.css │ │ ├── custom.scss │ │ ├── microposts.scss │ │ ├── password_resets.scss │ │ ├── relationships.scss │ │ ├── sessions.scss │ │ ├── static_pages.scss │ │ └── users.scss ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb ├── controllers │ ├── account_activations_controller.rb │ ├── api │ │ └── v1 │ │ │ ├── base_controller.rb │ │ │ ├── feeds_controller.rb │ │ │ ├── followers_controller.rb │ │ │ ├── followings_controller.rb │ │ │ ├── microposts_controller.rb │ │ │ ├── root_controller.rb │ │ │ ├── sessions_controller.rb │ │ │ └── users_controller.rb │ ├── application_controller.rb │ ├── concerns │ │ ├── .keep │ │ └── custom_errors.rb │ ├── microposts_controller.rb │ ├── password_resets_controller.rb │ ├── relationships_controller.rb │ ├── sessions_controller.rb │ ├── static_pages_controller.rb │ └── users_controller.rb ├── helpers │ ├── account_activations_helper.rb │ ├── application_helper.rb │ ├── microposts_helper.rb │ ├── password_resets_helper.rb │ ├── relationships_helper.rb │ ├── sessions_helper.rb │ ├── static_pages_helper.rb │ └── users_helper.rb ├── jobs │ └── application_job.rb ├── mailers │ ├── application_mailer.rb │ └── user_mailer.rb ├── models │ ├── application_record.rb │ ├── concerns │ │ └── .keep │ ├── micropost.rb │ ├── relationship.rb │ └── user.rb ├── policies │ ├── application_policy.rb │ ├── follower_policy.rb │ ├── following_policy.rb │ ├── micropost_policy.rb │ └── user_policy.rb ├── serializers │ └── api │ │ └── v1 │ │ ├── base_serializer.rb │ │ ├── error_serializer.rb │ │ ├── micropost_serializer.rb │ │ ├── session_serializer.rb │ │ └── user_serializer.rb ├── uploaders │ └── picture_uploader.rb └── views │ ├── layouts │ ├── _footer.html.erb │ ├── _header.html.erb │ ├── _shim.html.erb │ ├── application.html.erb │ ├── mailer.html.erb │ └── mailer.text.erb │ ├── microposts │ └── _micropost.html.erb │ ├── password_resets │ ├── edit.html.erb │ └── new.html.erb │ ├── relationships │ ├── create.js.erb │ └── destroy.js.erb │ ├── sessions │ └── new.html.erb │ ├── shared │ ├── _error_messages.html.erb │ ├── _feed.html.erb │ ├── _micropost_form.html.erb │ ├── _stats.html.erb │ └── _user_info.html.erb │ ├── static_pages │ ├── about.html.erb │ ├── contact.html.erb │ ├── help.html.erb │ └── home.html.erb │ ├── user_mailer │ ├── account_activation.html.erb │ ├── account_activation.text.erb │ ├── ember_account_activation.html.erb │ ├── ember_account_activation.text.erb │ ├── password_reset.html.erb │ └── password_reset.text.erb │ └── users │ ├── _follow.html.erb │ ├── _follow_form.html.erb │ ├── _unfollow.html.erb │ ├── _user.html.erb │ ├── edit.html.erb │ ├── index.html.erb │ ├── new.html.erb │ ├── show.html.erb │ └── show_follow.html.erb ├── bin ├── bundle ├── rails ├── rake ├── setup └── update ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cable.yml ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── active_model_serializers.rb │ ├── active_record_belongs_to_required_by_default.rb │ ├── application_controller_renderer.rb │ ├── assets.rb │ ├── callback_terminator.rb │ ├── carrier_wave.rb │ ├── cookies_serializer.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── per_form_csrf_tokens.rb │ ├── rack_attack.rb │ ├── request_forgery_protection.rb │ ├── session_store.rb │ ├── ssl_options.rb │ ├── to_time_preserves_timezone.rb │ └── wrap_parameters.rb ├── locales │ └── en.yml ├── puma.rb ├── routes.rb ├── secrets.yml └── spring.rb ├── db ├── migrate │ ├── 20160523185459_create_users.rb │ ├── 20160523202806_add_index_to_users_email.rb │ ├── 20160523203059_add_password_digest_to_users.rb │ ├── 20160602174637_add_remember_digest_to_users.rb │ ├── 20160605021434_add_admin_to_users.rb │ ├── 20160606194223_add_activation_to_users.rb │ ├── 20160606233616_add_reset_to_users.rb │ ├── 20160608164853_create_microposts.rb │ ├── 20160608202205_add_picture_to_microposts.rb │ ├── 20160609220802_create_relationships.rb │ ├── 20160807112453_add_token_to_users.rb │ └── 20160907183517_add_cache_counters.rb ├── schema.rb └── seeds.rb ├── docs ├── .gitignore ├── CHANGELOG.md ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── Vagrantfile ├── config.rb ├── deploy.sh ├── font-selection.json └── source │ ├── fonts │ ├── slate.eot │ ├── slate.svg │ ├── slate.ttf │ ├── slate.woff │ └── slate.woff2 │ ├── images │ ├── logo.png │ └── navbar.png │ ├── includes │ ├── _feed.md │ ├── _followers.md │ ├── _followings.md │ ├── _microposts.md │ ├── _overview.md │ ├── _sessions.md │ └── _users.md │ ├── index.html.md │ ├── javascripts │ ├── all.js │ ├── all_nosearch.js │ ├── app │ │ ├── _lang.js │ │ ├── _search.js │ │ └── _toc.js │ └── lib │ │ ├── _energize.js │ │ ├── _imagesloaded.min.js │ │ ├── _jquery.highlight.js │ │ ├── _jquery.js │ │ ├── _jquery.tocify.js │ │ ├── _jquery_ui.js │ │ └── _lunr.js │ ├── layouts │ └── layout.erb │ └── stylesheets │ ├── _icon-font.scss │ ├── _normalize.scss │ ├── _variables.scss │ ├── print.css.scss │ └── screen.css.scss ├── lib ├── assets │ └── .keep └── tasks │ ├── .keep │ └── auto_annotate_models.rake ├── log └── .keep ├── public ├── 404.html ├── 422.html ├── 500.html ├── api │ └── v1 │ │ └── docs │ │ ├── fonts │ │ ├── slate.eot │ │ ├── slate.svg │ │ ├── slate.ttf │ │ ├── slate.woff │ │ └── slate.woff2 │ │ ├── images │ │ ├── logo.png │ │ └── navbar.png │ │ ├── index.html │ │ ├── javascripts │ │ ├── all.js │ │ └── all_nosearch.js │ │ └── stylesheets │ │ ├── print.css │ │ └── screen.css ├── apple-touch-icon-precomposed.png ├── apple-touch-icon.png ├── favicon.ico └── robots.txt ├── spec ├── apis │ └── resource │ │ ├── feeds │ │ └── show_spec.rb │ │ ├── followers │ │ ├── destroy_spec.rb │ │ └── index_spec.rb │ │ ├── followings │ │ ├── create_spec.rb │ │ ├── destroy_spec.rb │ │ └── index_spec.rb │ │ ├── microposts │ │ ├── create_spec.rb │ │ ├── destroy_spec.rb │ │ ├── index_spec.rb │ │ ├── show_spec.rb │ │ └── update_spec.rb │ │ ├── sessions │ │ ├── create_spec.rb │ │ └── show_spec.rb │ │ └── users │ │ ├── activate_spec.rb │ │ ├── create_spec.rb │ │ ├── destroy_spec.rb │ │ ├── index_spec.rb │ │ ├── show_spec.rb │ │ └── update_spec.rb ├── factories │ ├── micropost.rb │ ├── relationships.rb │ └── users.rb ├── rails_helper.rb ├── schemas │ ├── admin │ │ ├── micropost.json │ │ ├── microposts.json │ │ ├── user.json │ │ └── users.json │ ├── errors.json │ ├── guest │ │ ├── user.json │ │ └── users.json │ └── regular │ │ ├── micropost.json │ │ ├── microposts.json │ │ ├── session.json │ │ ├── user.json │ │ └── users.json ├── spec_helper.rb └── support │ ├── api_helpers.rb │ ├── authentication_helper.rb │ ├── database_cleaner.rb │ ├── factory_girl.rb │ └── rack_helper.rb ├── test ├── controllers │ ├── .keep │ ├── microposts_controller_test.rb │ ├── relationships_controller_test.rb │ ├── sessions_controller_test.rb │ ├── static_pages_controller_test.rb │ └── users_controller_test.rb ├── fixtures │ ├── .keep │ ├── files │ │ └── .keep │ ├── microposts.yml │ ├── relationships.yml │ └── users.yml ├── helpers │ ├── .keep │ └── sessions_helper_test.rb ├── integration │ ├── .keep │ ├── following_test.rb │ ├── microposts_interface_test.rb │ ├── password_resets_test.rb │ ├── site_layout_test.rb │ ├── users_edit_test.rb │ ├── users_index_test.rb │ ├── users_login_test.rb │ ├── users_profile_test.rb │ └── users_signup_test.rb ├── mailers │ ├── .keep │ ├── previews │ │ └── user_mailer_preview.rb │ └── user_mailer_test.rb ├── models │ ├── .keep │ ├── micropost_test.rb │ ├── relationship_test.rb │ └── user_test.rb └── test_helper.rb ├── tmp └── .keep └── vendor └── assets ├── javascripts └── .keep └── stylesheets └── .keep /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-journal 13 | 14 | # Ignore all logfiles and tempfiles. 15 | /log/* 16 | /tmp/* 17 | !/log/.keep 18 | !/tmp/.keep 19 | 20 | # Ignore Byebug command history file. 21 | .byebug_history 22 | 23 | # Ignore Spring files. 24 | /spring/*.pid 25 | 26 | # Ignore uploaded test images. 27 | /public/uploads 28 | .envrc 29 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | ruby-2.3.3 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | ruby '2.3.3' 4 | 5 | gem 'rails', '5.0.0' 6 | gem 'bcrypt', '3.1.11' 7 | gem 'faker', '1.6.3' 8 | gem 'carrierwave', '0.11.2' 9 | gem 'mini_magick', '4.5.1' 10 | gem 'fog', '1.38.0' 11 | gem 'will_paginate', '3.1.0' 12 | gem 'bootstrap-will_paginate', '0.0.10' 13 | gem 'bootstrap-sass', '3.3.6' 14 | gem 'puma', '3.4.0' 15 | gem 'sass-rails', '5.0.5' 16 | gem 'uglifier', '3.0.0' 17 | gem 'coffee-rails', '4.2.1' 18 | gem 'jquery-rails', '4.1.1' 19 | gem 'turbolinks', '5.0.0' 20 | gem 'jbuilder', '2.4.1' 21 | 22 | group :development, :test do 23 | gem 'sqlite3', '1.3.11' 24 | gem 'byebug', '9.0.0', platform: :mri 25 | end 26 | 27 | group :development do 28 | gem 'web-console', '3.1.1' 29 | gem 'listen', '3.0.8' 30 | gem 'spring', '1.7.2' 31 | gem 'spring-watcher-listen', '2.0.0' 32 | gem 'annotate' 33 | end 34 | 35 | group :test do 36 | gem 'rails-controller-testing', '0.1.1' 37 | gem 'minitest-reporters', '1.1.9' 38 | gem 'guard', '2.13.0' 39 | gem 'guard-minitest', '2.4.4' 40 | end 41 | 42 | group :production do 43 | gem 'pg', '0.18.4' 44 | end 45 | 46 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 47 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 48 | 49 | #gems added for API development.. 50 | 51 | gem 'pundit' 52 | gem 'active_model_serializers', github: 'rails-api/active_model_serializers' 53 | gem 'active_hash_relation', '~> 1.4.0' 54 | gem 'rack-cors', :require => 'rack/cors' 55 | gem 'flexible_permissions' 56 | gem 'rack-attack' 57 | gem 'redis-activesupport' 58 | 59 | group :development, :test do 60 | gem 'rspec-rails', '~> 3.5' 61 | gem 'rspec-api_helpers', '1.0.3' 62 | gem 'database_cleaner' 63 | gem 'factory_girl_rails' 64 | gem 'rspec-json_schema', :git => "git://github.com/blazed/rspec-json_schema.git" 65 | gem 'pry-rails' 66 | end 67 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | # Defines the matching rules for Guard. 2 | guard :minitest, spring: true, all_on_start: false do 3 | watch(%r{^test/(.*)/?(.*)_test\.rb$}) 4 | watch('test/test_helper.rb') { 'test' } 5 | watch('config/routes.rb') { integration_tests } 6 | watch(%r{^app/models/(.*?)\.rb$}) do |matches| 7 | "test/models/#{matches[1]}_test.rb" 8 | end 9 | watch(%r{^app/controllers/(.*?)_controller\.rb$}) do |matches| 10 | resource_tests(matches[1]) 11 | end 12 | watch(%r{^app/views/([^/]*?)/.*\.html\.erb$}) do |matches| 13 | ["test/controllers/#{matches[1]}_controller_test.rb"] + 14 | integration_tests(matches[1]) 15 | end 16 | watch(%r{^app/helpers/(.*?)_helper\.rb$}) do |matches| 17 | integration_tests(matches[1]) 18 | end 19 | watch('app/views/layouts/application.html.erb') do 20 | 'test/integration/site_layout_test.rb' 21 | end 22 | watch('app/helpers/sessions_helper.rb') do 23 | integration_tests << 'test/helpers/sessions_helper_test.rb' 24 | end 25 | watch('app/controllers/sessions_controller.rb') do 26 | ['test/controllers/sessions_controller_test.rb', 27 | 'test/integration/users_login_test.rb'] 28 | end 29 | watch('app/controllers/account_activations_controller.rb') do 30 | 'test/integration/users_signup_test.rb' 31 | end 32 | watch(%r{app/views/users/*}) do 33 | resource_tests('users') + 34 | ['test/integration/microposts_interface_test.rb'] 35 | end 36 | end 37 | 38 | # Returns the integration tests corresponding to the given resource. 39 | def integration_tests(resource = :all) 40 | if resource == :all 41 | Dir["test/integration/*"] 42 | else 43 | Dir["test/integration/#{resource}_*.rb"] 44 | end 45 | end 46 | 47 | # Returns the controller tests corresponding to the given resource. 48 | def controller_test(resource) 49 | "test/controllers/#{resource}_controller_test.rb" 50 | end 51 | 52 | # Returns all tests for the given resource. 53 | def resource_tests(resource) 54 | integration_tests(resource) << controller_test(resource) 55 | end 56 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All source code in the [Ruby on Rails Tutorial](http://railstutorial.org/) is available jointly under the MIT License and the Beerware License. 2 | 3 | ``` 4 | The MIT License 5 | 6 | Copyright (c) 2016 Michael Hartl 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | ``` 26 | 27 | ``` 28 | THE BEERWARE LICENSE (Revision 42) 29 | 30 | Michael Hartl wrote this code. As long as you retain this notice you can do 31 | whatever you want with this stuff. If we meet some day, and you think this 32 | stuff is worth it, you can buy me a beer in return. 33 | ``` -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec puma -C config/puma.rb 2 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css 4 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/images/kitten.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/app/assets/images/kitten.jpg -------------------------------------------------------------------------------- /app/assets/images/rails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/app/assets/images/rails.png -------------------------------------------------------------------------------- /app/assets/javascripts/account_activations.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | //= require jquery 2 | //= require jquery_ujs 3 | //= require bootstrap 4 | //= require turbolinks 5 | //= require_tree . 6 | -------------------------------------------------------------------------------- /app/assets/javascripts/cable.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 | //= require action_cable 5 | //= require_self 6 | //= require_tree ./channels 7 | 8 | (function() { 9 | this.App || (this.App = {}); 10 | 11 | App.cable = ActionCable.createConsumer(); 12 | 13 | }).call(this); 14 | -------------------------------------------------------------------------------- /app/assets/javascripts/channels/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/app/assets/javascripts/channels/.keep -------------------------------------------------------------------------------- /app/assets/javascripts/microposts.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/password_resets.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/relationships.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/sessions.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/static_pages.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/users.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/account_activations.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the AccountActivations 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/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, vendor/assets/stylesheets, 6 | * or any plugin's 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/microposts.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Microposts 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/password_resets.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the PasswordResets 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/relationships.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Relationships 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/sessions.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Sessions 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/static_pages.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the StaticPages 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/users.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Users controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading. 2 | module ApplicationCable 3 | class Channel < ActionCable::Channel::Base 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading. 2 | module ApplicationCable 3 | class Connection < ActionCable::Connection::Base 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/controllers/account_activations_controller.rb: -------------------------------------------------------------------------------- 1 | class AccountActivationsController < ApplicationController 2 | 3 | def edit 4 | user = User.find_by(email: params[:email]) 5 | if user && !user.activated? && user.authenticated?(:activation, params[:id]) 6 | user.activate 7 | log_in user 8 | flash[:success] = "Account activated!" 9 | redirect_to user 10 | else 11 | flash[:danger] = "Invalid activation link" 12 | redirect_to root_url 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /app/controllers/api/v1/feeds_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::FeedsController < Api::V1::BaseController 2 | before_action :load_resource 3 | 4 | def show 5 | auth_microposts = policy_scope(@feed) 6 | 7 | render jsonapi: auth_microposts.collection, 8 | each_serializer: Api::V1::MicropostSerializer, 9 | fields: {micropost: auth_microposts.fields(params[:fields])}, 10 | meta: meta_attributes(auth_microposts.collection) 11 | end 12 | 13 | private 14 | def load_resource 15 | case params[:action].to_sym 16 | when :show 17 | @feed = paginate(User.find(params[:user_id]).feed) 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /app/controllers/api/v1/followers_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::FollowersController < Api::V1::BaseController 2 | before_action :load_resource 3 | 4 | def index 5 | auth_followers = policy_scope(@followers) 6 | 7 | render jsonapi: auth_followers.collection, 8 | each_serializer: Api::V1::UserSerializer, 9 | fields: {users: auth_followers.fields(params[:fields]).concat( 10 | [:microposts, :followers, :followings] 11 | )}, 12 | include: [], 13 | meta: meta_attributes(auth_followers.collection) 14 | end 15 | 16 | #remove a follower 17 | def destroy 18 | auth_follower = FollowerPolicy.new(current_user, @relationship).destroy? 19 | 20 | @relationship.destroy! 21 | 22 | render jsonapi: auth_follower.record, serializer: Api::V1::UserSerializer, 23 | fields: { users: auth_follower.fields(params[:fields])}, 24 | include: [] 25 | end 26 | 27 | private 28 | def load_resource 29 | case params[:action].to_sym 30 | when :index 31 | @followers = paginate( 32 | apply_filters(User.find(params[:user_id]).followers, params) 33 | ) 34 | when :destroy 35 | @relationship = Relationship.find_by!( 36 | followed_id: params[:user_id], 37 | follower_id: params[:id] 38 | ) 39 | end 40 | end 41 | end 42 | 43 | -------------------------------------------------------------------------------- /app/controllers/api/v1/followings_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::FollowingsController < Api::V1::BaseController 2 | before_action :load_resource 3 | 4 | def index 5 | auth_followings = policy_scope(@followings) 6 | 7 | render jsonapi: auth_followings.collection, 8 | each_serializer: Api::V1::UserSerializer, 9 | fields: {users: auth_followings.fields(params[:fields]).concat( 10 | [:microposts, :followers, :followings] 11 | )}, 12 | include: [], 13 | meta: meta_attributes(auth_followings.collection) 14 | end 15 | 16 | #follow a user 17 | def create 18 | auth_following = FollowingPolicy.new(current_user, @relationship).create? 19 | 20 | @relationship.save! 21 | 22 | render jsonapi: auth_following.record, serializer: Api::V1::UserSerializer 23 | end 24 | 25 | #unfollow a user 26 | def destroy 27 | auth_following = FollowingPolicy.new(current_user, @relationship).destroy? 28 | 29 | @relationship.destroy! 30 | 31 | render jsonapi: auth_following.record, serializer: Api::V1::UserSerializer 32 | end 33 | 34 | private 35 | def load_resource 36 | case params[:action].to_sym 37 | when :index 38 | @followings = paginate( 39 | apply_filters(User.find(params[:user_id]).following, params) 40 | ) 41 | when :create 42 | @relationship = Relationship.new( 43 | follower_id: params[:user_id], 44 | followed_id: params[:id] 45 | ) 46 | when :destroy 47 | @relationship = Relationship.find_by!( 48 | follower_id: params[:user_id], 49 | followed_id: params[:id] 50 | ) 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /app/controllers/api/v1/microposts_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::MicropostsController < Api::V1::BaseController 2 | before_action :load_resource 3 | 4 | def index 5 | auth_microposts = policy_scope(@microposts) 6 | 7 | render jsonapi: auth_microposts.collection, 8 | each_serializer: Api::V1::MicropostSerializer, 9 | fields: {micropost: auth_microposts.fields(params[:fields])}, 10 | meta: meta_attributes(auth_microposts.collection) 11 | end 12 | 13 | def show 14 | auth_micropost = authorize_with_permissions(@micropost) 15 | 16 | render jsonapi: auth_micropost.record, 17 | serializer: Api::V1::MicropostSerializer, 18 | fields: {micropost: auth_micropost.fields} 19 | end 20 | 21 | def create 22 | auth_micropost = authorize_with_permissions(@micropost) 23 | 24 | if @micropost.save 25 | render jsonapi: auth_micropost.record, 26 | serializer: Api::V1::MicropostSerializer, 27 | fields: {mircopost: auth_micropost.fields}, status: 201 28 | else 29 | invalid_resource!(@micropost.errors) 30 | end 31 | end 32 | 33 | def update 34 | auth_micropost = authorize_with_permissions(@micropost, :update?) 35 | 36 | if @micropost.update(update_params) 37 | render jsonapi: auth_micropost.record, 38 | serializer: Api::V1::MicropostSerializer, 39 | micropost: {user: auth_micropost.fields} 40 | else 41 | invalid_resource!(@micropost.errors) 42 | end 43 | end 44 | 45 | def destroy 46 | auth_micropost = authorize_with_permissions(@micropost, :destroy?) 47 | 48 | @micropost.destroy! 49 | 50 | render jsonapi: auth_micropost.record, 51 | serializer: Api::V1::MicropostSerializer, 52 | micropost: {user: auth_micropost.fields} 53 | end 54 | 55 | private 56 | def load_resource 57 | case params[:action].to_sym 58 | when :index 59 | @microposts = paginate(apply_filters(Micropost.all, params)) 60 | when :create 61 | @micropost = Micropost.new(create_params) 62 | when :show, :update, :destroy 63 | @micropost = Micropost.find(params[:id]) 64 | end 65 | end 66 | 67 | def create_params 68 | prms = normalized_params.permit( 69 | :content, :picture, :user_id 70 | ) 71 | if prms[:user_id].nil? && params[:action].to_sym == :create 72 | prms[:user_id] = current_user&.id 73 | end 74 | 75 | return prms 76 | end 77 | 78 | def update_params 79 | create_params 80 | end 81 | 82 | def normalized_params 83 | ActionController::Parameters.new( 84 | ActiveModelSerializers::Deserialization.jsonapi_parse(params) 85 | ) 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /app/controllers/api/v1/root_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::RootController < Api::V1::BaseController 2 | 3 | def options 4 | return head :ok 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/controllers/api/v1/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::SessionsController < Api::V1::BaseController 2 | before_action :load_resource 3 | skip_before_action :authenticate_user!, only: [:create] 4 | 5 | def create 6 | if @user 7 | render( 8 | jsonapi: @user, serializer: Api::V1::SessionSerializer, 9 | status: 201, include: [:user], scope: @user 10 | ) 11 | else 12 | return api_error(status: 401, errors: 'Wrong password or username') 13 | end 14 | end 15 | 16 | def show 17 | authorize(@user) 18 | 19 | render( 20 | jsonapi: @user, serializer: Api::V1::SessionSerializer, 21 | status: 200, include: [:user], fields: { 22 | user: UserPolicy::Regular.new(@user).fields 23 | } 24 | ) 25 | end 26 | 27 | private 28 | def create_params 29 | normalized_params.permit(:email, :password) 30 | end 31 | 32 | def load_resource 33 | case params[:action].to_sym 34 | when :create 35 | @user = User.find_by( 36 | email: create_params[:email] 37 | )&.authenticate(create_params[:password]) 38 | when :show 39 | @user = User.find(params[:id]) 40 | end 41 | end 42 | 43 | def normalized_params 44 | ActionController::Parameters.new( 45 | ActiveModelSerializers::Deserialization.jsonapi_parse(params) 46 | ) 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery with: :exception 3 | include SessionsHelper 4 | 5 | private 6 | 7 | # Confirms a logged-in user. 8 | def logged_in_user 9 | unless logged_in? 10 | store_location 11 | flash[:danger] = "Please log in." 12 | redirect_to login_url 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/concerns/custom_errors.rb: -------------------------------------------------------------------------------- 1 | module CustomErrors 2 | extend ActiveSupport::Concern 3 | 4 | class UnauthenticatedError < StandardError; end 5 | end 6 | -------------------------------------------------------------------------------- /app/controllers/microposts_controller.rb: -------------------------------------------------------------------------------- 1 | class MicropostsController < ApplicationController 2 | before_action :logged_in_user, only: [:create, :destroy] 3 | before_action :correct_user, only: :destroy 4 | 5 | def create 6 | @micropost = current_user.microposts.build(micropost_params) 7 | if @micropost.save 8 | flash[:success] = "Micropost created!" 9 | redirect_to root_url 10 | else 11 | @feed_items = [] 12 | render 'static_pages/home' 13 | end 14 | end 15 | 16 | def destroy 17 | @micropost.destroy 18 | flash[:success] = "Micropost deleted" 19 | redirect_to request.referrer || root_url 20 | end 21 | 22 | private 23 | 24 | def micropost_params 25 | params.require(:micropost).permit(:content, :picture) 26 | end 27 | def correct_user 28 | @micropost = current_user.microposts.find_by(id: params[:id]) 29 | redirect_to root_url if @micropost.nil? 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /app/controllers/password_resets_controller.rb: -------------------------------------------------------------------------------- 1 | class PasswordResetsController < ApplicationController 2 | before_action :get_user, only: [:edit, :update] 3 | before_action :valid_user, only: [:edit, :update] 4 | before_action :check_expiration, only: [:edit, :update] # Case (1) 5 | 6 | def new 7 | end 8 | 9 | def create 10 | @user = User.find_by(email: params[:password_reset][:email].downcase) 11 | if @user 12 | @user.create_reset_digest 13 | @user.send_password_reset_email 14 | flash[:info] = "Email sent with password reset instructions" 15 | redirect_to root_url 16 | else 17 | flash.now[:danger] = "Email address not found" 18 | render 'new' 19 | end 20 | end 21 | 22 | def edit 23 | end 24 | 25 | def update 26 | if params[:user][:password].empty? # Case (3) 27 | @user.errors.add(:password, "can't be empty") 28 | render 'edit' 29 | elsif @user.update_attributes(user_params) # Case (4) 30 | log_in @user 31 | flash[:success] = "Password has been reset." 32 | redirect_to @user 33 | else 34 | render 'edit' # Case (2) 35 | end 36 | end 37 | 38 | private 39 | 40 | def user_params 41 | params.require(:user).permit(:password, :password_confirmation) 42 | end 43 | 44 | # Before filters 45 | 46 | def get_user 47 | @user = User.find_by(email: params[:email]) 48 | end 49 | 50 | # Confirms a valid user. 51 | def valid_user 52 | unless (@user && @user.activated? && 53 | @user.authenticated?(:reset, params[:id])) 54 | redirect_to root_url 55 | end 56 | end 57 | 58 | # Checks expiration of reset token. 59 | def check_expiration 60 | if @user.password_reset_expired? 61 | flash[:danger] = "Password reset has expired." 62 | redirect_to new_password_reset_url 63 | end 64 | end 65 | end -------------------------------------------------------------------------------- /app/controllers/relationships_controller.rb: -------------------------------------------------------------------------------- 1 | class RelationshipsController < ApplicationController 2 | before_action :logged_in_user 3 | 4 | def create 5 | @user = User.find(params[:followed_id]) 6 | current_user.follow(@user) 7 | respond_to do |format| 8 | format.html { redirect_to @user } 9 | format.js 10 | end 11 | end 12 | 13 | def destroy 14 | @user = Relationship.find(params[:id]).followed 15 | current_user.unfollow(@user) 16 | respond_to do |format| 17 | format.html { redirect_to @user } 18 | format.js 19 | end 20 | end 21 | end -------------------------------------------------------------------------------- /app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | 3 | def new 4 | end 5 | 6 | def create 7 | user = User.find_by(email: params[:session][:email].downcase) 8 | if user && user.authenticate(params[:session][:password]) 9 | if user.activated? 10 | log_in user 11 | params[:session][:remember_me] == '1' ? remember(user) : forget(user) 12 | redirect_back_or user 13 | else 14 | message = "Account not activated. " 15 | message += "Check your email for the activation link." 16 | flash[:warning] = message 17 | redirect_to root_url 18 | end 19 | else 20 | flash.now[:danger] = 'Invalid email/password combination' # Not quite right! 21 | render 'new' 22 | end 23 | end 24 | 25 | def destroy 26 | log_out if logged_in? 27 | redirect_to root_url 28 | end 29 | end -------------------------------------------------------------------------------- /app/controllers/static_pages_controller.rb: -------------------------------------------------------------------------------- 1 | class StaticPagesController < ApplicationController 2 | 3 | def home 4 | if logged_in? 5 | @micropost = current_user.microposts.build 6 | @feed_items = current_user.feed.paginate(page: params[:page]) 7 | end 8 | end 9 | 10 | def help 11 | end 12 | 13 | def about 14 | end 15 | 16 | def contact 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | before_action :logged_in_user, only: [:index, :edit, :update, :destroy, 3 | :following, :followers] 4 | before_action :correct_user, only: [:edit, :update] 5 | before_action :admin_user, only: :destroy 6 | 7 | def index 8 | @users = User.paginate(page: params[:page]) 9 | end 10 | 11 | def show 12 | @user = User.find(params[:id]) 13 | @microposts = @user.microposts.paginate(page: params[:page]) 14 | end 15 | 16 | def new 17 | @user = User.new 18 | end 19 | 20 | def create 21 | @user = User.new(user_params) 22 | if @user.save 23 | @user.send_activation_email 24 | flash[:info] = "Please check your email to activate your account." 25 | redirect_to root_url 26 | else 27 | render 'new' 28 | end 29 | end 30 | 31 | def edit 32 | @user = User.find(params[:id]) 33 | end 34 | 35 | def update 36 | @user = User.find(params[:id]) 37 | if @user.update_attributes(user_params) 38 | flash[:success] = "Profile updated" 39 | redirect_to @user 40 | else 41 | render 'edit' 42 | end 43 | end 44 | 45 | def destroy 46 | User.find(params[:id]).destroy 47 | flash[:success] = "User deleted" 48 | redirect_to users_url 49 | end 50 | 51 | def following 52 | @title = "Following" 53 | @user = User.find(params[:id]) 54 | @users = @user.following.paginate(page: params[:page]) 55 | render 'show_follow' 56 | end 57 | 58 | def followers 59 | @title = "Followers" 60 | @user = User.find(params[:id]) 61 | @users = @user.followers.paginate(page: params[:page]) 62 | render 'show_follow' 63 | end 64 | 65 | private 66 | 67 | def user_params 68 | params.require(:user).permit(:name, :email, :password, 69 | :password_confirmation) 70 | end 71 | 72 | # Before filters 73 | 74 | # Confirms the correct user. 75 | def correct_user 76 | @user = User.find(params[:id]) 77 | redirect_to(root_url) unless current_user?(@user) 78 | end 79 | 80 | # Confirms an admin user. 81 | def admin_user 82 | redirect_to(root_url) unless current_user.admin? 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /app/helpers/account_activations_helper.rb: -------------------------------------------------------------------------------- 1 | module AccountActivationsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | 3 | # Returns the full title on a per-page basis. 4 | def full_title(page_title = '') 5 | base_title = "Ruby on Rails Tutorial Sample App" 6 | if page_title.empty? 7 | base_title 8 | else 9 | page_title + " | " + base_title 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/helpers/microposts_helper.rb: -------------------------------------------------------------------------------- 1 | module MicropostsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/password_resets_helper.rb: -------------------------------------------------------------------------------- 1 | module PasswordResetsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/relationships_helper.rb: -------------------------------------------------------------------------------- 1 | module RelationshipsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/sessions_helper.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | 3 | # Logs in the given user. 4 | def log_in(user) 5 | session[:user_id] = user.id 6 | end 7 | 8 | # Remembers a user in a persistent session. 9 | def remember(user) 10 | user.remember 11 | cookies.permanent.signed[:user_id] = user.id 12 | cookies.permanent[:remember_token] = user.remember_token 13 | end 14 | 15 | # Returns true if the given user is the current user. 16 | def current_user?(user) 17 | user == current_user 18 | end 19 | 20 | # Returns the user corresponding to the remember token cookie. 21 | def current_user 22 | if (user_id = session[:user_id]) 23 | @current_user ||= User.find_by(id: user_id) 24 | elsif (user_id = cookies.signed[:user_id]) 25 | user = User.find_by(id: user_id) 26 | if user && user.authenticated?(:remember, cookies[:remember_token]) 27 | log_in user 28 | @current_user = user 29 | end 30 | end 31 | end 32 | 33 | # Returns true if the user is logged in, false otherwise. 34 | def logged_in? 35 | !current_user.nil? 36 | end 37 | 38 | # Forgets a persistent session. 39 | def forget(user) 40 | user.forget 41 | cookies.delete(:user_id) 42 | cookies.delete(:remember_token) 43 | end 44 | 45 | # Logs out the current user. 46 | def log_out 47 | forget(current_user) 48 | session.delete(:user_id) 49 | @current_user = nil 50 | end 51 | 52 | # Redirects to stored location (or to the default). 53 | def redirect_back_or(default) 54 | redirect_to(session[:forwarding_url] || default) 55 | session.delete(:forwarding_url) 56 | end 57 | 58 | # Stores the URL trying to be accessed. 59 | def store_location 60 | session[:forwarding_url] = request.original_url if request.get? 61 | end 62 | end -------------------------------------------------------------------------------- /app/helpers/static_pages_helper.rb: -------------------------------------------------------------------------------- 1 | module StaticPagesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/users_helper.rb: -------------------------------------------------------------------------------- 1 | module UsersHelper 2 | 3 | # Returns the Gravatar for the given user. 4 | def gravatar_for(user, options = { size: 80 }) 5 | gravatar_id = Digest::MD5::hexdigest(user.email.downcase) 6 | size = options[:size] 7 | gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}" 8 | image_tag(gravatar_url, alt: user.name, class: "gravatar") 9 | end 10 | end -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: "noreply@example.com" 3 | layout 'mailer' 4 | end -------------------------------------------------------------------------------- /app/mailers/user_mailer.rb: -------------------------------------------------------------------------------- 1 | class UserMailer < ApplicationMailer 2 | helper_method :ember_activation_url 3 | 4 | def account_activation(user) 5 | @user = user 6 | mail to: user.email, subject: "Account activation" 7 | end 8 | 9 | def ember_account_activation(user) 10 | @user = user 11 | mail to: user.email, subject: "Account activation" 12 | end 13 | 14 | def password_reset(user) 15 | @user = user 16 | mail to: user.email, subject: "Password reset" 17 | end 18 | 19 | def ember_activation_url(token, email) 20 | "#{Rails.application.secrets.ember_activation_url}?token=#{token}&email=#{email}".html_safe 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/micropost.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: microposts 4 | # 5 | # id :integer not null, primary key 6 | # content :text 7 | # picture :string 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # user_id :integer 11 | # 12 | # Indexes 13 | # 14 | # index_microposts_on_user_id (user_id) 15 | # 16 | 17 | class Micropost < ApplicationRecord 18 | belongs_to :user, counter_cache: true 19 | 20 | default_scope -> { order(created_at: :desc) } 21 | mount_uploader :picture, PictureUploader 22 | validates :user_id, presence: true 23 | validates :content, presence: true, length: { maximum: 140 } 24 | validate :picture_size 25 | 26 | private 27 | 28 | # Validates the size of an uploaded picture. 29 | def picture_size 30 | if picture.size > 5.megabytes 31 | errors.add(:picture, "should be less than 5MB") 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /app/models/relationship.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: relationships 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime not null 7 | # updated_at :datetime not null 8 | # followed_id :integer 9 | # follower_id :integer 10 | # 11 | # Indexes 12 | # 13 | # index_relationships_on_followed_id (followed_id) 14 | # index_relationships_on_follower_id (follower_id) 15 | # index_relationships_on_follower_id_and_followed_id (follower_id,followed_id) UNIQUE 16 | # 17 | 18 | class Relationship < ApplicationRecord 19 | belongs_to :follower, class_name: "User", counter_cache: :followings_count 20 | belongs_to :followed, class_name: "User", counter_cache: :followers_count 21 | 22 | validates :follower_id, presence: true 23 | validates :followed_id, presence: true 24 | end 25 | -------------------------------------------------------------------------------- /app/policies/application_policy.rb: -------------------------------------------------------------------------------- 1 | class ApplicationPolicy 2 | attr_reader :user, :record 3 | 4 | def initialize(user, record) 5 | @user = user 6 | @record = record 7 | end 8 | 9 | def index? 10 | true 11 | end 12 | 13 | def show? 14 | scope.where(id: record.id).exists? 15 | end 16 | 17 | def create? 18 | false 19 | end 20 | 21 | def new? 22 | create? 23 | end 24 | 25 | def update? 26 | false 27 | end 28 | 29 | def edit? 30 | update? 31 | end 32 | 33 | def destroy? 34 | false 35 | end 36 | 37 | def scope 38 | Pundit.policy_scope!(user, record.class) 39 | end 40 | 41 | class Scope 42 | attr_reader :user, :scope 43 | 44 | def initialize(user, scope) 45 | @user = user 46 | @scope = scope 47 | end 48 | 49 | def resolve 50 | scope 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /app/policies/follower_policy.rb: -------------------------------------------------------------------------------- 1 | class FollowerPolicy < ApplicationPolicy 2 | def destroy? 3 | raise Pundit::NotAuthorizedError unless record.followed_id == user.id 4 | return Regular.new(record.follower) 5 | end 6 | 7 | class Scope < Scope 8 | def resolve 9 | return Regular.new(scope, User) 10 | end 11 | end 12 | 13 | class Regular < FlexiblePermissions::Base 14 | end 15 | end 16 | 17 | -------------------------------------------------------------------------------- /app/policies/following_policy.rb: -------------------------------------------------------------------------------- 1 | class FollowingPolicy < ApplicationPolicy 2 | def create? 3 | raise Pundit::NotAuthorizedError unless record.follower_id == user.id 4 | 5 | return Regular.new(record.followed) 6 | end 7 | 8 | def destroy? 9 | raise Pundit::NotAuthorizedError unless record.follower_id == user.id 10 | 11 | return Regular.new(record.followed) 12 | end 13 | 14 | class Scope < Scope 15 | def resolve 16 | return Regular.new(scope, User) 17 | end 18 | end 19 | 20 | class Regular < UserPolicy::Regular 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/policies/micropost_policy.rb: -------------------------------------------------------------------------------- 1 | class MicropostPolicy < ApplicationPolicy 2 | def create? 3 | return Regular.new(record) 4 | end 5 | 6 | def show? 7 | return Regular.new(record) 8 | end 9 | 10 | def update? 11 | return Regular.new(record) 12 | end 13 | 14 | def destroy? 15 | return Regular.new(record) 16 | end 17 | 18 | class Scope < Scope 19 | def resolve 20 | return Regular.new(scope, Micropost) 21 | end 22 | end 23 | 24 | class Admin < FlexiblePermissions::Base 25 | class Fields < self::Fields 26 | def permitted 27 | super + [ 28 | :links 29 | ] - [ 30 | :picture 31 | ] 32 | end 33 | end 34 | 35 | class Includes < self::Includes 36 | def default 37 | [] 38 | end 39 | end 40 | end 41 | 42 | class Regular < Admin 43 | class Fields < self::Fields 44 | def permitted 45 | super - [ 46 | :update_at 47 | ] 48 | end 49 | end 50 | end 51 | end 52 | 53 | -------------------------------------------------------------------------------- /app/policies/user_policy.rb: -------------------------------------------------------------------------------- 1 | class UserPolicy < ApplicationPolicy 2 | def create? 3 | return Regular.new(record) 4 | end 5 | 6 | def show? 7 | return Guest.new(record) unless user 8 | return Admin.new(record) if user.admin? 9 | return Regular.new(record) 10 | end 11 | 12 | def update? 13 | raise Pundit::NotAuthorizedError unless user 14 | return Admin.new(record) if user.admin? 15 | return Regular.new(record) 16 | end 17 | 18 | def destroy? 19 | raise Pundit::NotAuthorizedError unless user 20 | return Admin.new(record) if user.admin? 21 | return Regular.new(record) 22 | end 23 | 24 | def activate? 25 | raise Pundit::NotAuthorizedError unless record.is_a? User 26 | return Admin.new(record) if record.admin? 27 | return Regular.new(record) 28 | end 29 | 30 | class Scope < Scope 31 | def resolve 32 | return Guest.new(scope, User) unless user 33 | return Admin.new(scope, User) if user.admin? 34 | return Regular.new(scope, User) 35 | end 36 | end 37 | 38 | class Admin < FlexiblePermissions::Base 39 | class Fields < self::Fields 40 | def permitted 41 | super + [ 42 | :links, :following_state, :follower_state 43 | ] 44 | end 45 | end 46 | 47 | class Includes < self::Includes 48 | def permitted 49 | super + [:feed] 50 | end 51 | 52 | def transformations 53 | {following: :followings} 54 | end 55 | end 56 | end 57 | 58 | class Regular < Admin 59 | class Fields < self::Fields 60 | def permitted 61 | super - [ 62 | :activated, :activated_at, :activation_digest, :admin, 63 | :password_digest, :remember_digest, :reset_digest, :reset_sent_at, 64 | :token, :updated_at, 65 | ] 66 | end 67 | end 68 | end 69 | 70 | class Guest < Regular 71 | class Fields < self::Fields 72 | def permitted 73 | super - [ 74 | :following_state, :follower_state, :email, :microposts_count, 75 | :followers_count, :followings_count 76 | ] 77 | end 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /app/serializers/api/v1/base_serializer.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::BaseSerializer < ActiveModel::Serializer 2 | include Rails.application.routes.url_helpers 3 | 4 | def created_at 5 | object.created_at.to_datetime.in_time_zone('UTC').iso8601 if object.created_at 6 | end 7 | 8 | def updated_at 9 | object.updated_at.to_datetime.in_time_zone('UTC').iso8601 if object.updated_at 10 | end 11 | 12 | def reset_sent_at 13 | object.published_at.to_datetime.in_time_zone('UTC').iso8601 if object.reset_sent_at 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/serializers/api/v1/error_serializer.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::ErrorSerializer 2 | UNKNOWN_ERROR = 'Something went wrong, no more info is available unforunately!'.freeze 3 | DEFAULT_POINTER = 'data'.freeze 4 | 5 | def initialize(status, errors) 6 | @status = status 7 | if errors.is_a? ActiveModel::Errors 8 | @errors = parse_am_errors(errors) 9 | else #it's an array or a string 10 | @errors = [errors].flatten 11 | end 12 | end 13 | 14 | def as_json 15 | { 16 | errors: errors 17 | } 18 | end 19 | 20 | def to_json 21 | as_json.to_json 22 | end 23 | 24 | private 25 | 26 | def parse_am_errors(errors) 27 | error_messages = errors.full_messages 28 | 29 | errors.map.with_index do |(k, v), i| 30 | ErrorDecorator.new(k, v, error_messages[i]) 31 | end 32 | end 33 | 34 | def errors 35 | @errors.map do |error| 36 | { 37 | status: @status, 38 | title: normalize_title(error), 39 | detail: normalize_error(error), 40 | source: { 41 | pointer: error_pointer(error) 42 | } 43 | } 44 | end 45 | end 46 | 47 | def normalize_title(error) 48 | error.try(:title) || error.try(:to_s) || UNKNOWN_ERROR 49 | end 50 | 51 | def normalize_error(error) 52 | error.try(:details) || error.try(:to_s) || UNKNOWN_ERROR 53 | end 54 | 55 | def error_pointer(error) 56 | if error.respond_to?(:pointer) 57 | return error.pointer 58 | else 59 | return DEFAULT_POINTER 60 | end 61 | end 62 | 63 | class ErrorDecorator 64 | def initialize(key, value, message) 65 | @key, @value, @message = key, value, message 66 | end 67 | 68 | def title 69 | @value 70 | end 71 | 72 | def details 73 | @value 74 | end 75 | 76 | def to_s 77 | @message 78 | end 79 | 80 | def pointer 81 | "data/attributes/#{@key.to_s}" 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /app/serializers/api/v1/micropost_serializer.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::MicropostSerializer < Api::V1::BaseSerializer 2 | attributes(*Micropost.attribute_names.map(&:to_sym)) 3 | 4 | belongs_to :user, serializer: Api::V1::UserSerializer do 5 | include_data(false) 6 | link(:related) {api_v1_user_path(object.user_id)} 7 | end 8 | 9 | def picture 10 | object.picture.url 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/serializers/api/v1/session_serializer.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::SessionSerializer < Api::V1::BaseSerializer 2 | type :session 3 | 4 | attributes :email, :token, :user_id 5 | 6 | has_one :user, serializer: Api::V1::UserSerializer do 7 | link(:self) {api_v1_user_path(object.id)} 8 | link(:related) {api_v1_user_path(object.id)} 9 | 10 | object 11 | end 12 | 13 | def user 14 | object 15 | end 16 | 17 | def user_id 18 | object.id 19 | end 20 | 21 | def token 22 | object.token 23 | end 24 | 25 | def email 26 | object.email 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /app/serializers/api/v1/user_serializer.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::UserSerializer < Api::V1::BaseSerializer 2 | attributes(*User.attribute_names.map(&:to_sym)) 3 | 4 | attribute :following_state 5 | attribute :follower_state 6 | 7 | def following_state 8 | Relationship.where( 9 | follower_id: current_user.id, 10 | followed_id: object.id 11 | ).exists? 12 | end 13 | 14 | def follower_state 15 | Relationship.where( 16 | follower_id: object.id, 17 | followed_id: current_user.id 18 | ).exists? 19 | end 20 | 21 | has_one :feed, serializer: Api::V1::MicropostSerializer do 22 | include_data(false) 23 | link(:related) {api_v1_user_feed_path(user_id: object.id)} 24 | end 25 | 26 | has_many :microposts, serializer: Api::V1::MicropostSerializer do 27 | include_data(false) 28 | link(:related) {api_v1_microposts_path(user_id: object.id)} 29 | end 30 | 31 | has_many :followers, serializer: Api::V1::UserSerializer do 32 | include_data(false) 33 | link(:related) {api_v1_user_followers_path(user_id: object.id)} 34 | end 35 | 36 | has_many :followings, key: :followings, serializer: Api::V1::UserSerializer do 37 | include_data(false) 38 | link(:related) {api_v1_user_followings_path(user_id: object.id)} 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /app/uploaders/picture_uploader.rb: -------------------------------------------------------------------------------- 1 | 2 | class PictureUploader < CarrierWave::Uploader::Base 3 | include CarrierWave::MiniMagick 4 | process resize_to_limit: [400, 400] 5 | 6 | if Rails.env.production? 7 | storage :fog 8 | else 9 | storage :file 10 | end 11 | 12 | # Override the directory where uploaded files will be stored. 13 | # This is a sensible default for uploaders that are meant to be mounted: 14 | def store_dir 15 | "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" 16 | end 17 | 18 | # Add a white list of extensions which are allowed to be uploaded. 19 | def extension_white_list 20 | %w(jpg jpeg gif png) 21 | end 22 | end -------------------------------------------------------------------------------- /app/views/layouts/_footer.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/layouts/_header.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/layouts/_shim.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= full_title(yield(:title)) %> 5 | <%= csrf_meta_tags %> 6 | <%= stylesheet_link_tag 'application', media: 'all', 7 | 'data-turbolinks-track': 'reload' %> 8 | <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> 9 | <%= render 'layouts/shim' %> 10 | 11 | 12 | <%= render 'layouts/header' %> 13 |
14 | <% flash.each do |message_type, message| %> 15 |
<%= message %>
16 | <% end %> 17 | <%= yield %> 18 | <%= render 'layouts/footer' %> 19 | <%= debug(params) if Rails.env.development? %> 20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/views/microposts/_micropost.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %> 3 | <%= link_to micropost.user.name, micropost.user %> 4 | 5 | <%= micropost.content %> 6 | <%= image_tag micropost.picture.url if micropost.picture? %> 7 | 8 | 9 | Posted <%= time_ago_in_words(micropost.created_at) %> ago. 10 | <% if current_user?(micropost.user) %> 11 | <%= link_to "delete", micropost, method: :delete, 12 | data: { confirm: "You sure?" } %> 13 | <% end %> 14 | 15 |
  • -------------------------------------------------------------------------------- /app/views/password_resets/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'Reset password') %> 2 |

    Password reset

    3 | 4 |
    5 |
    6 | <%= form_for(@user, url: password_reset_path(params[:id])) do |f| %> 7 | <%= render 'shared/error_messages', object: f.object %> 8 | 9 | <%= hidden_field_tag :email, @user.email %> 10 | 11 | <%= f.label :password %> 12 | <%= f.password_field :password, class: 'form-control' %> 13 | 14 | <%= f.label :password_confirmation, "Confirmation" %> 15 | <%= f.password_field :password_confirmation, class: 'form-control' %> 16 | 17 | <%= f.submit "Update password", class: "btn btn-primary" %> 18 | <% end %> 19 |
    20 |
    -------------------------------------------------------------------------------- /app/views/password_resets/new.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "Forgot password") %> 2 |

    Forgot password

    3 | 4 |
    5 |
    6 | <%= form_for(:password_reset, url: password_resets_path) do |f| %> 7 | <%= f.label :email %> 8 | <%= f.email_field :email, class: 'form-control' %> 9 | 10 | <%= f.submit "Submit", class: "btn btn-primary" %> 11 | <% end %> 12 |
    13 |
    -------------------------------------------------------------------------------- /app/views/relationships/create.js.erb: -------------------------------------------------------------------------------- 1 | $("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>"); 2 | $("#followers").html('<%= @user.followers.count %>'); -------------------------------------------------------------------------------- /app/views/relationships/destroy.js.erb: -------------------------------------------------------------------------------- 1 | $("#follow_form").html("<%= escape_javascript(render('users/follow')) %>"); 2 | $("#followers").html('<%= @user.followers.count %>'); -------------------------------------------------------------------------------- /app/views/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "Log in") %> 2 |

    Log in

    3 | 4 |
    5 |
    6 | <%= form_for(:session, url: login_path) do |f| %> 7 | 8 | <%= f.label :email %> 9 | <%= f.email_field :email, class: 'form-control' %> 10 | 11 | <%= f.label :password %> 12 | <%= link_to "(forgot password)", new_password_reset_path %> 13 | <%= f.password_field :password, class: 'form-control' %> 14 | 15 | <%= f.label :remember_me, class: "checkbox inline" do %> 16 | <%= f.check_box :remember_me %> 17 | Remember me on this computer 18 | <% end %> 19 | 20 | <%= f.submit "Log in", class: "btn btn-primary" %> 21 | <% end %> 22 | 23 |

    New user? <%= link_to "Sign up now!", signup_path %>

    24 |
    25 |
    -------------------------------------------------------------------------------- /app/views/shared/_error_messages.html.erb: -------------------------------------------------------------------------------- 1 | <% if object.errors.any? %> 2 |
    3 |
    4 | The form contains <%= pluralize(object.errors.count, "error") %>. 5 |
    6 | 11 |
    12 | <% end %> -------------------------------------------------------------------------------- /app/views/shared/_feed.html.erb: -------------------------------------------------------------------------------- 1 | <% if @feed_items.any? %> 2 |
      3 | <%= render @feed_items %> 4 |
    5 | <%= will_paginate @feed_items %> 6 | <% end %> -------------------------------------------------------------------------------- /app/views/shared/_micropost_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for(@micropost, html: { multipart: true }) do |f| %> 2 | <%= render 'shared/error_messages', object: f.object %> 3 |
    4 | <%= f.text_area :content, placeholder: "Compose new micropost..." %> 5 |
    6 | <%= f.submit "Post", class: "btn btn-primary" %> 7 | 8 | <%= f.file_field :picture, accept: 'image/jpeg,image/gif,image/png' %> 9 | 10 | <% end %> 11 | 12 | -------------------------------------------------------------------------------- /app/views/shared/_stats.html.erb: -------------------------------------------------------------------------------- 1 | <% @user ||= current_user %> 2 |
    3 | 4 | 5 | <%= @user.following.count %> 6 | 7 | following 8 | 9 | 10 | 11 | <%= @user.followers.count %> 12 | 13 | followers 14 | 15 |
    -------------------------------------------------------------------------------- /app/views/shared/_user_info.html.erb: -------------------------------------------------------------------------------- 1 | <%= link_to gravatar_for(current_user, size: 50), current_user %> 2 |

    <%= current_user.name %>

    3 | <%= link_to "view my profile", current_user %> 4 | <%= pluralize(current_user.microposts.count, "micropost") %> -------------------------------------------------------------------------------- /app/views/static_pages/about.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "About") %> 2 |

    About

    3 |

    4 | The Ruby on Rails 5 | Tutorial is a 6 | book and 7 | screencast series 8 | to teach web development with 9 | Ruby on Rails. 10 | This is the sample application for the tutorial. 11 |

    -------------------------------------------------------------------------------- /app/views/static_pages/contact.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'Contact') %> 2 |

    Contact

    3 |

    4 | Contact the Ruby on Rails Tutorial about the sample app at the 5 | contact page. 6 |

    -------------------------------------------------------------------------------- /app/views/static_pages/help.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "Help") %> 2 |

    Help

    3 |

    4 | Get help on the Ruby on Rails Tutorial at the 5 | Rails Tutorial help section. 6 | To get help on this sample app, see the 7 | Ruby on Rails Tutorial 8 | book. 9 |

    -------------------------------------------------------------------------------- /app/views/static_pages/home.html.erb: -------------------------------------------------------------------------------- 1 | <% if logged_in? %> 2 |
    3 | 14 |
    15 |

    Micropost Feed

    16 | <%= render 'shared/feed' %> 17 |
    18 |
    19 | <% else %> 20 |
    21 |

    Welcome to the Sample App

    22 | 23 |

    24 | This is the home page for the 25 | Ruby on Rails Tutorial 26 | sample application. 27 |

    28 | 29 | <%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %> 30 |
    31 | 32 | <%= link_to image_tag("rails.png", alt: "Rails logo"), 33 | 'http://rubyonrails.org/' %> 34 | <% end %> -------------------------------------------------------------------------------- /app/views/user_mailer/account_activation.html.erb: -------------------------------------------------------------------------------- 1 |

    Sample App

    2 | 3 |

    Hi <%= @user.name %>,

    4 | 5 |

    6 | Welcome to the Sample App! Click on the link below to activate your account: 7 |

    8 | 9 | <%= link_to "Activate", edit_account_activation_url(@user.activation_token, 10 | email: @user.email) %> -------------------------------------------------------------------------------- /app/views/user_mailer/account_activation.text.erb: -------------------------------------------------------------------------------- 1 | Hi <%= @user.name %>, 2 | 3 | Welcome to the Sample App! Click on the link below to activate your account: 4 | 5 | <%= edit_account_activation_url(@user.activation_token, email: @user.email) %> -------------------------------------------------------------------------------- /app/views/user_mailer/ember_account_activation.html.erb: -------------------------------------------------------------------------------- 1 |

    Sample App

    2 | 3 |

    Hi <%= @user.name %>,

    4 | 5 |

    6 | Welcome to the Sample App! Click on the link below to activate your account: 7 |

    8 | 9 | <%= link_to "Activate", ember_activation_url(@user.activation_token, @user.email) %> 10 | -------------------------------------------------------------------------------- /app/views/user_mailer/ember_account_activation.text.erb: -------------------------------------------------------------------------------- 1 | Hi <%= @user.name %>, 2 | 3 | Welcome to the Sample App! Click on the link below to activate your account: 4 | 5 | <%= link_to "Activate", ember_activation_url(@user.activation_token, @user.email) %> 6 | -------------------------------------------------------------------------------- /app/views/user_mailer/password_reset.html.erb: -------------------------------------------------------------------------------- 1 |

    Password reset

    2 | 3 |

    To reset your password click the link below:

    4 | 5 | <%= link_to "Reset password", edit_password_reset_url(@user.reset_token, 6 | email: @user.email) %> 7 | 8 |

    This link will expire in two hours.

    9 | 10 |

    11 | If you did not request your password to be reset, please ignore this email and 12 | your password will stay as it is. 13 |

    -------------------------------------------------------------------------------- /app/views/user_mailer/password_reset.text.erb: -------------------------------------------------------------------------------- 1 | To reset your password click the link below: 2 | 3 | <%= edit_password_reset_url(@user.reset_token, email: @user.email) %> 4 | 5 | This link will expire in two hours. 6 | 7 | If you did not request your password to be reset, please ignore this email and 8 | your password will stay as it is. -------------------------------------------------------------------------------- /app/views/users/_follow.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for(current_user.active_relationships.build, remote: true) do |f| %> 2 |
    <%= hidden_field_tag :followed_id, @user.id %>
    3 | <%= f.submit "Follow", class: "btn btn-primary" %> 4 | <% end %> -------------------------------------------------------------------------------- /app/views/users/_follow_form.html.erb: -------------------------------------------------------------------------------- 1 | <% unless current_user?(@user) %> 2 |
    3 | <% if current_user.following?(@user) %> 4 | <%= render 'unfollow' %> 5 | <% else %> 6 | <%= render 'follow' %> 7 | <% end %> 8 |
    9 | <% end %> -------------------------------------------------------------------------------- /app/views/users/_unfollow.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for(current_user.active_relationships.find_by(followed_id: @user.id), 2 | html: { method: :delete }, 3 | remote: true) do |f| %> 4 | <%= f.submit "Unfollow", class: "btn" %> 5 | <% end %> -------------------------------------------------------------------------------- /app/views/users/_user.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | <%= gravatar_for user, size: 50 %> 3 | <%= link_to user.name, user %> 4 | <% if current_user.admin? && !current_user?(user) %> 5 | | <%= link_to "delete", user, method: :delete, 6 | data: { confirm: "You sure?" } %> 7 | <% end %> 8 |
  • -------------------------------------------------------------------------------- /app/views/users/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "Edit user") %> 2 |

    Update your profile

    3 | 4 |
    5 |
    6 | <%= form_for(@user) do |f| %> 7 | <%= render 'shared/error_messages', object: f.object %> 8 | 9 | <%= f.label :name %> 10 | <%= f.text_field :name, class: 'form-control' %> 11 | 12 | <%= f.label :email %> 13 | <%= f.email_field :email, class: 'form-control' %> 14 | 15 | <%= f.label :password %> 16 | <%= f.password_field :password, class: 'form-control' %> 17 | 18 | <%= f.label :password_confirmation, "Confirmation" %> 19 | <%= f.password_field :password_confirmation, class: 'form-control' %> 20 | 21 | <%= f.submit "Save changes", class: "btn btn-primary" %> 22 | <% end %> 23 | 24 |
    25 | <%= gravatar_for @user %> 26 | change 27 |
    28 |
    29 |
    -------------------------------------------------------------------------------- /app/views/users/index.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'All users') %> 2 |

    All users

    3 | 4 | <%= will_paginate %> 5 | 6 | 9 | 10 | <%= will_paginate %> 11 | -------------------------------------------------------------------------------- /app/views/users/new.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'Sign up') %> 2 |

    Sign up

    3 | 4 |
    5 |
    6 | <%= form_for(@user) do |f| %> 7 | <%= render 'shared/error_messages', object: f.object %> 8 | <%= f.label :name %> 9 | <%= f.text_field :name, class: 'form-control' %> 10 | 11 | <%= f.label :email %> 12 | <%= f.email_field :email, class: 'form-control' %> 13 | 14 | <%= f.label :password %> 15 | <%= f.password_field :password, class: 'form-control' %> 16 | 17 | <%= f.label :password_confirmation, "Confirmation" %> 18 | <%= f.password_field :password_confirmation, class: 'form-control' %> 19 | 20 | <%= f.submit "Create my account", class: "btn btn-primary" %> 21 | <% end %> 22 |
    23 |
    -------------------------------------------------------------------------------- /app/views/users/show.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, @user.name) %> 2 |
    3 | 14 |
    15 | <%= render 'follow_form' if logged_in? %> 16 | <% if @user.microposts.any? %> 17 |

    Microposts (<%= @user.microposts.count %>)

    18 |
      19 | <%= render @microposts %> 20 |
    21 | <%= will_paginate @microposts %> 22 | <% end %> 23 |
    24 |
    -------------------------------------------------------------------------------- /app/views/users/show_follow.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, @title) %> 2 |
    3 | 21 |
    22 |

    <%= @title %>

    23 | <% if @users.any? %> 24 | 27 | <%= will_paginate %> 28 | <% end %> 29 |
    30 |
    -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /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 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a starting point to setup your application. 15 | # Add necessary setup steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | # puts "\n== Copying sample files ==" 22 | # unless File.exist?('config/database.yml') 23 | # cp 'config/database.yml.sample', 'config/database.yml' 24 | # end 25 | 26 | puts "\n== Preparing database ==" 27 | system! 'bin/rails db:setup' 28 | 29 | puts "\n== Removing old logs and tempfiles ==" 30 | system! 'bin/rails log:clear tmp:clear' 31 | 32 | puts "\n== Restarting application server ==" 33 | system! 'bin/rails restart' 34 | end 35 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module SampleApp 10 | class Application < Rails::Application 11 | # Settings in config/environments/* take precedence over those specified here. 12 | # Application configuration should go into files in config/initializers 13 | # -- all .rb files in that directory are automatically loaded. 14 | 15 | # Include the authenticity token in remote forms. 16 | config.action_view.embed_authenticity_token_in_remote_forms = true 17 | 18 | config.middleware.insert_before 0, Rack::Cors do 19 | allow do 20 | origins '*' 21 | resource '*', headers: :any, methods: [ 22 | :get, :post, :put, :patch, :delete, :options, :head 23 | ] 24 | end 25 | end 26 | 27 | config.middleware.use Rack::Attack 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | # Action Cable uses Redis by default to administer connections, channels, and sending/receiving messages over the WebSocket. 2 | production: 3 | adapter: redis 4 | url: redis://localhost:6379/1 5 | 6 | development: 7 | adapter: async 8 | 9 | test: 10 | adapter: async 11 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 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: postgresql 9 | pool: 5 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: rails5_tutorial_api_development 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: rails5_tutorial_api_test 22 | 23 | production: 24 | <<: *default 25 | database: rails5_tutorial_api 26 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | if Rails.root.join('tmp/caching-dev.txt').exist? 17 | config.action_controller.perform_caching = true 18 | 19 | config.cache_store = :memory_store 20 | config.public_file_server.headers = { 21 | 'Cache-Control' => 'public, max-age=172800' 22 | } 23 | else 24 | config.action_controller.perform_caching = false 25 | 26 | config.cache_store = :null_store 27 | end 28 | 29 | config.action_mailer.raise_delivery_errors = true 30 | config.action_mailer.delivery_method = :test 31 | host = 'rails-tutorial-mhartl.c9users.io' 32 | config.action_mailer.default_url_options = { host: host, protocol: 'https' } 33 | config.action_mailer.perform_caching = false 34 | 35 | # Print deprecation notices to the Rails logger. 36 | config.active_support.deprecation = :log 37 | 38 | # Raise an error on page load if there are pending migrations. 39 | config.active_record.migration_error = :page_load 40 | 41 | # Debug mode disables concatenation and preprocessing of assets. 42 | # This option may cause significant delays in view rendering with a large 43 | # number of complex assets. 44 | config.assets.debug = true 45 | 46 | # Raises error for missing translations 47 | # config.action_view.raise_on_missing_translations = true 48 | 49 | # Use an evented file watcher to asynchronously detect changes in source code, 50 | # routes, locales, etc. This feature depends on the listen gem. 51 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 52 | end 53 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | 'Cache-Control' => 'public, max-age=3600' 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | config.action_mailer.perform_caching = false 31 | 32 | # Tell Action Mailer not to deliver emails to the real world. 33 | # The :test delivery method accumulates sent emails in the 34 | # ActionMailer::Base.deliveries array. 35 | config.action_mailer.delivery_method = :test 36 | config.action_mailer.default_url_options = { host: 'example.com' } 37 | 38 | # Print deprecation notices to the stderr. 39 | config.active_support.deprecation = :stderr 40 | 41 | # Raises error for missing translations 42 | # config.action_view.raise_on_missing_translations = true 43 | end 44 | -------------------------------------------------------------------------------- /config/initializers/active_model_serializers.rb: -------------------------------------------------------------------------------- 1 | ActiveModelSerializers.config.adapter = :json_api 2 | ActiveSupport.on_load(:action_controller) do 3 | require 'active_model_serializers/register_jsonapi_renderer' 4 | end 5 | -------------------------------------------------------------------------------- /config/initializers/active_record_belongs_to_required_by_default.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Require `belongs_to` associations by default. This is a new Rails 5.0 4 | # default, so it is introduced as a configuration option to ensure that apps 5 | # made on earlier versions of Rails are not affected when upgrading. 6 | Rails.application.config.active_record.belongs_to_required_by_default = true 7 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ApplicationController.renderer.defaults.merge!( 4 | # http_host: 'example.org', 5 | # https: false 6 | # ) 7 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 11 | # Rails.application.config.assets.precompile += %w( search.js ) 12 | -------------------------------------------------------------------------------- /config/initializers/callback_terminator.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Do not halt callback chains when a callback returns false. This is a new 4 | # Rails 5.0 default, so it is introduced as a configuration option to ensure 5 | # that apps made with earlier versions of Rails are not affected when upgrading. 6 | ActiveSupport.halt_callback_chains_on_return_false = false 7 | -------------------------------------------------------------------------------- /config/initializers/carrier_wave.rb: -------------------------------------------------------------------------------- 1 | if Rails.env.production? && ENV['S3_BUCKET'] 2 | CarrierWave.configure do |config| 3 | config.fog_credentials = { 4 | # Configuration for Amazon S3 5 | :provider => 'AWS', 6 | :aws_access_key_id => ENV['S3_ACCESS_KEY'], 7 | :aws_secret_access_key => ENV['S3_SECRET_KEY'] 8 | } 9 | config.fog_directory = ENV['S3_BUCKET'] 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /config/initializers/per_form_csrf_tokens.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Enable per-form CSRF tokens. 4 | Rails.application.config.action_controller.per_form_csrf_tokens = true 5 | -------------------------------------------------------------------------------- /config/initializers/rack_attack.rb: -------------------------------------------------------------------------------- 1 | class Rack::Attack 2 | redis = ENV['REDISTOGO_URL'] || 'localhost' 3 | Rack::Attack.cache.store = ActiveSupport::Cache::RedisStore.new(redis) 4 | 5 | throttle('req/ip', :limit => 1000, :period => 10.minutes) do |req| 6 | req.ip if req.path.starts_with?('/api/v1') 7 | end 8 | end 9 | 10 | Rack::Attack.throttled_response = lambda do |env| 11 | now = Time.now 12 | match_data = env['rack.attack.match_data'] 13 | 14 | headers = { 15 | 'X-RateLimit-Limit' => match_data[:limit].to_s, 16 | 'X-RateLimit-Remaining' => '0', 17 | 'X-RateLimit-Reset' => (now + (match_data[:period] - now.to_i % match_data[:period])).to_s 18 | } 19 | 20 | [ 429, headers, [Api::V1::ErrorSerializer.new(429, 'Too many requests').to_json]] 21 | end 22 | -------------------------------------------------------------------------------- /config/initializers/request_forgery_protection.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Enable origin-checking CSRF mitigation. 4 | Rails.application.config.action_controller.forgery_protection_origin_check = true 5 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_sample_app_session' 4 | -------------------------------------------------------------------------------- /config/initializers/ssl_options.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure SSL options to enable HSTS with subdomains. 4 | Rails.application.config.ssl_options = { hsts: { subdomains: true } } 5 | -------------------------------------------------------------------------------- /config/initializers/to_time_preserves_timezone.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Preserve the timezone of the receiver when calling to `to_time`. 4 | # Ruby 2.4 will change the behavior of `to_time` to preserve the timezone 5 | # when converting to an instance of `Time` instead of the previous behavior 6 | # of converting to the local system timezone. 7 | # 8 | # Rails 5.0 introduced this config option so that apps made with earlier 9 | # versions of Rails are not affected when upgrading. 10 | ActiveSupport.to_time_preserves_timezone = true 11 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /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 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | workers Integer(ENV['WEB_CONCURRENCY'] || 2) 2 | threads_count = Integer(ENV['RAILS_MAX_THREADS'] || 5) 3 | threads threads_count, threads_count 4 | 5 | preload_app! 6 | 7 | rackup DefaultRackup 8 | port ENV['PORT'] || 3000 9 | environment ENV['RACK_ENV'] || 'development' 10 | 11 | on_worker_boot do 12 | # Worker specific setup for Rails 4.1+ 13 | # See: https://devcenter.heroku.com/articles/ 14 | # deploying-rails-applications-with-the-puma-web-server#on-worker-boot 15 | ActiveRecord::Base.establish_connection 16 | end 17 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root 'static_pages#home' 3 | get '/help', to: 'static_pages#help' 4 | get '/about', to: 'static_pages#about' 5 | get '/contact', to: 'static_pages#contact' 6 | get '/signup', to: 'users#new' 7 | get '/login', to: 'sessions#new' 8 | post '/login', to: 'sessions#create' 9 | delete '/logout', to: 'sessions#destroy' 10 | resources :users do 11 | member do 12 | get :following, :followers 13 | end 14 | end 15 | resources :account_activations, only: [:edit] 16 | resources :password_resets, only: [:new, :create, :edit, :update] 17 | resources :microposts, only: [:create, :destroy] 18 | resources :relationships, only: [:create, :destroy] 19 | 20 | #api 21 | namespace :api do 22 | namespace :v1 do 23 | resources :sessions, only: [:create, :show] 24 | 25 | resources :users, only: [:index, :create, :show, :update, :destroy] do 26 | post :activate, on: :collection 27 | resources :followers, only: [:index, :destroy] 28 | resources :followings, only: [:index, :destroy] do 29 | post :create, on: :member 30 | end 31 | resource :feed, only: [:show] 32 | end 33 | resources :microposts, only: [:index, :create, :show, :update, :destroy] 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rails secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: 45ec0d9f13a6cc38fc2cfd0314fae312cbf9e8db5dc8485cba84fce8ae39c62944ad57e4dab04c7f89c6fc3cbc865fbd4ce93005b4aa2b47ed96712be1b1a28d 15 | ember_activation_url: <%= ENV['EMBER_ACTIVATION_URL'] %> 16 | 17 | test: 18 | secret_key_base: f76a37afd9d7f155210462ac50b966ace268ba7fee105d13dab09ddd56844cf6d4c47e6ffa57f088b2beb3db923a27779d5c45297f0f7aa1df48849cd00cea03 19 | ember_activation_url: <%= ENV['EMBER_ACTIVATION_URL'] %> 20 | 21 | # Do not keep production secrets in the repository, 22 | # instead read values from the environment. 23 | production: 24 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 25 | ember_activation_url: <%= ENV['EMBER_ACTIVATION_URL'] %> 26 | -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | %w( 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ).each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /db/migrate/20160523185459_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :users do |t| 4 | t.string :name 5 | t.string :email 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20160523202806_add_index_to_users_email.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToUsersEmail < ActiveRecord::Migration[5.0] 2 | def change 3 | add_index :users, :email, unique: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160523203059_add_password_digest_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddPasswordDigestToUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :password_digest, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160602174637_add_remember_digest_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddRememberDigestToUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :remember_digest, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160605021434_add_admin_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddAdminToUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :admin, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160606194223_add_activation_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddActivationToUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :activation_digest, :string 4 | add_column :users, :activated, :boolean, default: false 5 | add_column :users, :activated_at, :datetime 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20160606233616_add_reset_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddResetToUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :reset_digest, :string 4 | add_column :users, :reset_sent_at, :datetime 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20160608164853_create_microposts.rb: -------------------------------------------------------------------------------- 1 | class CreateMicroposts < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :microposts do |t| 4 | t.text :content 5 | t.references :user, foreign_key: true 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20160608202205_add_picture_to_microposts.rb: -------------------------------------------------------------------------------- 1 | class AddPictureToMicroposts < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :microposts, :picture, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160609220802_create_relationships.rb: -------------------------------------------------------------------------------- 1 | class CreateRelationships < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :relationships do |t| 4 | t.integer :follower_id 5 | t.integer :followed_id 6 | 7 | t.timestamps 8 | end 9 | add_index :relationships, :follower_id 10 | add_index :relationships, :followed_id 11 | add_index :relationships, [:follower_id, :followed_id], unique: true end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20160807112453_add_token_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddTokenToUsers < ActiveRecord::Migration[5.0] 2 | def up 3 | add_column :users, :token, :string 4 | 5 | User.find_each{|user| user.save!} 6 | 7 | change_column_null :users, :token, false 8 | end 9 | 10 | def down 11 | remove_column :users, :token, :string 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20160907183517_add_cache_counters.rb: -------------------------------------------------------------------------------- 1 | class AddCacheCounters < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :microposts_count, :integer, :null => false, :default => 0 4 | add_column :users, :followers_count, :integer, :null => false, :default => 0 5 | add_column :users, :followings_count, :integer, :null => false, :default => 0 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /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: 20160907183517) do 14 | 15 | # These are extensions that must be enabled in order to support this database 16 | enable_extension "plpgsql" 17 | 18 | create_table "microposts", force: :cascade do |t| 19 | t.text "content" 20 | t.integer "user_id" 21 | t.datetime "created_at", null: false 22 | t.datetime "updated_at", null: false 23 | t.string "picture" 24 | t.index ["user_id"], name: "index_microposts_on_user_id", using: :btree 25 | end 26 | 27 | create_table "relationships", force: :cascade do |t| 28 | t.integer "follower_id" 29 | t.integer "followed_id" 30 | t.datetime "created_at", null: false 31 | t.datetime "updated_at", null: false 32 | t.index ["followed_id"], name: "index_relationships_on_followed_id", using: :btree 33 | t.index ["follower_id", "followed_id"], name: "index_relationships_on_follower_id_and_followed_id", unique: true, using: :btree 34 | t.index ["follower_id"], name: "index_relationships_on_follower_id", using: :btree 35 | end 36 | 37 | create_table "users", force: :cascade do |t| 38 | t.string "name" 39 | t.string "email" 40 | t.datetime "created_at", null: false 41 | t.datetime "updated_at", null: false 42 | t.string "password_digest" 43 | t.string "remember_digest" 44 | t.boolean "admin", default: false 45 | t.string "activation_digest" 46 | t.boolean "activated", default: false 47 | t.datetime "activated_at" 48 | t.string "reset_digest" 49 | t.datetime "reset_sent_at" 50 | t.string "token", null: false 51 | t.integer "microposts_count", default: 0, null: false 52 | t.integer "followers_count", default: 0, null: false 53 | t.integer "followings_count", default: 0, null: false 54 | t.index ["email"], name: "index_users_on_email", unique: true, using: :btree 55 | end 56 | 57 | add_foreign_key "microposts", "users" 58 | end 59 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # Users 2 | User.create!(name: "Example User", 3 | email: "example@railstutorial.org", 4 | password: "foobar", 5 | password_confirmation: "foobar", 6 | admin: true, 7 | activated: true, 8 | activated_at: Time.zone.now) 9 | 10 | 99.times do |n| 11 | name = Faker::Name.name 12 | email = "example-#{n+1}@railstutorial.org" 13 | password = "password" 14 | User.create!(name: name, 15 | email: email, 16 | password: password, 17 | password_confirmation: password, 18 | activated: true, 19 | activated_at: Time.zone.now) 20 | end 21 | 22 | # Microposts 23 | users = User.order(:created_at).take(6) 24 | 50.times do 25 | content = Faker::Lorem.sentence(5) 26 | users.each { |user| user.microposts.create!(content: content) } 27 | end 28 | 29 | # Following relationships 30 | users = User.all 31 | user = users.first 32 | following = users[2..50] 33 | followers = users[3..40] 34 | following.each { |followed| user.follow(followed) } 35 | followers.each { |follower| follower.follow(user) } -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | coverage 6 | InstalledFiles 7 | lib/bundler/man 8 | pkg 9 | rdoc 10 | spec/reports 11 | test/tmp 12 | test/version_tmp 13 | tmp 14 | *.DS_STORE 15 | build/ 16 | .cache 17 | .vagrant 18 | .sass-cache 19 | 20 | # YARD artifacts 21 | .yardoc 22 | _yardoc 23 | doc/ 24 | .idea/ 25 | -------------------------------------------------------------------------------- /docs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Version 1.4.0 4 | 5 | *November 24, 2016* 6 | 7 | - Upgrade Middleman and Rouge gems, should hopefully solve a number of bugs 8 | - Update some links in README 9 | - Fix broken Vagrant startup script 10 | - Fix some problems with deploy.sh help message 11 | - Fix bug with language tabs not hiding properly if no error 12 | - Add `!default` to SASS variables 13 | - Fix bug with logo margin 14 | - Bump tested Ruby versions in .travis.yml 15 | 16 | ## Version 1.3.3 17 | 18 | *June 11, 2016* 19 | 20 | Documentation and example changes. 21 | 22 | ## Version 1.3.2 23 | 24 | *February 3, 2016* 25 | 26 | A small bugfix for slightly incorrect background colors on code samples in some cases. 27 | 28 | ## Version 1.3.1 29 | 30 | *January 31, 2016* 31 | 32 | A small bugfix for incorrect whitespace in code blocks. 33 | 34 | ## Version 1.3 35 | 36 | *January 27, 2016* 37 | 38 | We've upgraded Middleman and a number of other dependencies, which should fix quite a few bugs. 39 | 40 | Instead of `rake build` and `rake deploy`, you should now run `bundle exec middleman build --clean` to build your server, and `./deploy.sh` to deploy it to Github Pages. 41 | 42 | ## Version 1.2 43 | 44 | *June 20, 2015* 45 | 46 | **Fixes:** 47 | 48 | - Remove crash on invalid languages 49 | - Update Tocify to scroll to the highlighted header in the Table of Contents 50 | - Fix variable leak and update search algorithms 51 | - Update Python examples to be valid Python 52 | - Update gems 53 | - More misc. bugfixes of Javascript errors 54 | - Add Dockerfile 55 | - Remove unused gems 56 | - Optimize images, fonts, and generated asset files 57 | - Add chinese font support 58 | - Remove RedCarpet header ID patch 59 | - Update language tabs to not disturb existing query strings 60 | 61 | ## Version 1.1 62 | 63 | *July 27, 2014* 64 | 65 | **Fixes:** 66 | 67 | - Finally, a fix for the redcarpet upgrade bug 68 | 69 | ## Version 1.0 70 | 71 | *July 2, 2014* 72 | 73 | [View Issues](https://github.com/tripit/slate/issues?milestone=1&state=closed) 74 | 75 | **Features:** 76 | 77 | - Responsive designs for phones and tablets 78 | - Started tagging versions 79 | 80 | **Fixes:** 81 | 82 | - Fixed 'unrecognized expression' error 83 | - Fixed #undefined hash bug 84 | - Fixed bug where the current language tab would be unselected 85 | - Fixed bug where tocify wouldn't highlight the current section while searching 86 | - Fixed bug where ids of header tags would have special characters that caused problems 87 | - Updated layout so that pages with disabled search wouldn't load search.js 88 | - Cleaned up Javascript 89 | -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Middleman 4 | gem 'middleman', '~>4.1.0' 5 | gem 'middleman-syntax', '~> 3.0.0' 6 | gem 'middleman-autoprefixer', '~> 2.7.0' 7 | gem "middleman-sprockets", "~> 4.0.0" 8 | gem 'rouge', '~> 2.0.5' 9 | gem 'redcarpet', '~> 3.3.2' 10 | -------------------------------------------------------------------------------- /docs/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2008-2013 Concur Technologies, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | not use this file except in compliance with the License. You may obtain 5 | a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | License for the specific language governing permissions and limitations 13 | under the License. -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Ruby on Rails API tutorial Readme 2 | 3 | [ ![Codeship Status for vasilakisfil/rails5_api_tutorial](https://app.codeship.com/projects/05259880-c6b4-0134-9508-6e9b40f38dec/status?branch=master)](https://app.codeship.com/projects/198686) 4 | 5 | This is the sample application for the 6 | [*Ruby on Rails Tutorial: 7 | Learn Web Development with Rails*](http://www.railstutorial.org/) 8 | by [Michael Hartl](http://www.michaelhartl.com/) **but with an API for the [Ember version](https://github.com/vasilakisfil/ember_on_rails5)**, 9 | used by the [api tutorial](https://github.com/vasilakisfil/rails5_api_tutorial). 10 | 11 | It's deployed [here](https://rails-tutorial-api.herokuapp.com/). 12 | Ember version is deployed [here](https://ember-on-rails-tutorial.herokuapp.com) based on this [repo](https://github.com/vasilakisfil/ember_on_rails5) 13 | 14 | If you are looking the Rails 4 version there is a [blog post](https://labs.kollegorna.se/blog/2015/04/build-an-api-now/) 15 | 16 | ## License 17 | 18 | All code added by me (vasilakisfil) is availabe under the MIT License. 19 | The rest is available jointly under the MIT License and the Beerware License. See [LICENSE.md](LICENSE.md) for details. 20 | 21 | ## Getting started 22 | 23 | To get started with the app, clone the repo and then install the needed gems: 24 | 25 | ``` 26 | cd ~/tmp 27 | git clone https://github.com/vasilakisfil/rails5_api_tutorial.git 28 | cd rails5_api_tutorial 29 | bundle install 30 | bundle exec rake db:create #we use postgresql instead of sqlite3 31 | bundle exec rake db:migrate 32 | bundle exec rails s 33 | ``` 34 | 35 | ## Contributing 36 | 37 | Bug reports and pull requests are welcome on GitHub at https://github.com/vasilakisfil/rails5_api_tutorial. 38 | This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to 39 | the [Contributor Covenant](http://contributor-covenant.org) code of conduct. 40 | -------------------------------------------------------------------------------- /docs/Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.configure(2) do |config| 2 | config.vm.box = "ubuntu/trusty64" 3 | config.vm.network :forwarded_port, guest: 4567, host: 4567 4 | 5 | config.vm.provision "bootstrap", 6 | type: "shell", 7 | inline: <<-SHELL 8 | sudo apt-get update 9 | sudo apt-get install -yq ruby2.0 ruby2.0-dev pkg-config build-essential nodejs git libxml2-dev libxslt-dev 10 | sudo apt-get autoremove -yq 11 | gem2.0 install --no-ri --no-rdoc bundler 12 | SHELL 13 | 14 | # add the local user git config to the vm 15 | config.vm.provision "file", source: "~/.gitconfig", destination: ".gitconfig" 16 | 17 | config.vm.provision "install", 18 | type: "shell", 19 | privileged: false, 20 | inline: <<-SHELL 21 | echo "==============================================" 22 | echo "Installing app dependencies" 23 | cd /vagrant 24 | bundle config build.nokogiri --use-system-libraries 25 | bundle install 26 | SHELL 27 | 28 | config.vm.provision "run", 29 | type: "shell", 30 | privileged: false, 31 | run: "always", 32 | inline: <<-SHELL 33 | echo "==============================================" 34 | echo "Starting up middleman at http://localhost:4567" 35 | echo "If it does not come up, check the ~/middleman.log file for any error messages" 36 | cd /vagrant 37 | bundle exec middleman server --watcher-force-polling --watcher_latency=1 &> ~/middleman.log & 38 | SHELL 39 | end 40 | -------------------------------------------------------------------------------- /docs/config.rb: -------------------------------------------------------------------------------- 1 | # Markdown 2 | set :markdown_engine, :redcarpet 3 | set :markdown, 4 | fenced_code_blocks: true, 5 | smartypants: true, 6 | disable_indented_code_blocks: true, 7 | prettify: true, 8 | tables: true, 9 | with_toc_data: true, 10 | no_intra_emphasis: true 11 | 12 | # Assets 13 | set :css_dir, 'stylesheets' 14 | set :js_dir, 'javascripts' 15 | set :images_dir, 'images' 16 | set :fonts_dir, 'fonts' 17 | 18 | # Activate the syntax highlighter 19 | activate :syntax 20 | 21 | activate :sprockets 22 | 23 | activate :autoprefixer do |config| 24 | config.browsers = ['last 2 version', 'Firefox ESR'] 25 | config.cascade = false 26 | config.inline = true 27 | end 28 | 29 | # Github pages require relative links 30 | activate :relative_assets 31 | set :relative_links, true 32 | 33 | set :build_dir, '../public/api/v1/docs/' 34 | 35 | # Build Configuration 36 | configure :build do 37 | # If you're having trouble with Middleman hanging, commenting 38 | # out the following two lines has been known to help 39 | activate :minify_css 40 | activate :minify_javascript 41 | # activate :asset_hash 42 | # activate :gzip 43 | end 44 | 45 | # Deploy Configuration 46 | # If you want Middleman to listen on a different port, you can set that below 47 | set :port, 4567 48 | -------------------------------------------------------------------------------- /docs/source/fonts/slate.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/docs/source/fonts/slate.eot -------------------------------------------------------------------------------- /docs/source/fonts/slate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs/source/fonts/slate.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/docs/source/fonts/slate.ttf -------------------------------------------------------------------------------- /docs/source/fonts/slate.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/docs/source/fonts/slate.woff -------------------------------------------------------------------------------- /docs/source/fonts/slate.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/docs/source/fonts/slate.woff2 -------------------------------------------------------------------------------- /docs/source/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/docs/source/images/logo.png -------------------------------------------------------------------------------- /docs/source/images/navbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/docs/source/images/navbar.png -------------------------------------------------------------------------------- /docs/source/includes/_feed.md: -------------------------------------------------------------------------------- 1 | # Feed 2 | 3 | The feed of a user is a list of microposts of users she follows order by `created_at` in a descending order. 4 | 5 | ## Show Feed 6 | ```http 7 | GET /api/v1/users/1/followings HTTP/1.1 8 | Micropost-Agent: MyClient/1.0.0 9 | Accept: application/vnd.api+json 10 | ``` 11 | ```http 12 | HTTP/1.1 200 OK 13 | { 14 | "data":[ 15 | { 16 | "id":"349", 17 | "type":"microposts", 18 | "attributes":{ 19 | "content":"test", 20 | "user-id":1, 21 | "created-at":"2016-11-19T18:56:55Z", 22 | "updated-at":"2016-11-19T18:56:55Z" 23 | }, 24 | "relationships":{ 25 | "user":{ 26 | "links":{ 27 | "related":"/api/v1/users/1" 28 | } 29 | } 30 | } 31 | } 32 | ], 33 | "links":{ 34 | "self":"http://localhost:3000/api/v1/microposts?page%5Bnumber%5D=1\u0026page%5Bsize%5D=1\u0026per_page=1", 35 | "next":"http://localhost:3000/api/v1/microposts?page%5Bnumber%5D=2\u0026page%5Bsize%5D=1\u0026per_page=1", 36 | "last":"http://localhost:3000/api/v1/microposts?page%5Bnumber%5D=349\u0026page%5Bsize%5D=1\u0026per_page=1" 37 | }, 38 | "meta":{ 39 | "current-page":1, 40 | "next-page":2, 41 | "prev-page":null, 42 | "total-pages":349, 43 | "total-count":349 44 | } 45 | } 46 | ``` 47 | 48 | If you want to filter the microposts, use the [microposts](#microposts) API. 49 | -------------------------------------------------------------------------------- /docs/source/index.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: API Reference 3 | 4 | language_tabs: 5 | - http 6 | 7 | toc_footers: 8 | - Documentation Powered by Slate 9 | 10 | includes: 11 | - overview 12 | - sessions 13 | - users 14 | - microposts 15 | - followers 16 | - followings 17 | - feed 18 | 19 | search: true 20 | --- 21 | 22 | # Introduction 23 | 24 | Welcome to rails tutorial API. 25 | The implementation of the API can be found [here](https://github.com/vasilakisfil/rails5_api_tutorial). Using this API an Ember application is also built, found [here](https://github.com/vasilakisfil/ember_on_rails5). 26 | 27 | 28 | The following documents the V1 API. 29 | -------------------------------------------------------------------------------- /docs/source/javascripts/all.js: -------------------------------------------------------------------------------- 1 | //= require ./lib/_energize 2 | //= require ./app/_lang 3 | //= require ./app/_search 4 | //= require ./app/_toc 5 | -------------------------------------------------------------------------------- /docs/source/javascripts/all_nosearch.js: -------------------------------------------------------------------------------- 1 | //= require ./lib/_energize 2 | //= require ./app/_lang 3 | //= require ./app/_toc 4 | -------------------------------------------------------------------------------- /docs/source/javascripts/app/_search.js: -------------------------------------------------------------------------------- 1 | //= require ../lib/_lunr 2 | //= require ../lib/_jquery 3 | //= require ../lib/_jquery.highlight 4 | (function () { 5 | 'use strict'; 6 | 7 | var content, searchResults; 8 | var highlightOpts = { element: 'span', className: 'search-highlight' }; 9 | 10 | var index = new lunr.Index(); 11 | 12 | index.ref('id'); 13 | index.field('title', { boost: 10 }); 14 | index.field('body'); 15 | index.pipeline.add(lunr.trimmer, lunr.stopWordFilter); 16 | 17 | $(populate); 18 | $(bind); 19 | 20 | function populate() { 21 | $('h1, h2').each(function() { 22 | var title = $(this); 23 | var body = title.nextUntil('h1, h2'); 24 | index.add({ 25 | id: title.prop('id'), 26 | title: title.text(), 27 | body: body.text() 28 | }); 29 | }); 30 | } 31 | 32 | function bind() { 33 | content = $('.content'); 34 | searchResults = $('.search-results'); 35 | 36 | $('#input-search').on('keyup', search); 37 | } 38 | 39 | function search(event) { 40 | unhighlight(); 41 | searchResults.addClass('visible'); 42 | 43 | // ESC clears the field 44 | if (event.keyCode === 27) this.value = ''; 45 | 46 | if (this.value) { 47 | var results = index.search(this.value).filter(function(r) { 48 | return r.score > 0.0001; 49 | }); 50 | 51 | if (results.length) { 52 | searchResults.empty(); 53 | $.each(results, function (index, result) { 54 | var elem = document.getElementById(result.ref); 55 | searchResults.append("
  • " + $(elem).text() + "
  • "); 56 | }); 57 | highlight.call(this); 58 | } else { 59 | searchResults.html('
  • '); 60 | $('.search-results li').text('No Results Found for "' + this.value + '"'); 61 | } 62 | } else { 63 | unhighlight(); 64 | searchResults.removeClass('visible'); 65 | } 66 | } 67 | 68 | function highlight() { 69 | if (this.value) content.highlight(this.value, highlightOpts); 70 | } 71 | 72 | function unhighlight() { 73 | content.unhighlight(highlightOpts); 74 | } 75 | })(); 76 | -------------------------------------------------------------------------------- /docs/source/javascripts/app/_toc.js: -------------------------------------------------------------------------------- 1 | //= require ../lib/_jquery 2 | //= require ../lib/_jquery_ui 3 | //= require ../lib/_jquery.tocify 4 | //= require ../lib/_imagesloaded.min 5 | (function (global) { 6 | 'use strict'; 7 | 8 | var closeToc = function() { 9 | $(".tocify-wrapper").removeClass('open'); 10 | $("#nav-button").removeClass('open'); 11 | }; 12 | 13 | var makeToc = function() { 14 | global.toc = $("#toc").tocify({ 15 | selectors: 'h1, h2', 16 | extendPage: false, 17 | theme: 'none', 18 | smoothScroll: false, 19 | showEffectSpeed: 0, 20 | hideEffectSpeed: 180, 21 | ignoreSelector: '.toc-ignore', 22 | highlightOffset: 60, 23 | scrollTo: -1, 24 | scrollHistory: true, 25 | hashGenerator: function (text, element) { 26 | return element.prop('id'); 27 | } 28 | }).data('toc-tocify'); 29 | 30 | $("#nav-button").click(function() { 31 | $(".tocify-wrapper").toggleClass('open'); 32 | $("#nav-button").toggleClass('open'); 33 | return false; 34 | }); 35 | 36 | $(".page-wrapper").click(closeToc); 37 | $(".tocify-item").click(closeToc); 38 | }; 39 | 40 | // Hack to make already open sections to start opened, 41 | // instead of displaying an ugly animation 42 | function animate() { 43 | setTimeout(function() { 44 | toc.setOption('showEffectSpeed', 180); 45 | }, 50); 46 | } 47 | 48 | $(function() { 49 | makeToc(); 50 | animate(); 51 | setupLanguages($('body').data('languages')); 52 | $('.content').imagesLoaded( function() { 53 | global.toc.calculateHeights(); 54 | }); 55 | }); 56 | })(window); 57 | 58 | -------------------------------------------------------------------------------- /docs/source/stylesheets/_icon-font.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'slate'; 3 | src:font-url('slate.eot?-syv14m'); 4 | src:font-url('slate.eot?#iefix-syv14m') format('embedded-opentype'), 5 | font-url('slate.woff2?-syv14m') format('woff2'), 6 | font-url('slate.woff?-syv14m') format('woff'), 7 | font-url('slate.ttf?-syv14m') format('truetype'), 8 | font-url('slate.svg?-syv14m#slate') format('svg'); 9 | font-weight: normal; 10 | font-style: normal; 11 | } 12 | 13 | %icon { 14 | font-family: 'slate'; 15 | speak: none; 16 | font-style: normal; 17 | font-weight: normal; 18 | font-variant: normal; 19 | text-transform: none; 20 | line-height: 1; 21 | } 22 | 23 | %icon-exclamation-sign { 24 | @extend %icon; 25 | content: "\e600"; 26 | } 27 | %icon-info-sign { 28 | @extend %icon; 29 | content: "\e602"; 30 | } 31 | %icon-ok-sign { 32 | @extend %icon; 33 | content: "\e606"; 34 | } 35 | %icon-search { 36 | @extend %icon; 37 | content: "\e607"; 38 | } 39 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/lib/assets/.keep -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/lib/tasks/.keep -------------------------------------------------------------------------------- /lib/tasks/auto_annotate_models.rake: -------------------------------------------------------------------------------- 1 | # NOTE: only doing this in development as some production environments (Heroku) 2 | # NOTE: are sensitive to local FS writes, and besides -- it's just not proper 3 | # NOTE: to have a dev-mode tool do its thing in production. 4 | if Rails.env.development? 5 | task :set_annotation_options do 6 | # You can override any of these by setting an environment variable of the 7 | # same name. 8 | Annotate.set_defaults({ 9 | 'position_in_routes' => "before", 10 | 'position_in_class' => "before", 11 | 'position_in_test' => "before", 12 | 'position_in_fixture' => "before", 13 | 'position_in_factory' => "before", 14 | 'show_indexes' => "true", 15 | 'simple_indexes' => "false", 16 | 'model_dir' => "app/models", 17 | 'include_version' => "false", 18 | 'require' => "", 19 | 'exclude_tests' => "false", 20 | 'exclude_fixtures' => "false", 21 | 'exclude_factories' => "false", 22 | 'exclude_serializers' => 'true', 23 | 'ignore_model_sub_dir' => "false", 24 | 'skip_on_db_migrate' => "false", 25 | 'format_bare' => "true", 26 | 'format_rdoc' => "false", 27 | 'format_markdown' => "false", 28 | 'sort' => "false", 29 | 'classified_sort' => "true", 30 | 'force' => "true", 31 | 'trace' => "false", 32 | }) 33 | end 34 | 35 | Annotate.load_tasks 36 | end 37 | 38 | -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/log/.keep -------------------------------------------------------------------------------- /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/api/v1/docs/fonts/slate.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/public/api/v1/docs/fonts/slate.eot -------------------------------------------------------------------------------- /public/api/v1/docs/fonts/slate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /public/api/v1/docs/fonts/slate.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/public/api/v1/docs/fonts/slate.ttf -------------------------------------------------------------------------------- /public/api/v1/docs/fonts/slate.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/public/api/v1/docs/fonts/slate.woff -------------------------------------------------------------------------------- /public/api/v1/docs/fonts/slate.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/public/api/v1/docs/fonts/slate.woff2 -------------------------------------------------------------------------------- /public/api/v1/docs/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/public/api/v1/docs/images/logo.png -------------------------------------------------------------------------------- /public/api/v1/docs/images/navbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/public/api/v1/docs/images/navbar.png -------------------------------------------------------------------------------- /public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /spec/apis/resource/feeds/show_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe Api::V1::FeedsController, '#show', type: :api do 4 | describe 'Authorization' do 5 | context 'when not authenticated' do 6 | before do 7 | user = FactoryGirl.create(:user) 8 | 9 | get api_v1_user_feed_path(user.id), format: :json 10 | end 11 | 12 | it_returns_status(401) 13 | it_follows_json_schema('errors') 14 | end 15 | 16 | context 'when authenticated as a regular user' do 17 | before do 18 | user = create_and_sign_in_user 19 | 5.times.collect{ FactoryGirl.create(:user) }.each do |u| 20 | 5.times{ FactoryGirl.create(:micropost, user: u) } 21 | FactoryGirl.create(:relationship, follower: u, followed: user) 22 | end 23 | 24 | get api_v1_microposts_path, format: :json 25 | end 26 | 27 | it_returns_status(200) 28 | it_follows_json_schema('regular/microposts') 29 | it_returns_collection_size(resource: 'microposts', size: 5*5) 30 | it_returns_collection_attribute_values( 31 | resource: 'microposts', model: proc{Micropost.first!}, attrs: [ 32 | :id, :content, :user_id, :created_at, :updated_at 33 | ], 34 | modifiers: { 35 | [:created_at, :updated_at] => proc{|i| i.in_time_zone('UTC').iso8601.to_s}, 36 | id: proc{|i| i.to_s} 37 | } 38 | ) 39 | end 40 | 41 | context 'when authenticated as an admin' do 42 | before do 43 | create_and_sign_in_admin_user 44 | user = FactoryGirl.create(:user) 45 | 5.times.collect{ FactoryGirl.create(:user) }.each do |u| 46 | 5.times{ FactoryGirl.create(:micropost, user: u) } 47 | FactoryGirl.create(:relationship, follower: u, followed: user) 48 | end 49 | 50 | get api_v1_microposts_path, format: :json 51 | end 52 | 53 | it_returns_status(200) 54 | it_follows_json_schema('admin/microposts') 55 | it_returns_collection_size(resource: 'microposts', size: 5*5) 56 | it_returns_collection_attribute_values( 57 | resource: 'microposts', model: proc{Micropost.first!}, attrs: [ 58 | :id, :content, :user_id, :created_at, :updated_at 59 | ], 60 | modifiers: { 61 | [:created_at, :updated_at] => proc{|i| i.iso8601}, 62 | id: proc{|i| i.to_s} 63 | } 64 | ) 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /spec/apis/resource/followers/destroy_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe Api::V1::FollowersController, '#destroy', type: :api do 4 | describe 'Authorization' do 5 | context 'when not authenticated' do 6 | before do 7 | user = FactoryGirl.create(:user) 8 | 5.times{ FactoryGirl.create(:relationship, followed: user) } 9 | 10 | delete api_v1_user_follower_path( 11 | user_id: user.id, id: user.followers.first!.id 12 | ) 13 | end 14 | 15 | it_returns_status(401) 16 | it_follows_json_schema('errors') 17 | end 18 | 19 | context 'when authenticated as not the owner' do 20 | before do 21 | user = FactoryGirl.create(:user) 22 | 5.times{ FactoryGirl.create(:relationship, followed: user) } 23 | create_and_sign_in_user 24 | 25 | @follower = user.followers.first! 26 | 27 | delete api_v1_user_follower_path( 28 | user_id: user.id, id: @follower.id 29 | ) 30 | end 31 | 32 | it_returns_status(403) 33 | it_follows_json_schema('errors') 34 | end 35 | 36 | context 'when autenticated as the owner' do 37 | before do 38 | user = create_and_sign_in_user 39 | 5.times{ FactoryGirl.create(:relationship, followed: user) } 40 | 41 | @follower = user.followers.first! 42 | 43 | delete api_v1_user_follower_path( 44 | user_id: user.id, id: @follower.id 45 | ) 46 | end 47 | 48 | it_returns_status(200) 49 | 50 | it_returns_attribute_values( 51 | resource: 'user', model: proc{@follower}, attrs: [ 52 | :id, :name, :created_at, :microposts_count, :followers_count, 53 | :followings_count 54 | ], 55 | modifiers: { 56 | created_at: proc{|i| i.in_time_zone('UTC').iso8601.to_s}, 57 | id: proc{|i| i.to_s}, 58 | followings_count: proc{|i| i - 1} 59 | } 60 | ) 61 | end 62 | end 63 | end 64 | 65 | -------------------------------------------------------------------------------- /spec/apis/resource/followers/index_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe Api::V1::FollowersController, '#index', type: :api do 4 | describe 'Authorization' do 5 | context 'when not authenticated' do 6 | before do 7 | user = FactoryGirl.create(:user) 8 | 5.times{ FactoryGirl.create(:relationship, followed: user) } 9 | 10 | get api_v1_user_followers_path(user_id: user.id) 11 | end 12 | 13 | it_returns_status(401) 14 | it_follows_json_schema('errors') 15 | end 16 | 17 | context 'when authenticated as a regular user' do 18 | before do 19 | @user = FactoryGirl.create(:user) 20 | 5.times{ FactoryGirl.create(:relationship, followed: @user) } 21 | create_and_sign_in_user 22 | 23 | get api_v1_user_followers_path(user_id: @user.id) 24 | end 25 | 26 | it_returns_status(200) 27 | it_follows_json_schema('regular/users') 28 | it_returns_collection_size(resource: 'users', size: 5) 29 | 30 | it_returns_collection_attribute_values( 31 | resource: 'users', model: proc{@user.followers.first!}, attrs: [ 32 | :id, :name, :created_at, :microposts_count, :followers_count, 33 | :followings_count 34 | ], 35 | modifiers: { 36 | [:created_at] => proc{|i| i.iso8601}, 37 | id: proc{|i| i.to_s} 38 | } 39 | ) 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/apis/resource/followings/create_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe Api::V1::FollowingsController, '#create', type: :api do 4 | describe 'Authorization' do 5 | context 'when not authenticated' do 6 | before do 7 | user = FactoryGirl.create(:user) 8 | following = FactoryGirl.create(:user) 9 | 10 | post api_v1_user_following_path( 11 | user_id: user.id, id: following.id 12 | ) 13 | end 14 | 15 | it_returns_status(401) 16 | it_follows_json_schema('errors') 17 | end 18 | 19 | context 'when authenticated as not the owner' do 20 | before do 21 | user = FactoryGirl.create(:user) 22 | following = FactoryGirl.create(:user) 23 | create_and_sign_in_user 24 | 25 | post api_v1_user_following_path( 26 | user_id: user.id, id: following.id 27 | ) 28 | end 29 | 30 | it_returns_status(403) 31 | it_follows_json_schema('errors') 32 | end 33 | 34 | context 'when authenticated as the owner' do 35 | before do 36 | user = FactoryGirl.create(:user) 37 | @following = FactoryGirl.create(:user) 38 | sign_in(user) 39 | 40 | post api_v1_user_following_path( 41 | user_id: user.id, id: @following.id 42 | ) 43 | end 44 | 45 | it_returns_status(200) 46 | it_follows_json_schema('regular/user') 47 | 48 | it_returns_attribute_values( 49 | resource: 'user', model: proc{@following}, attrs: [ 50 | :id, :name, :created_at, :microposts_count, :followers_count, 51 | :followings_count 52 | ], 53 | modifiers: { 54 | created_at: proc{|i| i.in_time_zone('UTC').iso8601.to_s}, 55 | id: proc{|i| i.to_s}, 56 | followers_count: proc{|i| i + 1} 57 | } 58 | ) 59 | end 60 | end 61 | end 62 | 63 | -------------------------------------------------------------------------------- /spec/apis/resource/followings/destroy_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe Api::V1::FollowingsController, '#destroy', type: :api do 4 | describe 'Authorization' do 5 | context 'when not authenticated' do 6 | before do 7 | user = FactoryGirl.create(:user) 8 | 5.times{ FactoryGirl.create(:relationship, follower: user) } 9 | 10 | delete api_v1_user_following_path( 11 | user_id: user.id, id: user.following.first!.id 12 | ) 13 | end 14 | 15 | it_returns_status(401) 16 | it_follows_json_schema('errors') 17 | end 18 | 19 | context 'when authenticated as not the owner' do 20 | before do 21 | user = FactoryGirl.create(:user) 22 | 5.times{ FactoryGirl.create(:relationship, follower: user) } 23 | create_and_sign_in_user 24 | @following = user.following.first! 25 | 26 | delete api_v1_user_following_path( 27 | user_id: user.id, id: @following.id 28 | ) 29 | end 30 | 31 | it_returns_status(403) 32 | it_follows_json_schema('errors') 33 | end 34 | 35 | context 'when authenticated as the owner' do 36 | before do 37 | user = FactoryGirl.create(:user) 38 | 5.times{ FactoryGirl.create(:relationship, follower: user) } 39 | sign_in(user) 40 | @following = user.following.first! 41 | 42 | delete api_v1_user_following_path( 43 | user_id: user.id, id: @following.id 44 | ) 45 | end 46 | 47 | it_returns_status(200) 48 | it_follows_json_schema('regular/user') 49 | it_returns_attribute_values( 50 | resource: 'user', model: proc{@following}, attrs: [ 51 | :id, :name, :created_at, :microposts_count, :followers_count, 52 | :followings_count 53 | ], 54 | modifiers: { 55 | created_at: proc{|i| i.in_time_zone('UTC').iso8601.to_s}, 56 | id: proc{|i| i.to_s}, 57 | followers_count: proc{|i| i - 1} 58 | } 59 | ) 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /spec/apis/resource/followings/index_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe Api::V1::FollowingsController, '#index', type: :api do 4 | describe 'Authorization' do 5 | context 'when not authenticated' do 6 | before do 7 | user = FactoryGirl.create(:user) 8 | 5.times{ FactoryGirl.create(:relationship, follower: user) } 9 | 10 | get api_v1_user_followings_path(user_id: user.id) 11 | end 12 | 13 | it_returns_status(401) 14 | it_follows_json_schema('errors') 15 | end 16 | 17 | context 'when authenticated as a regular user' do 18 | before do 19 | @user = FactoryGirl.create(:user) 20 | 5.times{ FactoryGirl.create(:relationship, follower: @user) } 21 | create_and_sign_in_user 22 | 23 | get api_v1_user_followings_path(user_id: @user.id) 24 | end 25 | 26 | it_returns_status(200) 27 | it_follows_json_schema('regular/users') 28 | it_returns_collection_size(resource: 'users', size: 5) 29 | 30 | it_returns_collection_attribute_values( 31 | resource: 'users', model: proc{@user.following.first!}, attrs: [ 32 | :id, :name, :created_at, :microposts_count, :followers_count, 33 | :followings_count 34 | ], 35 | modifiers: { 36 | [:created_at] => proc{|i| i.iso8601}, 37 | id: proc{|i| i.to_s} 38 | } 39 | ) 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/apis/resource/microposts/create_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe Api::V1::MicropostsController, '#create', type: :api do 4 | describe 'Authorization' do 5 | context 'when not authenticated' do 6 | before do 7 | user = FactoryGirl.create(:user) 8 | micropost = FactoryGirl.attributes_for(:micropost).merge(user_id: user.id) 9 | 10 | post api_v1_microposts_path, jsonapi_style(micropost: micropost.as_json) 11 | end 12 | 13 | it_returns_status(401) 14 | it_follows_json_schema('errors') 15 | end 16 | 17 | context 'when authenticated as a regular user' do 18 | before do 19 | user = create_and_sign_in_user 20 | @micropost = FactoryGirl.attributes_for(:micropost).merge(user_id: user.id) 21 | 22 | post api_v1_microposts_path, jsonapi_style(micropost: @micropost.as_json) 23 | end 24 | 25 | it_returns_status(201) 26 | it_follows_json_schema('regular/micropost') 27 | 28 | it_returns_attribute_values( 29 | resource: 'micropost', model: proc{@micropost}, attrs: [ 30 | :content, :user_id 31 | ] 32 | ) 33 | end 34 | 35 | context 'when authenticated as an admin' do 36 | before do 37 | user = create_and_sign_in_admin 38 | @micropost = FactoryGirl.attributes_for(:micropost).merge(user_id: user.id) 39 | 40 | post api_v1_microposts_path, jsonapi_style(micropost: @micropost.as_json) 41 | end 42 | 43 | it_returns_status(201) 44 | it_follows_json_schema('admin/micropost') 45 | 46 | it_returns_attribute_values( 47 | resource: 'micropost', model: proc{@micropost}, attrs: [ 48 | :content, :user_id 49 | ] 50 | ) 51 | end 52 | end 53 | end 54 | 55 | 56 | -------------------------------------------------------------------------------- /spec/apis/resource/microposts/destroy_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe Api::V1::MicropostsController, '#destroy', type: :api do 4 | describe 'Authorization' do 5 | context 'when not authenticated' do 6 | before do 7 | micropost = FactoryGirl.create(:micropost) 8 | 9 | delete api_v1_micropost_path(micropost), format: :json 10 | end 11 | 12 | it_returns_status(401) 13 | it_follows_json_schema('errors') 14 | end 15 | 16 | context 'when authenticated as a regular user' do 17 | before do 18 | create_and_sign_in_user 19 | @micropost = FactoryGirl.create(:micropost) 20 | 21 | delete api_v1_micropost_path(@micropost), format: :json 22 | end 23 | 24 | it_returns_status(200) 25 | it_follows_json_schema('regular/micropost') 26 | it_returns_attribute_values( 27 | resource: 'micropost', model: proc{@micropost}, attrs: [ 28 | :id, :content, :user_id, :created_at, :updated_at 29 | ], 30 | modifiers: { 31 | [:created_at, :updated_at] => proc{|i| i.in_time_zone('UTC').iso8601.to_s}, 32 | id: proc{|i| i.to_s} 33 | } 34 | ) 35 | end 36 | 37 | context 'when authenticated as an admin' do 38 | before do 39 | create_and_sign_in_admin 40 | @micropost = FactoryGirl.create(:micropost) 41 | 42 | delete api_v1_micropost_path(@micropost), format: :json 43 | end 44 | 45 | it_returns_status(200) 46 | it_follows_json_schema('admin/micropost') 47 | it_returns_attribute_values( 48 | resource: 'micropost', model: proc{@micropost}, attrs: [ 49 | :id, :content, :user_id, :created_at, :updated_at 50 | ], 51 | modifiers: { 52 | [:created_at, :updated_at] => proc{|i| i.in_time_zone('UTC').iso8601.to_s}, 53 | id: proc{|i| i.to_s} 54 | } 55 | ) 56 | end 57 | end 58 | end 59 | 60 | -------------------------------------------------------------------------------- /spec/apis/resource/microposts/index_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe Api::V1::MicropostsController, '#index', type: :api do 4 | describe 'Authorization' do 5 | context 'when not authenticated' do 6 | before do 7 | 5.times{ FactoryGirl.create(:micropost) } 8 | 9 | get api_v1_microposts_path, format: :json 10 | end 11 | 12 | it_returns_status(401) 13 | it_follows_json_schema('errors') 14 | end 15 | 16 | context 'when authenticated as a regular user' do 17 | before do 18 | create_and_sign_in_user 19 | 5.times{ FactoryGirl.create(:micropost) } 20 | 21 | get api_v1_microposts_path, format: :json 22 | end 23 | 24 | it_returns_status(200) 25 | it_follows_json_schema('regular/microposts') 26 | it_returns_collection_size(resource: 'microposts', size: 5) 27 | it_returns_collection_attribute_values( 28 | resource: 'microposts', model: proc{Micropost.first!}, attrs: [ 29 | :id, :content, :user_id, :created_at, :updated_at 30 | ], 31 | modifiers: { 32 | [:created_at, :updated_at] => proc{|i| i.in_time_zone('UTC').iso8601.to_s}, 33 | id: proc{|i| i.to_s} 34 | } 35 | ) 36 | end 37 | 38 | context 'when authenticated as an admin' do 39 | before do 40 | create_and_sign_in_admin_user 41 | 5.times{ FactoryGirl.create(:micropost) } 42 | 43 | get api_v1_microposts_path, format: :json 44 | end 45 | 46 | it_returns_status(200) 47 | it_follows_json_schema('admin/microposts') 48 | it_returns_collection_size(resource: 'microposts', size: 5) 49 | it_returns_collection_attribute_values( 50 | resource: 'microposts', model: proc{Micropost.first!}, attrs: [ 51 | :id, :content, :user_id, :created_at, :updated_at 52 | ], 53 | modifiers: { 54 | [:created_at, :updated_at] => proc{|i| i.iso8601}, 55 | id: proc{|i| i.to_s} 56 | } 57 | ) 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /spec/apis/resource/microposts/show_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe Api::V1::MicropostsController, '#show', type: :api do 4 | describe 'Authorization' do 5 | context 'when not authenticated' do 6 | before do 7 | micropost = FactoryGirl.create(:micropost) 8 | 9 | get api_v1_micropost_path(micropost), format: :json 10 | end 11 | 12 | it_returns_status(401) 13 | it_follows_json_schema('errors') 14 | end 15 | 16 | context 'when authenticated as a regular user' do 17 | before do 18 | create_and_sign_in_user 19 | @micropost = FactoryGirl.create(:micropost) 20 | 21 | get api_v1_micropost_path(@micropost), format: :json 22 | end 23 | 24 | it_returns_status(200) 25 | it_follows_json_schema('regular/micropost') 26 | 27 | it_returns_attribute_values( 28 | resource: 'micropost', model: proc{@micropost}, attrs: [ 29 | :id, :content, :user_id, :created_at, :updated_at 30 | ], 31 | modifiers: { 32 | [:created_at, :updated_at] => proc{|i| i.in_time_zone('UTC').iso8601.to_s}, 33 | id: proc{|i| i.to_s} 34 | } 35 | ) 36 | end 37 | 38 | context 'when authenticated as an admin' do 39 | before do 40 | create_and_sign_in_admin 41 | @micropost = FactoryGirl.create(:micropost) 42 | 43 | get api_v1_micropost_path(@micropost), format: :json 44 | end 45 | 46 | it_returns_status(200) 47 | it_follows_json_schema('admin/micropost') 48 | 49 | it_returns_attribute_values( 50 | resource: 'micropost', model: proc{@micropost}, attrs: [ 51 | :id, :content, :user_id, :created_at, :updated_at 52 | ], 53 | modifiers: { 54 | [:created_at, :updated_at] => proc{|i| i.in_time_zone('UTC').iso8601.to_s}, 55 | id: proc{|i| i.to_s} 56 | } 57 | ) 58 | end 59 | end 60 | end 61 | 62 | -------------------------------------------------------------------------------- /spec/apis/resource/microposts/update_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe Api::V1::MicropostsController, '#update', type: :api do 4 | describe 'Authorization' do 5 | context 'when not authenticated' do 6 | before do 7 | micropost = FactoryGirl.create(:micropost) 8 | 9 | put api_v1_micropost_path(micropost), format: :json 10 | end 11 | 12 | it_returns_status(401) 13 | it_follows_json_schema('errors') 14 | end 15 | 16 | context 'when authenticated as a regular user' do 17 | before do 18 | create_and_sign_in_user 19 | @micropost = FactoryGirl.create(:micropost) 20 | 21 | put api_v1_micropost_path(@micropost), format: :json 22 | end 23 | 24 | it_returns_status(200) 25 | it_follows_json_schema('regular/micropost') 26 | it_returns_attribute_values( 27 | resource: 'micropost', model: proc{@micropost}, attrs: [ 28 | :id, :content, :user_id, :created_at, :updated_at 29 | ], 30 | modifiers: { 31 | [:created_at, :updated_at] => proc{|i| i.in_time_zone('UTC').iso8601.to_s}, 32 | id: proc{|i| i.to_s} 33 | } 34 | ) 35 | end 36 | 37 | context 'when authenticated as an admin' do 38 | before do 39 | create_and_sign_in_admin 40 | @micropost = FactoryGirl.create(:micropost) 41 | 42 | put api_v1_micropost_path(@micropost), format: :json 43 | end 44 | 45 | it_returns_status(200) 46 | it_follows_json_schema('admin/micropost') 47 | it_returns_attribute_values( 48 | resource: 'micropost', model: proc{@micropost}, attrs: [ 49 | :id, :content, :user_id, :created_at, :updated_at 50 | ], 51 | modifiers: { 52 | [:created_at, :updated_at] => proc{|i| i.in_time_zone('UTC').iso8601.to_s}, 53 | id: proc{|i| i.to_s} 54 | } 55 | ) 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /spec/apis/resource/sessions/create_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe Api::V1::SessionsController, '#create', type: :api do 4 | describe 'Authoriation' do 5 | context 'with wrong params' do 6 | before do 7 | user = FactoryGirl.create(:user, { 8 | password: 'foobar', password_confirmation: nil 9 | }) 10 | 11 | post api_v1_sessions_path( 12 | jsonapi_style(user: {email: user.email, password: 'foobar1'}) 13 | ) 14 | end 15 | 16 | it_returns_status(401) 17 | end 18 | 19 | context 'with correct params' do 20 | before do 21 | user = FactoryGirl.create(:user, { 22 | password: 'foobar', password_confirmation: nil 23 | }) 24 | 25 | post api_v1_sessions_path( 26 | jsonapi_style(user: {email: user.email, password: 'foobar'}) 27 | ) 28 | end 29 | 30 | it_returns_status(201) 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/apis/resource/sessions/show_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe Api::V1::SessionsController, '#show', type: :api do 4 | describe 'Authoriation' do 5 | context 'with wrong params' do 6 | before do 7 | user = FactoryGirl.create(:user, { 8 | password: 'foobar', password_confirmation: 'foobar' 9 | }) 10 | 11 | get api_v1_session_path(user.id) 12 | 13 | end 14 | 15 | it_returns_status(401) 16 | it_follows_json_schema('errors') 17 | end 18 | 19 | context 'with correct params' do 20 | before do 21 | user = FactoryGirl.create(:user, { 22 | password: 'foobar', password_confirmation: 'foobar' 23 | }) 24 | sign_in(user) 25 | 26 | get api_v1_session_path(user.id) 27 | end 28 | 29 | it_returns_status(200) 30 | it_follows_json_schema('regular/session') 31 | end 32 | end 33 | end 34 | 35 | -------------------------------------------------------------------------------- /spec/apis/resource/users/activate_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe Api::V1::UsersController, '#activate', type: :api do 4 | describe 'Authorization' do 5 | context 'when not authenticated' do 6 | context 'with correct token' do 7 | before do 8 | @user = FactoryGirl.create(:user) 9 | 10 | post activate_api_v1_users_path(email: @user.email, token: @user.activation_token) 11 | end 12 | 13 | it_returns_status(200) 14 | it_follows_json_schema('regular/user') 15 | it_returns_attribute_values( 16 | resource: 'user', model: proc{@user}, attrs: [ 17 | :name, :email 18 | ] 19 | ) 20 | end 21 | 22 | context 'with wrong token' do 23 | before do 24 | @user = FactoryGirl.create(:user) 25 | 26 | post activate_api_v1_users_path(email: @user.email, token: 'allo?') 27 | end 28 | 29 | it_returns_status(404) 30 | it_follows_json_schema('errors') 31 | end 32 | end 33 | 34 | context 'when authenticated as a regular user' do 35 | before do 36 | create_and_sign_in_user 37 | @user = FactoryGirl.create(:user) 38 | 39 | post activate_api_v1_users_path(email: @user.email, token: @user.activation_token) 40 | end 41 | 42 | it_returns_status(200) 43 | it_follows_json_schema('regular/user') 44 | it_returns_attribute_values( 45 | resource: 'user', model: proc{@user}, attrs: [ 46 | :name, :email 47 | ] 48 | ) 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /spec/apis/resource/users/create_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe Api::V1::UsersController, '#create', type: :api do 4 | describe 'Authorization' do 5 | context 'when not authenticated' do 6 | before do 7 | user = FactoryGirl.attributes_for(:user) 8 | post api_v1_users_path, jsonapi_style(user: user.as_json) 9 | end 10 | 11 | it_returns_status(201) 12 | it_follows_json_schema('regular/user') 13 | end 14 | 15 | context 'when authenticated as a regular user' do 16 | before do 17 | create_and_sign_in_user 18 | @user = FactoryGirl.attributes_for(:user) 19 | 20 | post api_v1_users_path, jsonapi_style(user: @user.as_json) 21 | end 22 | 23 | it_returns_status(201) 24 | it_follows_json_schema('regular/user') 25 | it_returns_attribute_values( 26 | resource: 'user', model: proc{@user}, attrs: [ 27 | :name, :email 28 | ] 29 | ) 30 | end 31 | 32 | context 'when authenticated as an admin' do 33 | before do 34 | create_and_sign_in_admin 35 | @user = FactoryGirl.attributes_for(:user) 36 | 37 | post api_v1_users_path, jsonapi_style(user: @user.as_json) 38 | end 39 | 40 | it_returns_status(201) 41 | it_follows_json_schema('regular/user') 42 | it_returns_attribute_values( 43 | resource: 'user', model: proc{@user}, attrs: [ 44 | :name, :email 45 | ] 46 | ) 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/apis/resource/users/destroy_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe Api::V1::UsersController, type: :api do 4 | context :destroy do 5 | context 'when authenticated as a guest' do 6 | before do 7 | @user = FactoryGirl.create(:user) 8 | 9 | delete api_v1_user_path(@user.id) 10 | end 11 | 12 | it_returns_status(401) 13 | it_follows_json_schema('errors') 14 | end 15 | 16 | context 'when authenticated as a regular user' do 17 | before do 18 | create_and_sign_in_user 19 | @user = FactoryGirl.create(:user) 20 | 21 | delete api_v1_user_path(@user.id) 22 | end 23 | 24 | it_returns_status(200) 25 | it_follows_json_schema('regular/user') 26 | it_returns_attribute_values( 27 | resource: 'user', model: proc{@user}, attrs: [ 28 | :id, :name, :email, :created_at 29 | ], modifiers: { 30 | created_at: proc{|i| i.in_time_zone('UTC').iso8601.to_s}, 31 | id: proc{|i| i.to_s} 32 | } 33 | ) 34 | end 35 | 36 | context 'when authenticated as an admin' do 37 | before do 38 | create_and_sign_in_admin 39 | @user = FactoryGirl.create(:user) 40 | 41 | delete api_v1_user_path(@user.id) 42 | end 43 | 44 | it_returns_status(200) 45 | it_follows_json_schema('admin/user') 46 | it_returns_attribute_values( 47 | resource: 'user', model: proc{@user}, attrs: User.column_names, 48 | modifiers: { 49 | [:created_at, :updated_at] => proc{|i| i.in_time_zone('UTC').iso8601.to_s}, 50 | id: proc{|i| i.to_s} 51 | } 52 | ) 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /spec/apis/resource/users/index_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe Api::V1::UsersController, '#index', type: :api do 4 | describe 'Authorization' do 5 | context 'when not authenticated' do 6 | before do 7 | 5.times{ FactoryGirl.create(:user) } 8 | 9 | get api_v1_users_path, format: :json 10 | end 11 | 12 | it_returns_status(200) 13 | it_follows_json_schema('guest/users') 14 | end 15 | 16 | context 'when authenticated as a regular user' do 17 | before do 18 | create_and_sign_in_user 19 | 5.times{ FactoryGirl.create(:user) } 20 | 21 | get api_v1_users_path, format: :json 22 | end 23 | 24 | it_returns_status(200) 25 | it_follows_json_schema('regular/users') 26 | it_returns_collection_size(resource: 'users', size: 5+1) 27 | 28 | it_returns_collection_attribute_values( 29 | resource: 'users', model: proc{User.first!}, attrs: [ 30 | :id, :name, :created_at, :microposts_count, :followers_count, 31 | :followings_count 32 | ], 33 | modifiers: { 34 | created_at: proc{|i| i.in_time_zone('UTC').iso8601.to_s}, 35 | id: proc{|i| i.to_s} 36 | } 37 | ) 38 | end 39 | 40 | context 'when authenticated as an admin' do 41 | before do 42 | create_and_sign_in_admin_user 43 | 5.times{ FactoryGirl.create(:user) } 44 | 45 | get api_v1_users_path, format: :json 46 | end 47 | 48 | it_returns_status(200) 49 | it_follows_json_schema('admin/users') 50 | it_returns_collection_size(resource: 'users', size: 5+1) 51 | 52 | it_returns_collection_attribute_values( 53 | resource: 'users', model: proc{User.first!}, attrs: User.column_names, 54 | modifiers: { 55 | [:created_at, :updated_at] => proc{|i| i.iso8601}, 56 | id: proc{|i| i.to_s} 57 | } 58 | ) 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /spec/apis/resource/users/show_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe Api::V1::UsersController, '#show', type: :api do 4 | describe 'Authorization' do 5 | context 'when as guest' do 6 | before do 7 | FactoryGirl.create(:user) 8 | @user = User.last! 9 | 10 | get api_v1_user_path(@user.id) 11 | end 12 | 13 | it_returns_status(200) 14 | it_follows_json_schema('guest/user') 15 | it_returns_attribute_values( 16 | resource: 'user', model: proc{@user}, attrs: [:name, :created_at], 17 | modifiers: { 18 | created_at: proc{|i| i.in_time_zone('UTC').iso8601.to_s}, 19 | } 20 | ) 21 | end 22 | 23 | context 'when authenticated as a regular user' do 24 | before do 25 | create_and_sign_in_user 26 | FactoryGirl.create(:user) 27 | @user = User.last! 28 | 29 | get api_v1_user_path(@user.id) 30 | end 31 | 32 | it_returns_status(200) 33 | it_follows_json_schema('regular/user') 34 | it_returns_attribute_values( 35 | resource: 'user', model: proc{@user}, attrs: [ 36 | :id, :name, :created_at, :microposts_count, :followers_count, 37 | :followings_count 38 | ], 39 | modifiers: { 40 | created_at: proc{|i| i.in_time_zone('UTC').iso8601.to_s}, 41 | id: proc{|i| i.to_s} 42 | } 43 | ) 44 | end 45 | 46 | context 'when authenticated as an admin' do 47 | before do 48 | create_and_sign_in_admin 49 | FactoryGirl.create(:user) 50 | @user = User.last! 51 | 52 | get api_v1_user_path(@user.id) 53 | end 54 | 55 | it_returns_status(200) 56 | it_follows_json_schema('admin/user') 57 | it_returns_attribute_values( 58 | resource: 'user', model: proc{@user}, attrs: User.column_names, 59 | modifiers: { 60 | [:created_at, :updated_at] => proc{|i| i.in_time_zone('UTC').iso8601.to_s}, 61 | id: proc{|i| i.to_s} 62 | } 63 | ) 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /spec/apis/resource/users/update_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe Api::V1::UsersController, '#update', type: :api do 4 | describe 'Authorization' do 5 | context 'when authenticated as a guest user' do 6 | before do 7 | FactoryGirl.create(:user) 8 | @user = User.last! 9 | @user.name = 'Something else' 10 | 11 | put api_v1_user_path(@user.id), jsonapi_style(user: @user.as_json) 12 | end 13 | 14 | it_returns_status(401) 15 | it_follows_json_schema('errors') 16 | end 17 | 18 | context 'when authenticated as a regular user' do 19 | before do 20 | create_and_sign_in_user 21 | FactoryGirl.create(:user) 22 | @user = User.last! 23 | @user.name = 'Something else' 24 | 25 | put api_v1_user_path(@user.id), jsonapi_style(user: @user.as_json) 26 | end 27 | 28 | it_returns_status(200) 29 | it_follows_json_schema('regular/user') 30 | it_returns_attribute_values( 31 | resource: 'user', model: proc{@user}, attrs: [ 32 | :id, :name, :created_at, :microposts_count, :followers_count, 33 | :followings_count 34 | ], 35 | modifiers: { 36 | created_at: proc{|i| i.in_time_zone('UTC').iso8601.to_s}, 37 | id: proc{|i| i.to_s} 38 | } 39 | ) 40 | end 41 | 42 | context 'when authenticated as an admin' do 43 | before do 44 | create_and_sign_in_admin 45 | FactoryGirl.create(:user) 46 | @user = User.last! 47 | @user.name = 'Something else' 48 | 49 | put api_v1_user_path(@user.id), jsonapi_style(user: @user.as_json) 50 | end 51 | 52 | it_returns_status(200) 53 | it_follows_json_schema('admin/user') 54 | it_returns_attribute_values( 55 | resource: 'user', model: proc{@user}, attrs: User.column_names, 56 | modifiers: { 57 | [:created_at, :updated_at] => proc{|i| i.in_time_zone('UTC').iso8601.to_s}, 58 | id: proc{|i| i.to_s} 59 | } 60 | ) 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /spec/factories/micropost.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :micropost do 3 | content { 'foobar' } 4 | 5 | user 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/factories/relationships.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: relationships 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime not null 7 | # updated_at :datetime not null 8 | # followed_id :integer 9 | # follower_id :integer 10 | # 11 | # Indexes 12 | # 13 | # index_relationships_on_followed_id (followed_id) 14 | # index_relationships_on_follower_id (follower_id) 15 | # index_relationships_on_follower_id_and_followed_id (follower_id,followed_id) UNIQUE 16 | # 17 | 18 | FactoryGirl.define do 19 | factory :relationship do 20 | association :follower, factory: :user 21 | association :followed, factory: :user 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/factories/users.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: users 4 | # 5 | # id :integer not null, primary key 6 | # activated :boolean default(FALSE) 7 | # activated_at :datetime 8 | # activation_digest :string 9 | # admin :boolean default(FALSE) 10 | # email :string 11 | # followers_count :integer default(0), not null 12 | # followings_count :integer default(0), not null 13 | # microposts_count :integer default(0), not null 14 | # name :string 15 | # password_digest :string 16 | # remember_digest :string 17 | # reset_digest :string 18 | # reset_sent_at :datetime 19 | # token :string not null 20 | # created_at :datetime not null 21 | # updated_at :datetime not null 22 | # 23 | # Indexes 24 | # 25 | # index_users_on_email (email) UNIQUE 26 | # 27 | 28 | FactoryGirl.define do 29 | factory :user do 30 | pwd = Faker::Internet.password 31 | 32 | email { Faker::Internet.email } 33 | name { Faker::Name.name } 34 | password { pwd } 35 | password_confirmation { pwd } 36 | 37 | factory :admin do 38 | admin { true } 39 | end 40 | =begin 41 | factory :user_with_microposts do 42 | transient do 43 | posts_count 5 44 | end 45 | 46 | after(:create) do |user, evaluator| 47 | create_list(:micropost, evaluator.posts_count, user: user) 48 | end 49 | end 50 | =end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require File.expand_path('../../config/environment', __FILE__) 3 | abort("The Rails environment is running in production mode!") if Rails.env.production? 4 | require 'spec_helper' 5 | require 'rspec/rails' 6 | 7 | require 'rspec/json_schema' 8 | 9 | Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } 10 | ActiveRecord::Migration.maintain_test_schema! 11 | 12 | RSpec.configure do |config| 13 | config.fixture_path = "#{::Rails.root}/spec/fixtures" 14 | config.use_transactional_fixtures = true 15 | config.infer_spec_type_from_file_location! 16 | config.filter_rails_from_backtrace! 17 | config.include Rspec::ApiHelpers.with(adapter: :json_api), type: :api #from rspec-api_helpers gem 18 | end 19 | -------------------------------------------------------------------------------- /spec/schemas/admin/micropost.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "data": { 6 | "type": "object", 7 | "properties": { 8 | "id": { 9 | "type": "string" 10 | }, 11 | "type": { 12 | "type": "string" 13 | }, 14 | "attributes": { 15 | "type": "object", 16 | "properties": { 17 | "content": { 18 | "type": "string" 19 | }, 20 | "user-id": { 21 | "type": "integer" 22 | }, 23 | "created-at": { 24 | "type": "string" 25 | }, 26 | "updated-at": { 27 | "type": "string" 28 | } 29 | }, 30 | "required": [ 31 | "content", 32 | "user-id", 33 | "created-at", 34 | "updated-at" 35 | ] 36 | }, 37 | "relationships": { 38 | "type": "object", 39 | "properties": { 40 | "user": { 41 | "type": "object", 42 | "properties": { 43 | "links": { 44 | "type": "object", 45 | "properties": { 46 | "related": { 47 | "type": "string" 48 | } 49 | }, 50 | "required": [ 51 | "related" 52 | ] 53 | } 54 | }, 55 | "required": [ 56 | "links" 57 | ] 58 | } 59 | }, 60 | "required": [ 61 | "user" 62 | ] 63 | } 64 | }, 65 | "required": [ 66 | "id", 67 | "type", 68 | "attributes", 69 | "relationships" 70 | ] 71 | } 72 | }, 73 | "required": [ 74 | "data" 75 | ] 76 | } 77 | -------------------------------------------------------------------------------- /spec/schemas/errors.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "errors": { 6 | "type": "array", 7 | "items": { 8 | "type": "object", 9 | "properties": { 10 | "status": { 11 | "type": "integer" 12 | }, 13 | "title": { 14 | "type": "string" 15 | }, 16 | "detail": { 17 | "type": "string" 18 | }, 19 | "source": { 20 | "type": "object", 21 | "properties": { 22 | "pointer": { 23 | "type": "string" 24 | } 25 | }, 26 | "required": [ 27 | "pointer" 28 | ] 29 | } 30 | }, 31 | "required": [ 32 | "status", 33 | "title", 34 | "detail", 35 | "source" 36 | ] 37 | } 38 | } 39 | }, 40 | "required": [ 41 | "errors" 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /spec/schemas/regular/micropost.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "data": { 6 | "type": "object", 7 | "properties": { 8 | "id": { 9 | "type": "string" 10 | }, 11 | "type": { 12 | "type": "string" 13 | }, 14 | "attributes": { 15 | "type": "object", 16 | "properties": { 17 | "content": { 18 | "type": "string" 19 | }, 20 | "user-id": { 21 | "type": "integer" 22 | }, 23 | "created-at": { 24 | "type": "string" 25 | } 26 | }, 27 | "required": [ 28 | "content", 29 | "user-id", 30 | "created-at", 31 | "updated-at" 32 | ] 33 | }, 34 | "relationships": { 35 | "type": "object", 36 | "properties": { 37 | "user": { 38 | "type": "object", 39 | "properties": { 40 | "links": { 41 | "type": "object", 42 | "properties": { 43 | "related": { 44 | "type": "string" 45 | } 46 | }, 47 | "required": [ 48 | "related" 49 | ] 50 | } 51 | }, 52 | "required": [ 53 | "links" 54 | ] 55 | } 56 | }, 57 | "required": [ 58 | "user" 59 | ] 60 | } 61 | }, 62 | "required": [ 63 | "id", 64 | "type", 65 | "attributes", 66 | "relationships" 67 | ] 68 | } 69 | }, 70 | "required": [ 71 | "data" 72 | ] 73 | } 74 | -------------------------------------------------------------------------------- /spec/schemas/regular/microposts.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "data": { 6 | "type": "array", 7 | "items": { 8 | "type": "object", 9 | "properties": { 10 | "id": { 11 | "type": "string" 12 | }, 13 | "type": { 14 | "type": "string" 15 | }, 16 | "attributes": { 17 | "type": "object", 18 | "properties": { 19 | "content": { 20 | "type": "string" 21 | }, 22 | "user-id": { 23 | "type": "integer" 24 | }, 25 | "created-at": { 26 | "type": "string" 27 | } 28 | }, 29 | "required": [ 30 | "content", 31 | "user-id", 32 | "created-at" 33 | ] 34 | }, 35 | "relationships": { 36 | "type": "object", 37 | "properties": { 38 | "user": { 39 | "type": "object", 40 | "properties": { 41 | "links": { 42 | "type": "object", 43 | "properties": { 44 | "related": { 45 | "type": "string" 46 | } 47 | }, 48 | "required": [ 49 | "related" 50 | ] 51 | } 52 | }, 53 | "required": [ 54 | "links" 55 | ] 56 | } 57 | }, 58 | "required": [ 59 | "user" 60 | ] 61 | } 62 | }, 63 | "required": [ 64 | "id", 65 | "type", 66 | "attributes", 67 | "relationships" 68 | ] 69 | } 70 | }, 71 | "meta": { 72 | "type": "object", 73 | "properties": { 74 | "current-page": { 75 | "type": "integer" 76 | }, 77 | "next-page": { 78 | "type": ["null", "integer"] 79 | }, 80 | "prev-page": { 81 | "type": ["null", "integer"] 82 | }, 83 | "total-pages": { 84 | "type": "integer" 85 | }, 86 | "total-count": { 87 | "type": "integer" 88 | } 89 | }, 90 | "required": [ 91 | "current-page", 92 | "next-page", 93 | "prev-page", 94 | "total-pages", 95 | "total-count" 96 | ] 97 | } 98 | }, 99 | "required": [ 100 | "data", 101 | "links", 102 | "meta" 103 | ] 104 | } 105 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.expect_with :rspec do |expectations| 3 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 4 | end 5 | 6 | config.mock_with :rspec do |mocks| 7 | mocks.verify_partial_doubles = true 8 | end 9 | 10 | config.shared_context_metadata_behavior = :apply_to_host_groups 11 | end 12 | -------------------------------------------------------------------------------- /spec/support/api_helpers.rb: -------------------------------------------------------------------------------- 1 | module ApiHelpers 2 | def jsonapi_style(hash) 3 | resource_key = hash.first.first 4 | resource_attributes = hash.first.last 5 | 6 | return { 7 | data: { 8 | type: resource_key.to_s.pluralize, 9 | attributes: resource_attributes 10 | } 11 | } 12 | end 13 | end 14 | 15 | RSpec.configure do |config| 16 | config.include ApiHelpers, type: :api 17 | end 18 | -------------------------------------------------------------------------------- /spec/support/authentication_helper.rb: -------------------------------------------------------------------------------- 1 | module AuthenticationHelper 2 | def sign_in(user) 3 | header('Authorization', %Q{Token token="#{user.token}", email="#{user.email}"}) 4 | end 5 | 6 | def create_and_sign_in_user 7 | user = FactoryGirl.create(:user) 8 | sign_in(user) 9 | return user 10 | end 11 | alias_method :create_and_sign_in_another_user, :create_and_sign_in_user 12 | 13 | def create_and_sign_in_admin 14 | admin = FactoryGirl.create(:admin) 15 | sign_in(admin) 16 | return admin 17 | end 18 | alias_method :create_and_sign_in_admin_user, :create_and_sign_in_admin 19 | end 20 | 21 | RSpec.configure do |config| 22 | config.include AuthenticationHelper, :type=>:api 23 | end 24 | -------------------------------------------------------------------------------- /spec/support/database_cleaner.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.before :suite do 3 | DatabaseCleaner[:active_record].strategy = :transaction 4 | DatabaseCleaner.clean_with(:truncation) 5 | end 6 | 7 | config.around(:each) do |example| 8 | DatabaseCleaner.cleaning do 9 | example.run 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/support/factory_girl.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.include FactoryGirl::Syntax::Methods 3 | 4 | config.before(:suite) do 5 | begin 6 | DatabaseCleaner.start 7 | FactoryGirl.lint 8 | ensure 9 | DatabaseCleaner.clean 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/support/rack_helper.rb: -------------------------------------------------------------------------------- 1 | module RackHelper 2 | include Rack::Test::Methods 3 | 4 | def app 5 | Rails.application 6 | end 7 | end 8 | 9 | RSpec.configure do |config| 10 | config.include RackHelper, type: :api 11 | config.include Rails.application.routes.url_helpers, type: :api 12 | end 13 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/test/controllers/.keep -------------------------------------------------------------------------------- /test/controllers/microposts_controller_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: microposts 4 | # 5 | # id :integer not null, primary key 6 | # content :text 7 | # picture :string 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # user_id :integer 11 | # 12 | # Indexes 13 | # 14 | # index_microposts_on_user_id (user_id) 15 | # 16 | 17 | require 'test_helper' 18 | 19 | class MicropostsControllerTest < ActionDispatch::IntegrationTest 20 | 21 | def setup 22 | @micropost = microposts(:orange) 23 | end 24 | 25 | test "should redirect create when not logged in" do 26 | assert_no_difference 'Micropost.count' do 27 | post microposts_path, params: { micropost: { content: "Lorem ipsum" } } 28 | end 29 | assert_redirected_to login_url 30 | end 31 | 32 | test "should redirect destroy when not logged in" do 33 | assert_no_difference 'Micropost.count' do 34 | delete micropost_path(@micropost) 35 | end 36 | assert_redirected_to login_url 37 | end 38 | 39 | test "should redirect destroy for wrong micropost" do 40 | log_in_as(users(:michael)) 41 | micropost = microposts(:ants) 42 | assert_no_difference 'Micropost.count' do 43 | delete micropost_path(micropost) 44 | end 45 | assert_redirected_to root_url 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /test/controllers/relationships_controller_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: relationships 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime not null 7 | # updated_at :datetime not null 8 | # followed_id :integer 9 | # follower_id :integer 10 | # 11 | # Indexes 12 | # 13 | # index_relationships_on_followed_id (followed_id) 14 | # index_relationships_on_follower_id (follower_id) 15 | # index_relationships_on_follower_id_and_followed_id (follower_id,followed_id) UNIQUE 16 | # 17 | 18 | require 'test_helper' 19 | 20 | class RelationshipsControllerTest < ActionDispatch::IntegrationTest 21 | 22 | test "create should require logged-in user" do 23 | assert_no_difference 'Relationship.count' do 24 | post relationships_path 25 | end 26 | assert_redirected_to login_url 27 | end 28 | 29 | test "destroy should require logged-in user" do 30 | assert_no_difference 'Relationship.count' do 31 | delete relationship_path(relationships(:one)) 32 | end 33 | assert_redirected_to login_url 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /test/controllers/sessions_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SessionsControllerTest < ActionDispatch::IntegrationTest 4 | 5 | test "should get new" do 6 | get login_path 7 | assert_response :success 8 | end 9 | end -------------------------------------------------------------------------------- /test/controllers/static_pages_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class StaticPagesControllerTest < ActionDispatch::IntegrationTest 4 | 5 | test "should get home" do 6 | get root_path 7 | assert_response :success 8 | assert_select "title", "Ruby on Rails Tutorial Sample App" 9 | end 10 | 11 | test "should get help" do 12 | get help_path 13 | assert_response :success 14 | assert_select "title", "Help | Ruby on Rails Tutorial Sample App" 15 | end 16 | 17 | test "should get about" do 18 | get about_path 19 | assert_response :success 20 | assert_select "title", "About | Ruby on Rails Tutorial Sample App" 21 | end 22 | 23 | test "should get contact" do 24 | get contact_path 25 | assert_response :success 26 | assert_select "title", "Contact | Ruby on Rails Tutorial Sample App" 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/fixtures/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/test/fixtures/.keep -------------------------------------------------------------------------------- /test/fixtures/files/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/test/fixtures/files/.keep -------------------------------------------------------------------------------- /test/fixtures/microposts.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: microposts 4 | # 5 | # id :integer not null, primary key 6 | # content :text 7 | # picture :string 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # user_id :integer 11 | # 12 | # Indexes 13 | # 14 | # index_microposts_on_user_id (user_id) 15 | # 16 | 17 | orange: 18 | content: "I just ate an orange!" 19 | created_at: <%= 10.minutes.ago %> 20 | user: michael 21 | 22 | tau_manifesto: 23 | content: "Check out the @tauday site by @mhartl: http://tauday.com" 24 | created_at: <%= 3.years.ago %> 25 | user: michael 26 | 27 | cat_video: 28 | content: "Sad cats are sad: http://youtu.be/PKffm2uI4dk" 29 | created_at: <%= 2.hours.ago %> 30 | user: michael 31 | 32 | most_recent: 33 | content: "Writing a short test" 34 | created_at: <%= Time.zone.now %> 35 | user: michael 36 | 37 | <% 30.times do |n| %> 38 | micropost_<%= n %>: 39 | content: <%= Faker::Lorem.sentence(5) %> 40 | created_at: <%= 42.days.ago %> 41 | user: michael 42 | <% end %> 43 | 44 | ants: 45 | content: "Oh, is that what you want? Because that's how you get ants!" 46 | created_at: <%= 2.years.ago %> 47 | user: archer 48 | 49 | zone: 50 | content: "Danger zone!" 51 | created_at: <%= 3.days.ago %> 52 | user: archer 53 | 54 | tone: 55 | content: "I'm sorry. Your words made sense, but your sarcastic tone did not." 56 | created_at: <%= 10.minutes.ago %> 57 | user: lana 58 | 59 | van: 60 | content: "Dude, this van's, like, rolling probable cause." 61 | created_at: <%= 4.hours.ago %> 62 | user: lana 63 | -------------------------------------------------------------------------------- /test/fixtures/relationships.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: relationships 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime not null 7 | # updated_at :datetime not null 8 | # followed_id :integer 9 | # follower_id :integer 10 | # 11 | # Indexes 12 | # 13 | # index_relationships_on_followed_id (followed_id) 14 | # index_relationships_on_follower_id (follower_id) 15 | # index_relationships_on_follower_id_and_followed_id (follower_id,followed_id) UNIQUE 16 | # 17 | 18 | one: 19 | follower: michael 20 | followed: lana 21 | 22 | two: 23 | follower: michael 24 | followed: malory 25 | 26 | three: 27 | follower: lana 28 | followed: michael 29 | 30 | four: 31 | follower: archer 32 | followed: michael 33 | -------------------------------------------------------------------------------- /test/fixtures/users.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: users 4 | # 5 | # id :integer not null, primary key 6 | # activated :boolean default(FALSE) 7 | # activated_at :datetime 8 | # activation_digest :string 9 | # admin :boolean default(FALSE) 10 | # email :string 11 | # followers_count :integer default(0), not null 12 | # followings_count :integer default(0), not null 13 | # microposts_count :integer default(0), not null 14 | # name :string 15 | # password_digest :string 16 | # remember_digest :string 17 | # reset_digest :string 18 | # reset_sent_at :datetime 19 | # token :string not null 20 | # created_at :datetime not null 21 | # updated_at :datetime not null 22 | # 23 | # Indexes 24 | # 25 | # index_users_on_email (email) UNIQUE 26 | # 27 | 28 | michael: 29 | name: Michael Example 30 | email: michael@example.com 31 | password_digest: <%= User.digest('password') %> 32 | admin: true 33 | token: <%= SecureRandom.hex %> 34 | activated: true 35 | activated_at: <%= Time.zone.now %> 36 | 37 | archer: 38 | name: Sterling Archer 39 | email: duchess@example.gov 40 | password_digest: <%= User.digest('password') %> 41 | token: <%= SecureRandom.hex %> 42 | activated: true 43 | activated_at: <%= Time.zone.now %> 44 | 45 | lana: 46 | name: Lana Kane 47 | email: hands@example.gov 48 | password_digest: <%= User.digest('password') %> 49 | token: <%= SecureRandom.hex %> 50 | activated: true 51 | activated_at: <%= Time.zone.now %> 52 | 53 | malory: 54 | name: Malory Archer 55 | email: boss@example.gov 56 | password_digest: <%= User.digest('password') %> 57 | token: <%= SecureRandom.hex %> 58 | activated: true 59 | activated_at: <%= Time.zone.now %> 60 | 61 | <% 30.times do |n| %> 62 | user_<%= n %>: 63 | name: <%= "User #{n}" %> 64 | email: <%= "user-#{n}@example.com" %> 65 | password_digest: <%= User.digest('password') %> 66 | token: <%= SecureRandom.hex %> 67 | activated: true 68 | activated_at: <%= Time.zone.now %> 69 | <% end %> 70 | -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/test/helpers/.keep -------------------------------------------------------------------------------- /test/helpers/sessions_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SessionsHelperTest < ActionView::TestCase 4 | 5 | def setup 6 | @user = users(:michael) 7 | remember(@user) 8 | end 9 | 10 | test "current_user returns right user when session is nil" do 11 | assert_equal @user, current_user 12 | assert is_logged_in? 13 | end 14 | 15 | test "current_user returns nil when remember digest is wrong" do 16 | @user.update_attribute(:remember_digest, User.digest(User.new_token)) 17 | assert_nil current_user 18 | end 19 | end -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/test/integration/.keep -------------------------------------------------------------------------------- /test/integration/following_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class FollowingTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | @other = users(:archer) 8 | log_in_as(@user) 9 | end 10 | 11 | test "following page" do 12 | get following_user_path(@user) 13 | assert_not @user.following.empty? 14 | assert_match @user.following.count.to_s, response.body 15 | @user.following.each do |user| 16 | assert_select "a[href=?]", user_path(user) 17 | end 18 | end 19 | 20 | test "followers page" do 21 | get followers_user_path(@user) 22 | assert_not @user.followers.empty? 23 | assert_match @user.followers.count.to_s, response.body 24 | @user.followers.each do |user| 25 | assert_select "a[href=?]", user_path(user) 26 | end 27 | end 28 | 29 | test "should follow a user the standard way" do 30 | assert_difference '@user.following.count', 1 do 31 | post relationships_path, params: { followed_id: @other.id } 32 | end 33 | end 34 | 35 | test "should follow a user with Ajax" do 36 | assert_difference '@user.following.count', 1 do 37 | post relationships_path, xhr: true, params: { followed_id: @other.id } 38 | end 39 | end 40 | 41 | test "should unfollow a user the standard way" do 42 | @user.follow(@other) 43 | relationship = @user.active_relationships.find_by(followed_id: @other.id) 44 | assert_difference '@user.following.count', -1 do 45 | delete relationship_path(relationship) 46 | end 47 | end 48 | 49 | test "should unfollow a user with Ajax" do 50 | @user.follow(@other) 51 | relationship = @user.active_relationships.find_by(followed_id: @other.id) 52 | assert_difference '@user.following.count', -1 do 53 | delete relationship_path(relationship), xhr: true 54 | end 55 | end 56 | end -------------------------------------------------------------------------------- /test/integration/microposts_interface_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class MicropostsInterfaceTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | end 8 | 9 | test "micropost interface" do 10 | log_in_as(@user) 11 | get root_path 12 | assert_select 'div.pagination' 13 | # Invalid submission 14 | assert_no_difference 'Micropost.count' do 15 | post microposts_path, params: { micropost: { content: "" } } 16 | end 17 | assert_select 'div#error_explanation' 18 | # Valid submission 19 | content = "This micropost really ties the room together" 20 | assert_difference 'Micropost.count', 1 do 21 | post microposts_path, params: { micropost: { content: content } } 22 | end 23 | assert_redirected_to root_url 24 | follow_redirect! 25 | assert_match content, response.body 26 | # Delete a post. 27 | assert_select 'a', text: 'delete' 28 | first_micropost = @user.microposts.paginate(page: 1).first 29 | assert_difference 'Micropost.count', -1 do 30 | delete micropost_path(first_micropost) 31 | end 32 | # Visit a different user. 33 | get user_path(users(:archer)) 34 | assert_select 'a', text: 'delete', count: 0 35 | end 36 | end -------------------------------------------------------------------------------- /test/integration/password_resets_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PasswordResetsTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | ActionMailer::Base.deliveries.clear 7 | @user = users(:michael) 8 | end 9 | 10 | test "password resets" do 11 | get new_password_reset_path 12 | assert_template 'password_resets/new' 13 | # Invalid email 14 | post password_resets_path, params: { password_reset: { email: "" } } 15 | assert_not flash.empty? 16 | assert_template 'password_resets/new' 17 | # Valid email 18 | post password_resets_path, 19 | params: { password_reset: { email: @user.email } } 20 | assert_not_equal @user.reset_digest, @user.reload.reset_digest 21 | assert_equal 1, ActionMailer::Base.deliveries.size 22 | assert_not flash.empty? 23 | assert_redirected_to root_url 24 | # Password reset form 25 | user = assigns(:user) 26 | # Wrong email 27 | get edit_password_reset_path(user.reset_token, email: "") 28 | assert_redirected_to root_url 29 | # Inactive user 30 | user.toggle!(:activated) 31 | get edit_password_reset_path(user.reset_token, email: user.email) 32 | assert_redirected_to root_url 33 | user.toggle!(:activated) 34 | # Right email, wrong token 35 | get edit_password_reset_path('wrong token', email: user.email) 36 | assert_redirected_to root_url 37 | # Right email, right token 38 | get edit_password_reset_path(user.reset_token, email: user.email) 39 | assert_template 'password_resets/edit' 40 | assert_select "input[name=email][type=hidden][value=?]", user.email 41 | # Invalid password & confirmation 42 | patch password_reset_path(user.reset_token), 43 | params: { email: user.email, 44 | user: { password: "foobaz", 45 | password_confirmation: "barquux" } } 46 | assert_select 'div#error_explanation' 47 | # Empty password 48 | patch password_reset_path(user.reset_token), 49 | params: { email: user.email, 50 | user: { password: "", 51 | password_confirmation: "" } } 52 | assert_select 'div#error_explanation' 53 | # Valid password & confirmation 54 | patch password_reset_path(user.reset_token), 55 | params: { email: user.email, 56 | user: { password: "foobaz", 57 | password_confirmation: "foobaz" } } 58 | assert is_logged_in? 59 | assert_not flash.empty? 60 | assert_redirected_to user 61 | end 62 | end -------------------------------------------------------------------------------- /test/integration/site_layout_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SiteLayoutTest < ActionDispatch::IntegrationTest 4 | 5 | test "layout links" do 6 | get root_path 7 | assert_template 'static_pages/home' 8 | assert_select "a[href=?]", root_path, count: 2 9 | assert_select "a[href=?]", help_path 10 | assert_select "a[href=?]", about_path 11 | assert_select "a[href=?]", contact_path 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/integration/users_edit_test.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'test_helper' 3 | 4 | class UsersEditTest < ActionDispatch::IntegrationTest 5 | 6 | def setup 7 | @user = users(:michael) 8 | end 9 | 10 | test "unsuccessful edit" do 11 | log_in_as(@user) 12 | get edit_user_path(@user) 13 | assert_template 'users/edit' 14 | patch user_path(@user), params: { user: { name: "", 15 | email: "foo@invalid", 16 | password: "foo", 17 | password_confirmation: "bar" } } 18 | assert_template 'users/edit' 19 | end 20 | 21 | test "successful edit with friendly forwarding" do 22 | get edit_user_path(@user) 23 | log_in_as(@user) 24 | assert_redirected_to edit_user_path(@user) 25 | name = "Foo Bar" 26 | email = "foo@bar.com" 27 | patch user_path(@user), params: { user: { name: name, 28 | email: email, 29 | password: "", 30 | password_confirmation: "" } } 31 | assert_not flash.empty? 32 | assert_redirected_to @user 33 | @user.reload 34 | assert_equal name, @user.name 35 | assert_equal email, @user.email 36 | end 37 | end -------------------------------------------------------------------------------- /test/integration/users_index_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UsersIndexTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @admin = users(:michael) 7 | @non_admin = users(:archer) 8 | end 9 | 10 | test "index as admin including pagination and delete links" do 11 | log_in_as(@admin) 12 | get users_path 13 | assert_template 'users/index' 14 | assert_select 'div.pagination' 15 | first_page_of_users = User.paginate(page: 1) 16 | first_page_of_users.each do |user| 17 | assert_select 'a[href=?]', user_path(user), text: user.name 18 | unless user == @admin 19 | assert_select 'a[href=?]', user_path(user), text: 'delete' 20 | end 21 | end 22 | assert_difference 'User.count', -1 do 23 | delete user_path(@non_admin) 24 | end 25 | end 26 | 27 | test "index as non-admin" do 28 | log_in_as(@non_admin) 29 | get users_path 30 | assert_select 'a', text: 'delete', count: 0 31 | end 32 | end -------------------------------------------------------------------------------- /test/integration/users_login_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UsersLoginTest < ActionDispatch::IntegrationTest 4 | def setup 5 | @user = users(:michael) 6 | end 7 | 8 | test "login with invalid information" do 9 | get login_path 10 | assert_template 'sessions/new' 11 | post login_path, params: { session: { email: "", password: "" } } 12 | assert_template 'sessions/new' 13 | assert_not flash.empty? 14 | get root_path 15 | assert flash.empty? 16 | end 17 | 18 | test "login with valid information followed by logout" do 19 | get login_path 20 | post login_path, params: { session: { email: @user.email, 21 | password: 'password' } } 22 | assert is_logged_in? 23 | assert_redirected_to @user 24 | follow_redirect! 25 | assert_template 'users/show' 26 | assert_select "a[href=?]", login_path, count: 0 27 | assert_select "a[href=?]", logout_path 28 | assert_select "a[href=?]", user_path(@user) 29 | delete logout_path 30 | assert_not is_logged_in? 31 | assert_redirected_to root_url 32 | # Simulate a user clicking logout in a second window. 33 | delete logout_path 34 | follow_redirect! 35 | assert_select "a[href=?]", login_path 36 | assert_select "a[href=?]", logout_path, count: 0 37 | assert_select "a[href=?]", user_path(@user), count: 0 38 | end 39 | 40 | test "login with remembering" do 41 | log_in_as(@user, remember_me: '1') 42 | assert_not_nil cookies['remember_token'] 43 | end 44 | 45 | test "login without remembering" do 46 | log_in_as(@user, remember_me: '0') 47 | assert_nil cookies['remember_token'] 48 | end 49 | end -------------------------------------------------------------------------------- /test/integration/users_profile_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UsersProfileTest < ActionDispatch::IntegrationTest 4 | include ApplicationHelper 5 | 6 | def setup 7 | @user = users(:michael) 8 | end 9 | 10 | test "profile display" do 11 | get user_path(@user) 12 | assert_template 'users/show' 13 | assert_select 'title', full_title(@user.name) 14 | assert_select 'h1', text: @user.name 15 | assert_select 'h1>img.gravatar' 16 | assert_match @user.microposts.count.to_s, response.body 17 | assert_select 'div.pagination' 18 | @user.microposts.paginate(page: 1).each do |micropost| 19 | assert_match micropost.content, response.body 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/integration/users_signup_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UsersSignupTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | ActionMailer::Base.deliveries.clear 7 | end 8 | 9 | test "invalid signup information" do 10 | get signup_path 11 | assert_no_difference 'User.count' do 12 | post users_path, params: { user: { name: "", 13 | email: "user@invalid", 14 | password: "foo", 15 | password_confirmation: "bar" } } 16 | end 17 | assert_template 'users/new' 18 | assert_select 'div#error_explanation' 19 | assert_select 'div.field_with_errors' 20 | end 21 | 22 | test "valid signup information with account activation" do 23 | get signup_path 24 | assert_difference 'User.count', 1 do 25 | post users_path, params: { user: { name: "Example User", 26 | email: "user@example.com", 27 | password: "password", 28 | password_confirmation: "password" } } 29 | end 30 | assert_equal 1, ActionMailer::Base.deliveries.size 31 | user = assigns(:user) 32 | assert_not user.activated? 33 | # Try to log in before activation. 34 | log_in_as(user) 35 | assert_not is_logged_in? 36 | # Invalid activation token 37 | get edit_account_activation_path("invalid token", email: user.email) 38 | assert_not is_logged_in? 39 | # Valid token, wrong email 40 | get edit_account_activation_path(user.activation_token, email: 'wrong') 41 | assert_not is_logged_in? 42 | # Valid activation token 43 | get edit_account_activation_path(user.activation_token, email: user.email) 44 | assert user.reload.activated? 45 | follow_redirect! 46 | assert_template 'users/show' 47 | assert is_logged_in? 48 | end 49 | end -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/test/mailers/.keep -------------------------------------------------------------------------------- /test/mailers/previews/user_mailer_preview.rb: -------------------------------------------------------------------------------- 1 | # Preview all emails at http://localhost:3000/rails/mailers/user_mailer 2 | class UserMailerPreview < ActionMailer::Preview 3 | 4 | # Preview this email at http://localhost:3000/rails/mailers/user_mailer/account_activation 5 | def account_activation 6 | user = User.first 7 | user.activation_token = User.new_token 8 | UserMailer.account_activation(user) 9 | end 10 | 11 | # Preview this email at http://localhost:3000/rails/mailers/user_mailer/password_reset 12 | def password_reset 13 | user = User.first 14 | user.reset_token = User.new_token 15 | UserMailer.password_reset(user) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/mailers/user_mailer_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UserMailerTest < ActionMailer::TestCase 4 | 5 | test "account_activation" do 6 | user = users(:michael) 7 | user.activation_token = User.new_token 8 | mail = UserMailer.account_activation(user) 9 | assert_equal "Account activation", mail.subject 10 | assert_equal [user.email], mail.to 11 | assert_equal ["noreply@example.com"], mail.from 12 | assert_match user.name, mail.body.encoded 13 | assert_match user.activation_token, mail.body.encoded 14 | assert_match CGI.escape(user.email), mail.body.encoded 15 | end 16 | 17 | test "password_reset" do 18 | user = users(:michael) 19 | user.reset_token = User.new_token 20 | mail = UserMailer.password_reset(user) 21 | assert_equal "Password reset", mail.subject 22 | assert_equal [user.email], mail.to 23 | assert_equal ["noreply@example.com"], mail.from 24 | assert_match user.reset_token, mail.body.encoded 25 | assert_match CGI.escape(user.email), mail.body.encoded 26 | end 27 | end -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/test/models/.keep -------------------------------------------------------------------------------- /test/models/micropost_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: microposts 4 | # 5 | # id :integer not null, primary key 6 | # content :text 7 | # picture :string 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # user_id :integer 11 | # 12 | # Indexes 13 | # 14 | # index_microposts_on_user_id (user_id) 15 | # 16 | 17 | require 'test_helper' 18 | 19 | class MicropostTest < ActiveSupport::TestCase 20 | 21 | def setup 22 | @user = users(:michael) 23 | # This code is not idiomatically correct. 24 | @micropost = @user.microposts.build(content: "Lorem ipsum") 25 | end 26 | 27 | test "should be valid" do 28 | assert @micropost.valid? 29 | end 30 | 31 | test "user id should be present" do 32 | @micropost.user_id = nil 33 | assert_not @micropost.valid? 34 | end 35 | 36 | test "content should be present" do 37 | @micropost.content = " " 38 | assert_not @micropost.valid? 39 | end 40 | 41 | test "content should be at most 140 characters" do 42 | @micropost.content = "a" * 141 43 | assert_not @micropost.valid? 44 | end 45 | 46 | test "order should be most recent first" do 47 | assert_equal microposts(:most_recent), Micropost.first 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /test/models/relationship_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: relationships 4 | # 5 | # id :integer not null, primary key 6 | # created_at :datetime not null 7 | # updated_at :datetime not null 8 | # followed_id :integer 9 | # follower_id :integer 10 | # 11 | # Indexes 12 | # 13 | # index_relationships_on_followed_id (followed_id) 14 | # index_relationships_on_follower_id (follower_id) 15 | # index_relationships_on_follower_id_and_followed_id (follower_id,followed_id) UNIQUE 16 | # 17 | 18 | require 'test_helper' 19 | 20 | class RelationshipTest < ActiveSupport::TestCase 21 | 22 | def setup 23 | @relationship = Relationship.new(follower_id: users(:michael).id, 24 | followed_id: users(:archer).id) 25 | end 26 | 27 | test "should be valid" do 28 | @relationship.valid? 29 | assert @relationship.valid? 30 | end 31 | 32 | test "should require a follower_id" do 33 | @relationship.follower_id = nil 34 | assert_not @relationship.valid? 35 | end 36 | 37 | test "should require a followed_id" do 38 | @relationship.followed_id = nil 39 | assert_not @relationship.valid? 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require File.expand_path('../../config/environment', __FILE__) 3 | require 'rails/test_help' 4 | require "minitest/reporters" 5 | Minitest::Reporters.use! 6 | 7 | class ActiveSupport::TestCase 8 | fixtures :all 9 | 10 | # Returns true if a test user is logged in. 11 | def is_logged_in? 12 | !session[:user_id].nil? 13 | end 14 | 15 | # Log in as a particular user. 16 | def log_in_as(user) 17 | session[:user_id] = user.id 18 | end 19 | end 20 | 21 | class ActionDispatch::IntegrationTest 22 | 23 | # Log in as a particular user. 24 | def log_in_as(user, password: 'password', remember_me: '1') 25 | post login_path, params: { session: { email: user.email, 26 | password: password, 27 | remember_me: remember_me } } 28 | end 29 | end -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/tmp/.keep -------------------------------------------------------------------------------- /vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/vendor/assets/javascripts/.keep -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vasilakisfil/rails5_api_tutorial/e4e4ea7a847a1268d49168fdec68d47d3707ad43/vendor/assets/stylesheets/.keep --------------------------------------------------------------------------------