Skip to content

Instantly share code, notes, and snippets.

@jendiamond
Last active April 13, 2016 00:59
Show Gist options
  • Select an option

  • Save jendiamond/3c2dcc1ddb0c652dacce031719185317 to your computer and use it in GitHub Desktop.

Select an option

Save jendiamond/3c2dcc1ddb0c652dacce031719185317 to your computer and use it in GitHub Desktop.
sarcastic_messages_tutorial

Jen Diamond

  • GET api/v1/users (index)
  • POST api/v1/users (create)
  • PUT api/v1/users (update)
  • GET api/v1/users/:id (show)

Don't forget tests :)

===

Steps:

Update routes

config/routes.rb

Rails.application.routes.draw do
  get 'api/v1/ping' => 'main#ping'
  resources :users, only: [:index, :create, :update, :show], constraints: {format: :json}
end

Add User Serializer

app/serializers/user_serializer.rb

class UserSerializer < ActiveModel::Serializer
  attributes :id, :email, :created_at, :updated_at
end

Add actions to the users_controller

app/controllers/api/v1/users_controller.rb

class UsersController < ApplicationController
  before_action :find_user, only: [:show, :update]

  def index
    users = User.all
  end

  def show
    render json: user
  end

  def create
    user = User.new(user_params)
    if user.save
      render json: user, status: 201
    else
      render json: { errors: user.errors }, status: 422
    end
  end

  def update
    if user.update(user_params)
      render json: user, status: 200
    else
      render json: { errors: user.errors }, status: 422
    end
  end

  private

  def find_user
    user = User.find(params[:id])
  end

  def user_params
    params.permit(:email,:id)
  end

end

Richard Cheng

  • Rails 5
  • Ruby 2.3.0
  • No database for now, we'll install / hookup Redis in a separate ticket
  • Make one route, api/v1/ping, that gives a 200 response, and this response
    { pong: true }
  • This is a JSON API. Make sure you take out all the parts of Rails that we don't need: views and view-related code, JS and js - related code.
  • Push your code to the repo

All the files for the ticket above

Step 1. In console/terminal: $ rails new sarcastic_messages -T -O

-T omits generating minitest. this is also useful when you want to setup a project to use rspec instead of minitest. just generate the project without any tests and then add rspec in. -O omits generating the project with a database.

Now I didn't use --api because I didn't have rails 5 installed yet. I looked at the instructions and thought it was a bit hard. So I figure I'll do it the old school way and then add rails 5 in the gemfile later.

Step 2. Start removing stuff.

Step 2a. In the app folder, remove the following folders and their files:
  • assets
  • helpers
  • mailers
  • views

Add a folder called serializers.

So in the app folder you should only have 3 folders:

  • controllers
  • models
  • serializers.

And within those 3 folders, remove the concerns folders

Step 2b. in /bin/setup remove the following lines:
  puts "\n== Preparing database =="
  system "bin/rake db:setup"

since there's no database

Step 2c. in /config/environments/development.rb

remove config for active_mailer, and all the asset stuff so you only have the following:

Rails.application.configure do
  # Settings specified here will take precedence over those in config/application.rb.

  # In the development environment your application's code is reloaded on
  # every request. This slows down response time but is perfect for development
  # since you don't have to restart the web server when you make code changes.
  config.cache_classes = false

  # Do not eager load code on boot.
  config.eager_load = false

  # Show full error reports and disable caching.
  config.consider_all_requests_local       = true
  config.action_controller.perform_caching = false

  # Print deprecation notices to the Rails logger.
  config.active_support.deprecation = :log
end
Step 2d. in /config/environments/production.rb

same thing as above, remove any config.asset stuff:

  # Compress JavaScripts and CSS.
  config.assets.js_compressor = :uglifier
  # config.assets.css_compressor = :sass

  # Do not fallback to assets pipeline if a precompiled asset is missed.
  config.assets.compile = false

  # Asset digests allow you to set far-future HTTP expiration dates on all assets,
  # yet still be able to expire them through the digest params.
  config.assets.digest = true
Step 2e. in /config/environments/test.rb

Same again, remove config_action_mailer:

  # Tell Action Mailer not to deliver emails to the real world.
  # The :test delivery method accumulates sent emails in the
  # ActionMailer::Base.deliveries array.
  config.action_mailer.delivery_method = :test
Step 2f. in /config/initializers

Leave the following files and delete everything else.

  • filter_parameter_logging.rb
  • session_store.rb
  • wrap_parameters.rb
Step 2g. in /config/application.rb

Remove the following lines:

  • require "action_mailer/railtie"
  • require "action_view/railtie"
  • require "sprockets/railtie"

That's all for file removal.

===

Step 3. setup the gemfile like the following:

source 'https://rubygems.org'

ruby "2.3.0"

gem 'rails', github: 'rails/rails'
gem "active_model_serializers", github: "rails-api/active_model_serializers"

group :development, :test do
  gem 'byebug'
  gem 'spring'
end

group :production do
end

Note the way I setup rails is by calling through the github location of the edge version.

Step 4. $ bundle install

Step 5. Quick rails version verification. run $ rails s and see the rails version launched by the web server.

Step 6. routes. setup config/routes.rb like this:

Rails.application.routes.draw do
  get 'api/v1/ping' => 'main#ping'
end

Step 7. Add the main controller: $ rails generate controller main --no-helper --no-assets

When i originally did this i didnt use those 2 flags, so i had to manually delete folders again. Doing it like this is much cleaner.

Step 8. In the newly created main_controller.rb add this within the class:

  def ping
    render json: {
      data: [{
        type: "responses",
        attributes: {
          pong: true
        }
      }]
    }
  end

Step 9. Launch the rails server and test it. Either via browser or command line:

Step 9a from browser, go to http://localhost:3000/api/v1/ping

you should see this:

{"data":[{"type":"responses","attributes":{"pong":true}}]}

Step 9b from commandline, type curl http://localhost:3000/api/v1/ping

and you should see the same message come back.



[Add Rspec](https://github.com/zipmark/rspec_api_documentation gem)

Write a test for the ping endpoint such that it outputs JSON documentation using the https://github.com/zipmark/rspec_api_documentation gem


Gemfile

source 'https://rubygems.org'

ruby "2.3.0"

gem 'rails', github: 'rails/rails'
gem "active_model_serializers", github: "rails-api/active_model_serializers"

group :development, :test do
  gem 'byebug'
  gem 'spring'
+ gem 'rspec-rails', '~> 3.0'
end

group :production do
end

``


``


``


``


``


``


``


.gitgnore

/.bundle

# Ignore all logfiles and tempfiles.
/log/*
!/log/.keep
/tmp

===

Gemfile

source 'https://rubygems.org'

ruby "2.3.0"

gem 'rails', github: 'rails/rails'
gem "active_model_serializers", github: "rails-api/active_model_serializers"

group :development, :test do
  gem 'byebug'
  gem 'spring'
end

group :production do
end

Rakefile

require File.expand_path('../config/application', __FILE__)

Rails.application.load_tasks

app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
end

app/controllers/main_controller.rb

class MainController < ApplicationController
  def ping
    render json: {
      data: [{
        type: "responses",
        attributes: {
          pong: true
        }
      }]
    }
  end
end

bin/bundle

#!/usr/bin/env ruby
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
load Gem.bin_path('bundler', 'bundle')

bin/rails

#!/usr/bin/env ruby
begin
  load File.expand_path('../spring', __FILE__)
rescue LoadError => e
  raise unless e.message.include?('spring')
end
APP_PATH = File.expand_path('../../config/application', __FILE__)
require_relative '../config/boot'
require 'rails/commands'

bin/rake

#!/usr/bin/env ruby
begin
  load File.expand_path('../spring', __FILE__)
rescue LoadError => e
  raise unless e.message.include?('spring')
end
require_relative '../config/boot'
require 'rake'
Rake.application.run

bin/setup

#!/usr/bin/env ruby
require 'pathname'

# path to your application root.
APP_ROOT = Pathname.new File.expand_path('../../',  __FILE__)

Dir.chdir APP_ROOT do
  # This script is a starting point to setup your application.
  # Add necessary setup steps to this file:

  puts "== Installing dependencies =="
  system "gem install bundler --conservative"
  system "bundle check || bundle install"

  # puts "\n== Copying sample files =="
  # unless File.exist?("config/database.yml")
  #   system "cp config/database.yml.sample config/database.yml"
  # end

  puts "\n== Removing old logs and tempfiles =="
  system "rm -f log/*"
  system "rm -rf tmp/cache"

  puts "\n== Restarting application server =="
  system "touch tmp/restart.txt"
end

bin/spring

#!/usr/bin/env ruby

# This file loads spring without using Bundler, in order to be fast.
# It gets overwritten when you run the `spring binstub` command.

unless defined?(Spring)
  require 'rubygems'
  require 'bundler'

  if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^    (?:  )*spring \((.*?)\)$.*?^$/m))
    Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq.join(Gem.path_separator) }
    gem 'spring', match[1]
    require 'spring/binstub'
  end
end

config.ru

# This file is used by Rack-based servers to start the application.

require ::File.expand_path('../config/environment', __FILE__)
run Rails.application

config/application.rb

require File.expand_path('../boot', __FILE__)

require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
require "active_job/railtie"
# require "active_record/railtie"
require "action_controller/railtie"
# require "rails/test_unit/railtie"

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module SarcasticMessages
  class Application < Rails::Application
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.

    # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
    # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
    # config.time_zone = 'Central Time (US & Canada)'

    # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
    # config.i18n.default_locale = :de
  end
end

config/boot.rb

# Load the Rails application.
require File.expand_path('../application', __FILE__)

# Initialize the Rails application.
Rails.application.initialize!

config/environment.rb

# Load the Rails application.
require File.expand_path('../application', __FILE__)

# Initialize the Rails application.
Rails.application.initialize!

config/environments/development.rb


Rails.application.configure do
  # Settings specified here will take precedence over those in config/application.rb.

  # In the development environment your application's code is reloaded on
  # every request. This slows down response time but is perfect for development
  # since you don't have to restart the web server when you make code changes.
  config.cache_classes = false

  # Do not eager load code on boot.
  config.eager_load = false

  # Show full error reports and disable caching.
  config.consider_all_requests_local       = true
  config.action_controller.perform_caching = false

  # Print deprecation notices to the Rails logger.
  config.active_support.deprecation = :log
end

config/environments/production.rb

Rails.application.configure do
  # Settings specified here will take precedence over those in config/application.rb.

  # Code is not reloaded between requests.
  config.cache_classes = true

  # Eager load code on boot. This eager loads most of Rails and
  # your application in memory, allowing both threaded web servers
  # and those relying on copy on write to perform better.
  # Rake tasks automatically ignore this option for performance.
  config.eager_load = true

  # Full error reports are disabled and caching is turned on.
  config.consider_all_requests_local       = false
  config.action_controller.perform_caching = true

  # Enable Rack::Cache to put a simple HTTP cache in front of your application
  # Add `rack-cache` to your Gemfile before enabling this.
  # For large-scale production use, consider using a caching reverse proxy like
  # NGINX, varnish or squid.
  # config.action_dispatch.rack_cache = true

  # Disable serving static files from the `/public` folder by default since
  # Apache or NGINX already handles this.
  config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present?

  # Specifies the header that your server uses for sending files.
  # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
  # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX

  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
  # config.force_ssl = true

  # Use the lowest log level to ensure availability of diagnostic information
  # when problems arise.
  config.log_level = :debug

  # Prepend all log lines with the following tags.
  # config.log_tags = [ :subdomain, :uuid ]

  # Use a different logger for distributed setups.
  # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)

  # Use a different cache store in production.
  # config.cache_store = :mem_cache_store

  # Enable serving of images, stylesheets, and JavaScripts from an asset server.
  # config.action_controller.asset_host = 'http://assets.example.com'

  # Ignore bad email addresses and do not raise email delivery errors.
  # Set this to true and configure the email server for immediate delivery to raise delivery errors.
  # config.action_mailer.raise_delivery_errors = false

  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
  # the I18n.default_locale when a translation cannot be found).
  config.i18n.fallbacks = true

  # Send deprecation notices to registered listeners.
  config.active_support.deprecation = :notify

  # Use default logging formatter so that PID and timestamp are not suppressed.
  config.log_formatter = ::Logger::Formatter.new
end

config/environments/test.rb

Rails.application.configure do
  # Settings specified here will take precedence over those in config/application.rb.

  # The test environment is used exclusively to run your application's
  # test suite. You never need to work with it otherwise. Remember that
  # your test database is "scratch space" for the test suite and is wiped
  # and recreated between test runs. Don't rely on the data there!
  config.cache_classes = true

  # Do not eager load code on boot. This avoids loading your whole application
  # just for the purpose of running a single test. If you are using a tool that
  # preloads Rails for running tests, you may have to set it to true.
  config.eager_load = false

  # Configure static file server for tests with Cache-Control for performance.
  config.serve_static_files   = true
  config.static_cache_control = 'public, max-age=3600'

  # Show full error reports and disable caching.
  config.consider_all_requests_local       = true
  config.action_controller.perform_caching = false

  # Raise exceptions instead of rendering exception templates.
  config.action_dispatch.show_exceptions = false

  # Disable request forgery protection in test environment.
  config.action_controller.allow_forgery_protection = false

  # Randomize the order test cases are executed.
  config.active_support.test_order = :random

  # Print deprecation notices to the stderr.
  config.active_support.deprecation = :stderr

  # Raises error for missing translations
  # config.action_view.raise_on_missing_translations = true
end

config/initializers/filter_parameter_logging.rb

# Be sure to restart your server when you modify this file.

# Configure sensitive parameters which will be filtered from the log file.
Rails.application.config.filter_parameters += [:password]

config/initializers/filter_parameter_logging.rb

# Be sure to restart your server when you modify this file.

# Configure sensitive parameters which will be filtered from the log file.
Rails.application.config.filter_parameters += [:password]

config/initializers/session_store.rb

# Be sure to restart your server when you modify this file.

Rails.application.config.session_store :cookie_store, key: '_sarcastic_messages_session'

config/initializers/wrap_parameters.rb


# Be sure to restart your server when you modify this file.

# This file contains settings for ActionController::ParamsWrapper which
# is enabled by default.

# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
ActiveSupport.on_load(:action_controller) do
  wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
end

config/locales/en.yml

# Files in the config/locales directory are used for internationalization
# and are automatically loaded by Rails. If you want to use locales other
# than English, add the necessary files in this directory.
#
# To use the locales, use `I18n.t`:
#
#     I18n.t 'hello'
#
# In views, this is aliased to just `t`:
#
#     <%= t('hello') %>
#
# To use a different locale, set it with `I18n.locale`:
#
#     I18n.locale = :es
#
# This would use the information in config/locales/es.yml.
#
# To learn more, please read the Rails Internationalization guide
# available at http://guides.rubyonrails.org/i18n.html.

en:
  hello: "Hello world"

config/routes.rb

Rails.application.routes.draw do
  get 'api/v1/ping' => 'main#ping'
end

config/secrets.yml

# Be sure to restart your server when you modify this file.

# Your secret key is used for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!

# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
# You can use `rake secret` to generate a secure secret key.

# Make sure the secrets in this file are kept private
# if you're sharing your code publicly.

development:
  secret_key_base: 83beac9e9449d5f16a63bc57561ea914aa987e8daa46de4e0eed7f25d906d62c3825da4841cc9718de718b6ec48c131694df0a53ae31cc88f1429f4c77ea702b

test:
  secret_key_base: 60c49a0bc1cbc8b1eeab4ce7192d3c6b1f42cf12caf6bb4c600c000061d009be7a7c6bcaf96a7dfc91fa975df5194a60d1c9d95f69e6003ef910e0a3abbf8939

# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

Redis

Redis is a key-value store has built-in support for data structures like lists, sets, and hashes, and can persist data to disk. As such, it is quite useful as both a cache store and as a full-fledged, NoSQL data store.

http://tutorials.jumpstartlab.com/topics/performance/installing_redis.html

Why use a NoSQL database

You might have a relational database that you want to keep traffic away from. You can use NoSQL as a caching layer and write code to pull data from the relational database and store it in NoSQL, then you can use NoSQL for your API.

Some NoSQL databases allow you to serve full web applications. Your HTML, stylesheets, and JavaScript can be served directly from NoSQL, then you can use the permissions in NoSQL to control who can read and write data. NoSQL databases can be used to build new applications from scratch as well as augment the capabilities of a relational database. Even if you're working with an existing application, you can start using NoSQL databases now to take advantage of their features.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment