モジュールについて

モジュールの定義

 module Greet
   def hello
      'hello'
   end
 end

クラス定義によく似ています。しかし、モジュールはクラスと異なる点があります。

  • モジュールからインスタンスを作成することはできない

  • 他のモジュールからクラスを継承することはできない

 name = Name.new # => モジュールのインスタンスは作成できない
 
module CategoryName < Name # => 他のモジュールを継承して新しいモジュールを作ることはできない
end


include

 module Greet
   def hello
      'hello'
   end
 end

 class User
    include Greet
 end 

 user = User.new

 puts user.hello # => hello

モジュールに定義したhelloメソッドをUserクラスで呼び出すことができます。このモジュールがあれば、他のクラスでも継承関係を気にすることなくログ出力を持つことができます。

このようにモジュールをクラスにincludeして機能を追加することをミックスインと言います。

また、1つのクラスに複数のモジュールをミックスインすることもできます。

  module Greet
   def hello
      'hello'
   end
 end

 class User
    include Greet
 end 

 puts user.hello # => クラスメソッドとして呼び出せない!

クラスにモジュールをincludeした場合は、クラスで呼び出せるのはモジュールのインスタンスメソッドで、includeしたクラスのインスタンスメソッドとして呼び出すことが出来ます。



extend

extendを使うと、モジュール内のメソッドをそのクラスのクラスメソッドにすることができます。

   module Greet
   def hello
      'hello'
   end
 end

 class User
    extend Greet
 end 

 puts user.hello # => hello



名前空間を分けて名前の衝突を防ぐ

 module User
   class Greeting
     def hello
        'hello'
     end
   end
 end

  module Author
   class Greeting
     def hello
        'こんにちは'
     end
   end
 end

モジュール構文の中にクラス定義を書くと「そのモジュールに属するクラス」という意味になるため、同名クラスがあっても外側のモジュール名さえ異なっていれば名前の衝突は発生しなくなります。

 User::Greeting.new

Author::Greeting.new

URIについて

このようなURIがあったとします

 http://yohei:pass@blog.example.jp:8000/serch?q=test&debug=true"n10

このURIは次のように分けられます。

  • URIスキーム: http リソースにアクセスする通信手段を表す文字列
  • ユーザー情報: yohei:pass
  • ホスト名: blog.example.jp
  • ポート番号: 8000
  • パス: 8000
  • クエリパラメータ: q=test&debug=true
  • URIフラグメント: #n10

オーソリティ = ユーザー情報+ホスト+ポート番号

ユーザー情報は省略して記載することもできる。

ポート番号も代表的なもの(HTTP, HTTPSなど)であれば省略して記載できる。

よって、ホスト名だけでもアクセスすることができます。  


パス

階層的に管理されているリソースへの場所を表す(省略可)


クエリ

パスに加えて付加的な情報をサーバーに送信するために使う。

パスの後ろに区切り文字である「?」が付き、名前=値形式のクエリが続きます。

この例ではq=testとdebug=trueがそれぞれクエリです。クエリが複数あるときは「&」で連結します。この1つ以上のクエリの集合をクエリパラメータと呼びます。クエリパラメータは、例えば検索サービスに検索キーワードを渡すときなど、クライアントから動的にURIを生成するときに利用する。


フラグメント

リソースの一部を指定するために用いる

リソース内部のさらに細かい部分を特定する時に利用します。例えば、このリソースがHTML文書だった場合は、id属性の値が「n10」である要素を示すことになります。

JSON 基本的なこと

JSONとは、データを記述するための表記法のひとつ


そもそもデータって?

基本データ型

整数、少数、文字、真偽値


集合データ型

配列型、文字列型、クラスや構造体(Javascriptのclassなど) 複数のデータをまとめて一つのデータと扱うことでプログラムの表現力を向上させる。

Rubyは純粋オブジェクト指向型の言語と呼ばれている。Rubyにはプリミティブ型(基本データ型)というものが存在せず、全て複合データ型をとっています。


データを別の切り口から分けると2種類に分けられます。 * 言語定義型: 言語が初めから用意してくれている型 * ユーザー定義型: プログラムを書く側が定義した型


JSONの用途

JSONはユーザー定義の複合データ型を記述するために使われます。 いくつかのデータを組み合わせて新しいデータを作る。

具体例

人間をプログラムでデータとして表現しよう! 言語定義型の整数、少数、文字列、配列を使って表現することもできるが... 名前だけたは足りない、生年月日だけでもダメ。人間というものをデータで表現するには基本データ型では不十分である。

複合データ型の出番!

  • 名前
  • 生年月日
  • 年齢
  • 身長
  • 体重
  • 住所

人間は複数種類のデータを合わせて初めて一つの人間というデータになる。

こういう時に使えるのがJSON

 {
   "human": {
      "name": "田中",
   "birthday": "1995/06/02",
      "age": 30,
      "height": 170,
      "weight": 70,
      "address": "大阪府〇〇市"
   }
 }

JSONで使えるデータの種類は以下の6種類

オブジェクト、配列、数値、文字列、真偽値、null

JavaScriptのオブジェクトの書き方とほぼ一緒!

代表的なリクエストヘッダとレスポンスヘッダ

リクエストヘッダ

User-Agent

HTTPリクエストを送ったアプリケーション名を指定する。 ブラウザからWebサイトにアクセスした場合、ブラウザ名はバージョン番号が入る。(Google chrome, Safari ...)


Referer

HTTPリクエストを送信するときに滞在していたWebサイトのURLを指定する。要するにどこからサイトを訪れたのかをWebサーバーに送信するために使う。


Authorization

特定のクライアントに対してのみリソースを公開したい場合に認証情報を送信するために用いるヘッダ。Web APIの認証に使われます。


Accept

クライアントが自分の処理できるメディアタイプをサーバーに伝えることができます。



レスポンスヘッダ

Content-Type

本文のメッセージに格納したデータのファイル形式を指定するためのヘッダ。 サーバーがレスポンスする本文のファイル形式を指定する。 MIMEタイプという形式で指定する。 HTMLならtest/heml CSSならtext/css JPEG画像ならimage/jpeg


Content-Length

レスポンスに格納されている本文の長さを指定するためのヘッダ。


Date

レスポンスを返した日時を返す。

GETとPOSTはどのように取得するの?

GET

GETリクエストの内容は URLに含まれ、クエリパラメータと言います。

例: https://runteq.jp/pages/33?page_category=document

--> ?page_category=document がクエリパラメータに該当します。

クエリパラメータは、例えば検索サービスに検索キーワードを渡すときなど、クライアントから動的にURIを生成する時に利用します。


POST

POSTの内容はURLには含まれず、リクエスボディに含まれます。

POSTリクエストの内容はリクエストボディに含まれ、HTTPS通信の場合は内容は暗号化され、他人が見ることは出来ません。

ログインなどパスワードを送る場合は、パスワードを他人に見られない様にするためにPOSTメソッドを使うことになります。

Rakeタスク Cron Whenever 一時間ごとに表示させる

Rakeタスク

まずRakeとは、rubyで処理内容を定義できるビルドツールです。 そしてこのRakeが実行する処理内容を「Rakeタスク」と呼び、定義する場所を「Rakefile」と呼びます。 whenneverと組み合わせることにより、自由なタイミングで処理を走らせることができます。


タスク

タスクとは、テストの実行やデータベースの更新などのタスクを実行してくれます。Rakeなしでこのような小さなタスクを実行しようと思うと、違うファイルを行き来してしまい、コードが散らかります。


cron

「クロン」または「クーロン」と呼ばれ、「○時になったら○○のコマンドを実行」などといった具合に、定期的にコマンドを実行するためにメモリ場で常に命令を待機しているプロセス(=デーモンプロセス)となります。


ファイル作成

lib/tasks以下ディレクトリにrakeファイルを作ります。

rails g task _article_state

※間違いで_article_stateと入力してしまいました。正しくはarticle_state
rakeタスクに実装していく。

_article_state.rake

 namespace :article_state do
   desc '公開待ちの中で、公開日時が過去のもの:ステータスを「公開」に変更する'
   task update_article_state: :environment do
     Article.publish_wait.past_published.find_each(&:published!)
   end
 end

task_nameがタスクの名前になります。どういう処理を行うタスクなのか分かりやすい名前をつけてあげると、後々タスクが増えた時にも混乱しなくて済みます。 DBとのやり取りがある処理の場合「:environment」を記載する必要がある。 Articleから「公開待ち」の状態で公開日時が現在〜過去のものを取ってきてから、find_eachでループさせます。 必要なデータだけ先に抽出してから繰り返し処理をしています。 (&:メソッド名)でpublished!を実行しています。 Rubyチェリー本P98に詳細の説明あり。


公開日時が現在〜過去の記事を取得するscopeを準備 article.rb

scope :past_published, -> { published.where('published_at <= ?', Time.current) }


Rakeタスクの実行

Rakeタスクの一覧を表示して登録されているか確認します。

以下のコマンドを入力することで、Rakeタスクの一覧を表示してくれます。以下は同じ出力結果になります。

bundle exec rake --tasks
bundle exec rake -T

Image from Gyazo

bundle exec rake _article_status:manage_status


Wheneverの導入

Wheneverはcron jobを実行してくれます。つまり、cronをruby文法で実行してくれるライブラリーです。 本来は「○○時になったら○○を実行」するcronというプロセスを自前で用意する必要があるのですが、wheneverを使えばcronに対して命令を行うcrontabに記述する内容をruby言語で書けるようになります。 これによりRakeタスクを1時間ごとに走らせるように設定することが可能です。

gem 'whenever', require: false
% bundle exec wheneverize .

schedule.rbファイル内にcron処理の設定を書き込む。 schedule.rb

 # Rails.rootを使用するために必要
 require File.expand_path(File.dirname(__FILE__) + '/environment')
 # cronを実行する環境変数
 rails_env = ENV['RAILS_ENV'] || :development
 # cronを実行する環境変数をセット
 set :environment, rails_env
 # cronのログの吐き出し場所
 set :output, "#{Rails.root}/log/cron.log"
 # 1時間ごとにrakeタスクを実行
 every :hour do
   rake 'article_state:update_article_state'
 end

公開日時を1時間ごとにしか設定できないようにする。 app/assets/javascripts/admin.js

format: 'YYYY-MM-DD HH:00'


Cronをアップデートする

$ bundle exec whenever --update-crontab

crontab -lで現在設定されているタスクの一覧が表示されます。

Railsのモデルのscope

scopeとは

スコープを設定することで、絞り込み条件、すなわちクエリー用のメソッドの連続した呼び出し部分をまとめて名前をつけ、カスタムのクエリー用メソッドとして使うことができます。

複雑なSQLを何度も書かなくてよくなります。繰り返し利用される絞り込み条件をスッキリ読みやすくでき、保守性に優れて、レビュアーにとっても読みやすくなります。

同じことがcontrollerでもできるのですが『Skinny Controller, Fat Model(コントローラーのコード量を少なくして、モデルを分厚くする)』という書き方が推奨されています。 このような背景から、ファットモデルという設計が目指すべき設計ということになります。


scopeを使った例

Railsガイドの例を使って説明していきます。

クラスの内部でscopeメソッドを使用し、スコープが呼び出されたときに実行して欲しいクエリをそこで渡します。

 class Article < ApplicationRecord
   scope :published, -> { where(published: true) }
 end


scopeはクラスメソッドの定義と完全に一致しています。どちらの形式を使用しても構いません。

 class Article < ApplicationRecord
   def self.published
     where(published: true)
   end
 end


scope内でチェインすることも可能です。

 class Article < ApplicationRecord
   scope :published,               -> { where(published: true) }
   scope :published_and_commented, -> { published.where("comments_count > 0") }
 end


scopeを使った呼び出し

 category = Category.first
 category.articles.published # => [このカテゴリに属する、公開済みの記事]



引数を受け取ったscope

article.rb

 scope :by_category, ->(category_id) { where(category_id: category_id) }

search_articles_form.rb

 relation = relation.by_category(category_id) if category_id.present?

このように書いておけば、選択したカテゴリーに該当する記事を検索できるようになる。


 scope :title_contain, ->(word) { where('title LIKE ?', "%#{word}%") }
 title_words.each do |word|
   relation = relation.title_contain(word)
 end

  private

  # タイトルの検索内容を配列に入れている。
 def title_words
   title.present? ? title.split(nil) : []
 end

入力内容に該当する記事を検索できるようになる

URL

railsguides.jp