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.
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
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
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
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'