一緒にスパ、Webアプリを構築するパッケージの全体の束、反応、レールを入れて


年にソフトウェア工学を研究した後Flatiron School , それは最終的に私の最終的なプロジェクトのための時間です.このプロジェクトは、レールのバックエンドと反応フロントエンドを使用してコースを通して学んだすべてを強調することになっています.そして、このプロジェクトが私のブートキャンプの頂点であるので、私は人として私が誰であるかを示すだけでなく、現実の問題を解決する何かを作成したかったです.
私は自分のポートフォリオについて理解しやすいかどうかを考えたかった.私はこれとワークアウトアプリケーションの間を行ったり来たりして、これについて決めました.なぜなら、私は本当に問題解決に参加することができて、何度も行われたプロジェクトを再ハッシュするだけではないからです.私の心配は、電子音楽が複雑すぎて誰かがすぐに理解できるようになったからです.しかし、私は、私が簡素化することができて、これらの複雑な問題をunderstrandして、使いやすいようにすることができたという確信を持ちました.
私は電子音楽が大好きです.ハウス、テクノ、プログレッシブ、“EDM”アンブレラの下のすべての小さなサブジャンル.私はインターネット上のDJの多くに従って、ミックスミックスのストリームの時間.私は自分自身が常に私のお気に入りのDJの再生トラックを識別しようとして下さい.通常、私はシャザムとSoundHoundのようなツールを使用しようとしますが、彼らは悪名高い家の音楽を識別するのはひどいです.それは私が曲の提言やアーティストチャートのためのインターネットを検索し、私はトラックに実行を期待して残します.もう少し複雑にするために、多くのDJがリリースされていないトラックを再生し、オンラインで見つけることはほとんど不可能になる.
この問題を解決するために、私はオンローテーションを作成しました-電子音楽のファンが電子音楽を特定して、彼らの大好きなトラックが特定されたとき、通知を受け取るために協力することができるSPAウェブアプリ.


機能
  • ユーザーログイン
  • トラックリスト、トラックラックトラック、アーティスト、ラベルを追加
  • YouTubeのビデオを入力してください
  • 未知のトラックの識別提案を入力してください
  • 他のユーザーによって提出された
  • ブックマークの追跡は、正しい識別が承認されている通知を受信する

  • プロジェクトアプローチ
    コードを書く前に、最終的な製品を思い出そうとしました.と尋ねた.
  • どのようなアプリを見て、振る舞うだろうか?
  • どのように、私はわかりやすい方法でユーザーにデータを提示することができますか?
  • 電子音楽の性質を考えると、どのように処理し、行方不明のデータを検証する必要がありますか?
  • どのような機能は、パブリックユーザー対に署名されているユーザーに利用できる必要がありますか?
  • どのような機能は、最小生存可能な製品(MVP)の一部と見なされませんか?
  • 私はノートでは、プロジェクトの描画を設計し、どのように機能を動作し、見て洗練された.私はノートを作り、アイコンと再利用可能なコンポーネントにアイデアを引き出した.それから、それがどのように見えるか、Adobe XDで機能するワイヤーフレームを作りました.私はいくつかの日のアプリのフリースペースを起草し、データを提示するさまざまな方法をブレーンストーミング.これは、私は正確にどのようにデータがお互いに話をする、特にアプリのコア機能の一部が不足しているデータに充填されているのを理解する助けた.私はバックエンドを作成したので、私はボタンが動作する方法の適切な名前を持っているように使用したいいくつかのアイコンを再作成しました.たとえば、ブックマークの代わりに、私はトラックを見るために「目」アイコンから始めました、しかし、それは十分に使用されるのに十分興奮していませんでした.私はそれから星または心臓について考えました、しかし、それは「誰かがこのトラックが何であるかについて理解するとき、私に知らせてください」というよりむしろ「類似」を意味するようでした私はそれが星でブックマークに定住したので、それが「お気に入り」であることを意味します、そして、また、「後に戻ってください」.


    バックエンド

    DBスキーマ
    それから、私はDrawioで私のスキーマを引き出して、データ型と妥当性と同様に要件を書きました.これは本当に、私が物事がどのように予定されるかについて考えるのを助けて、お互いに関係しています.私はモデルのモデル化と移行、モデル、建物関係、DB制約、モデル検証を始めました.私は、検証/制約と関係を確実にすることに取り組んでいる間、シードファイルを書きました.私は、すべてが働いていることを確認するために、しばらくこの段階にとどまりました.

    私は、よりわかりやすいコードを書くためにモデルとDB制約の両方にコラムリファレンス別名を使用することに決めました.私は、移行を開始しました{foreign_key: } ハッシュと{references: } ハッシュ.
    # /db/migrate/create_tracklists.rb
    
    class CreateTracklists < ActiveRecord::Migration[6.1]
      def change
        create_table :tracklists do |t|
          t.string :name, :null => false
          t.date :date_played, :null => false
          t.references :artist, :null => false, :foreign_key => true
          t.string :youtube_url
          t.references :creator, :references => :users, :null => false, :foreign_key => { :to_table => :users}
          t.timestamps
      end
    end
    
    また、ActiveRecord ::Baseは、同様のハッシュを渡すことによってエイリアスリレーショナルデータを知っている必要がありますbelongs_to メソッド.
    # /app/models/tracklsit.rb
    class Tracklist < ApplicationRecord
      belongs_to :creator, class_name: 'User'
    
      ...
    
    end
    
    もう一つの問題は、TrackListTracksが特定の順序で追跡リストから戻る必要があるということでしたが、SQLの構造では、結合テーブルを作成せずに、順序付けされた方法で格納された関係データを保持できません.この問題の解決策は、TrackListTrackをリンクリストとして構造化することで、前任者を参照する列を作成しました.カラム名を作成しましたpredessor_id それはid 前に来たTrackListTrackの.
    class CreateTracklistTracks < ActiveRecord::Migration[6.1]
      def change
        create_table :tracklist_tracks do |t|
          t.references :tracklist, :null => false, foreign_key: true
          t.references :track, :null => false, foreign_key: true
          t.time :cue_time
          t.integer :predessor_id, :unique => true
          t.references :identifier, references: :users, :null => false, foreign_key: { to_table: :users }
          t.timestamps
        end
      end
    end
    
    TrackListモデル内でループを使用し、デフォルトを上書きするbelongs_to メソッドは、順番にプルTrackListTrackを呼び出します.
    # /app/models/tracklist.rb
    
    class Tracklist < ApplicationRecord
      ...
    
      def tracks
        tracklist_tracks = self.tracklist_tracks.includes(:track)
        current_tracklist_track = tracklist_tracks.find { |tracklist_track| tracklist_track.predessor_id == nil}
    
        array_of_tracks = []
        order = 1
    
        loop do
          current_track = current_tracklist_track.track
          current_track.order = order
          order += 1
          array_of_tracks << current_track
          current_tracklist_track = tracklist_tracks.find { |tracklist_track| tracklist_track.predessor_id == current_tracklist_track.id}
    
          break if current_tracklist_track == nil
        end
    
        array_of_tracks
      end
    
    end
    

    データのシリアル化
    データをフロントエンドにシリアル化するためにactive_model_serializers , Netflixのサポートを中止して以来fast_jsonapi . Gemfileに追加した後、私はすぐに新しいserializersをrails g serializer <model_name> コンソールから.大きな特徴active_model_serializers そのコントローラーは自動的に一致するシリアライザーを/serializers ディレクトリを使用してシリアル化を適用します.もう一つの大きな特徴active_model_serializers 書くことができますかbelongs_to and has_many あなたのモデルの構造に合っているserializersの中の関係.
    ユーザーが受信する必要がある通知(BookMarketDacklistおよびBookMarketDackListTrack)の2種類があるので、私は通知シリアライザー内でカスタムデータのシリアル化を構築しました.この方法では、シリアライザはtrack への呼び出しのための属性BookmarkedTrack クラスを表示しますtracklist への呼び出しのための属性BookmarkedTracklistTrack クラス.を渡すことで条件属性を書くことができます{if: <instance_method>} メソッドが真の値を返す限り、属性または関係へのハッシュ.
    # /app/serializers/notification_serializer.rb
    
    class NotificationSerializer < ActiveModel::Serializer
      attributes :id, :updated_at, :has_unseen_updates
    
      belongs_to :track, serializer: TrackSerializer, if: :is_track?
      belongs_to :tracklist, if: :is_tracklist?
    
      def is_track?
        object.class == BookmarkedTrack
      end
    
      def is_tracklist?
        object.class == BookmarkedTracklist
      end
    
    end
    

    フロントエンド
    コンポーネントの構築を始めたので、コンポーネント、コンテナ、リダーズ、アクション、ページビューを別々にしたファイル構造を見つけました.少しの研究をした後に、私はすべてのReduxstore ディレクトリとすべてのページビューviews 逆襲.私はレイアウトコンポーネントをlayout ディレクトリをglobal 小さな機能コンポーネントのサブディレクトリは、すべてのアプリケーションを使用します.
    # .
    
    ├── README.md
    ├── public
    └── src
        ├── App.js
        ├── components
        ├── containers
        ├── index.js
        ├── layout
        │   ├── NavBar
        │   └── global
        ├── store
        │   ├── actions
        │   └── reducers
        └── views
            ├── Artist
            ├── Home.js
            ├── NotFound.js
            ├── Track
            └── Tracklist
    

    反応ルータの実現
    反応は、単一のページのアプリケーション内のすべてのコンポーネントを追加して削除し続けるので、ユーザーがすぐに手動で反応UIを使用してナビゲートせずに特定のページに移動することができます方法はありません.RESTのURLの錯覚を作成するには、Run Routernpm i react-router-dom シェルから.それからラップ<App> コンポーネント<Router> . そこから私は<Switch> and <Route> コンポーネントのルートをビルドします.を使用してrender prop、我々はルータによって提供される小道具を渡すことができます.このように、すべての子要素は、現在のパスを簡単に知ることができますid 特定のリソースの.
    // /src/App.js
    
    ...
    
      <Switch>
        <Route exact path="/" render={() => <Home />} />
        <Route exact path="/tracklists" render={(routerProps) => <TracklistIndex {...routerProps} />}/>
    
        ...
    
        <Redirect to="/404" />
      </Switch>
    
    ...
    
    
    を使用して<Redirect> コンポーネントは<Switch> コンポーネントは、404ページにユーザーを指示することができます.

    ReduxとThunkの追加
    私はアプリを構築したように、国家管理は問題になって始めた.ユーザーがログインしたかどうかを知るために必要なコンポーネントは、特定のコンポーネントで既に投票している場合、そのIDを作成した場合、そのIDがどのようなものであったか、その他の情報がページに表示されている場合.reduxを入力します.
    Reduxは、我々はすべてのコンポーネントの状態をすべてのコンポーネントの状態を自由にアプリケーション全体の状態を変更できるように、1つの中央の状態にすべてのコンポーネントの状態を移動することができますによって構築された反応パッケージです.

    使用combine-reducers , 私はいろいろな減速機を1つの中央店に動かすことができました.力の追加thunk 我々は、我々の中で非同期にフェッチ呼び出しを派遣することができますdispatch アクション.
    // src/store/reducers/index.js
    
    export default combineReducers({
      indexReducer,
      tracklistShowReducer,
      notificationReducer,
      sessionReducer,
    });
    
    // src/index.js
    
    import reducer from "./store/reducers/index";
    let store = createStore(reducer, composeWithDevTools(applyMiddleware(thunk)));
    
    

    スクリーンショット

    /


    /トラックリスト


    新しいリスト


    /トラックリスト/


    通知ドロップダウン


    提案軌道同定


    日付ピッカー