Fullstack App

Building and deploying a full application with Ruby on Rails. This is a quick library of city sounds that users can upload and view, for listening pleasure during a time of global isolation.

Users can login using social media – Twitter, Facebook, Google, and Github – and then create a simple profile. They can upload sounds and contribute to a library.

Sounds

rails generate scaffold Sounds location:string description:string name:string user:references
rails generate migration UsersSounds
# migration

def change
  create_table :users_sounds do |t|
    t.belongs_to :user, index: true
    t.belongs_to :sound, index: true
    t.timestamps
  end
end
# model

class Sound < ApplicationRecord
  has_one_attached :thumbnail
  has_many_attached :sound_clips
  belongs_to :user

  validates :sound_clips, presence: true, blob: {content_type: [ 'audio/mpeg', 'audio/x-mpeg', 'audio/mp3', 'audio/x-mp3', 'audio/mpeg3', 'audio/x-mpeg3', 'audio/mpg', 'audio/x-mpg', 'audio/x-mpegaudio' ]}
end
# controller

# GET /sounds/new
def new
  @user = current_user
  @sound = @user.sounds.build
end

def create
  @user = current_user
  @sound = @user.sounds.build(sound_params)

  respond_to do |format|
    if @sound.save
      format.html { redirect_to @sound, notice: 'Sound was successfully created.' }
      format.json { render :show, status: :created, location: @sound }
    else
      format.html { render :new }
      format.json { render json: @sound.errors, status: :unprocessable_entity }
    end
  end
end

Categories

rails generate model category
rails generate model soundscategories
# migration

def change
  create_table :sounds_categories do |t|
    t.belongs_to :sound, index: true
    t.belongs_to :category, index: true
    t.timestamps
  end
end
# models/sound.rb

has_many :sounds_categories
has_many :categories, through: :sounds_categories
# models/category.rb

has_many :sounds_categories
has_many :sounds, through: :sounds_categories
# models/soundscategory.rb

Coords

Create a migration to add optional coords to the sound object so that we can geocode and save easily:

def change
  change_table :sounds do |t|
    t.column :lat, :float, optional: true, :default => "None"
    t.column :lng, :float, optional: true, :default => "None"
  end
end

Mapbox

It’s easy to build a tool to serve geojson and display in mapbox. We can create a new controller, and then set a route for it.

yarn add mapbox mapbox-gl leaflet
rails g controller geojson index
class GeojsonController < ApplicationController
  def index
    @sounds = Sound.all
    @geojson = Array.new

    @sounds.each do |sound|

      # To save geocoding usage, check
      # If the record doesn't have coords already:
      if sound.lat == 0.0
        results = Geocoder.search(sound.location)

        sound.lat = results.first.coordinates[0]
        sound.lng = results.first.coordinates[1]
        sound.save
      end


      @geojson << {
        type: 'Feature',
        geometry: {
          type: 'Point',
          coordinates: [sound.lng, sound.lat]
        },
        properties: {
          name: sound.name,
          location: sound.location,
          description: sound.description,
          'marker-color': '#00607d',
          'marker-symbol': 'circle',
          'marker-size': 'large'
        }
      }
    end

    @json = { "type": 'FeatureCollection', "features": @geojson }
    render json: @json
  end
end

Create a new route and map it to the geojson controller

get 'geojson', to: 'geojson#index'