tixture55’s diary

主にプログラミング関係の日記です。

rails備忘録(mapとかfixtureなど)

railsの備忘録です。触ってて分かったこととか。

 

/config/application.rbはrails sのときに読まれる

railsは読み込みを早くするために、画面リロードでは読まれない設定があります。

/config/application.rbもそのひとつ。

 

予約語

deviseを入れてると、currrent_userという変数が使えます。あと、requestはユーザエージェントとかリファラの情報が入ってる。

 

@user = current_user

ようこそ!<%= @user.email %>  さん的に使える

 

mapを使った検索条件の記法

下記のように書くことで、commentsテーブルのcontent,name,email,site_urlカラムのsearchに入った文字列を含むレコードをすべて取得できる。こう書くことで、commentsテーブルのcontent,name,email,site_urlカラムのsearchに入った文字列を含むレコードをすべて取得できる。

 

if search.blank?  
all #rails3ならscopedと書いていた
else  
conditions = %w[content name email site_url].map { |c| "comments.#{c} like :search" }  where(conditions.join(" or "), :search => "%{search}%")               
end

 

test/fixtures/hoge.ymlのロード

予め、テストデータをhoge.ymlに書いておくと、データ壊れてもまたロードできて便利。でもdevelop環境のテーブルのデータ上書きされてしまうので注意。あと、主キーのidは1から書いていくこと。書かないとランダムな値が勝手に挿入されてテストデータとして使いモンにならない。

 

まとめ

学習のために作ってる本の販売サイト的なやつがあんまり詰まることなく、テーブルデータの抽出部分はなんとなく作れたのでやっぱRailsは使いやすいな印象です。

bookSearch/taskapp at master · tixture55/bookSearch · GitHub

railsのgemまとめ

railsの学習の家庭で、下記のgemをGemfileに追加しました。自分用の備忘録です。

  • kaminari
  • rails-footnotes

 

両方とも簡単に導入できるところがいいですね。

githubにこの2つのgemを盛り込んだ簡単な検索アプリを公開しました。

github.com

 

kaminariはいわずとしれたページネーション実装用のモジュールです。

実際のサービスではerbのCSSいじってもっと見栄えをよくしたいところ。下記ソースだけ(viewもすこしいじるけど)で3件ずつ表示させるようにできます。

 

class ProjectsController < AuthorizedController
PER = 3
def index
if params[:title] && params[:star]
@projects = Project.where(:title => params[:title]).page(params[:page]).per(PER)
end
end
end

 

rails-footnotesはデバッグ用モジュールです。

画面したの方に実行されたSQL、セッション、コントローラとアクション、読み込まれたjsやcssなどが表示できるリンク集が表示されます。cakeにもdebugToolというのがあったのですが、あれよりシンプルで使いやすいです。

 

まとめ

laravelやcakeよりシンプルでかつ細かくいろいろ設定できるrailsは癖があって初期段階でけっこう文法とかで苦戦しました。ActiveRecordもreadonlyかどうかやcastした上でupdateするかなど細かく方法を使い分けられるようなので、いろいろPHPフレームワークと違っていておもしろいですね。

 

参考サイト

[Ruby] - yk5656 diary

ActiveRecord の attribute 更新方法まとめ - Qiita

 

 

 

Railsの値取得とrailsコマンドを使ったDB接続

Railsを使ったアプリのソースコードを読んでいて戸惑ったこと、理解したことの雑記です。

 

突然viewファイルに登場するみたことないコロン付きのメソッド、どこから来ているのか調べました。(下記ソースでいうところの:titleや:content)

 

具体的には

①のソースを追加したところエラーになってしまったので(突然:contentというわけわからんパラメータ書いたので当たり前だけど)、:titleがエラーにならない理由はなんなのか調べました。

 

<%= form_for @project do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %><br><br>
 <%= f.text_area :content, :size => "20x10" %>#・・・①
<% if @project.errors.any? %>
<%= @project.errors.messages[:title][0] %>
<% end %>
</p>
<p>

<%= f.submit %>

DBのテーブルのカラム名と直接紐付いているのかなと思ったので、rails dbconsoleコマンドでテーブル定義を確認しました。

 

psgr_db=# \dt
List of relations
Schema | Name | Type | Owner
--------+-------------------+-------+----------
public | projects | table | postgres
public | schema_migrations | table | postgres
public | users | table | postgres
(3 rows)

projectsテーブルのテーブル定義は

 

psgr_db=# \d projects;

id | integer | not null default nextval('projects_id_seq'::regclass)
title | character varying(255) |
created_at | timestamp without time zone |
updated_at | timestamp without time zone |

titleフィールドがありました。contentフィールドを追加すると、①のソースコードを追加した状態でもエラーがでなくなりました。テーブルのフィールドに直接Railsがアクセスしてることが分かりました。

 

 

 

 

 

 

 

 

Railsでのモデルの書き方

Railsでのモデルの書き方の学習の記録です。

今回はアソシエーションとか複雑なクエリを何回も書かなくて済むスコープの書き方です。

 

railsのhas_manyの書き方

 

class Post < ActiveRecord::Base
 has_many :comments
end

class Comment < ActiveRecord::Base
 belong_to :post
end 

上が一番分かりやすい例だと思います。Postの下にCommentがぶら下がっている1対多の定義です。

 

dependentの定義

下記例だと、Episodeの下にcommentsとtaggingsとtagsがぶら下がっているイメージである。

 

class Episode < ActiveRecord::Base
 has_many :comments, :dependent => :destroy    
 has_many :taggings, :dependent => :destroy    
 has_many :tags, :through => :taggings
end

この場合、親に当たる投稿が削除されたら、コメントの扱いをどうするか、それを定義するのがdependentである。投稿が削除されたら、紐づくコメントも削除する場合は下記のように書く。

 

1. has_many :comments, :dependent => :destroy
2.  has_many :comments, :dependent => :delete_all

1の場合はComment::destroy()が毎回よばれるのに対し、2はdestroy()をスルーして1の場合はComment::destroy()が毎回よばれるのに対し、2はdestroy()をスルーして 削除するSQLを走らせる。
通常はdestroy()を使う

 

Railsのスコープ機能

 

モデルのスコープ機能とは、共通的に使うクエリをモデルのメソッドのように定義できる機能モデルのスコープ機能とは、共通的に使うクエリをモデルのメソッドのように定義できる機能 何度も複雑なSQLを何度も書かなくてよくなり、保守性が高まる。

 

#スコープメソッドで定義 
class Post < ActiveRecord::Base   
scope :published, -> { where(published: true) }               
end

上の定義をしたうえで

 

Post.published

とすると、publishedカラムがtrueのPostのみを取得できる。

 

スコープ同士のマージ

 

class User < ActiveRecord::Base    
scope :inactive, -> { where state: 'inactive' }    
scope :finished, -> { where state: 'finished' }
end

# 使用方法
User.inactive.finished
# => select users from users where state = "inactive" and state = "finished"

scopeを組み合わせれば複雑なSQLロジックを分離して考えることができる。

 

他のモデルのスコープを利用する方法

 

class Category < ActiveRecord::Base
has_many :posts

# スコープでjoinsを行い、Postクラスのrecentスコープを利用する方法
scope :with_posts, -> { joins(:posts).merge(Post.recent) }
end

class Post < ActiveRecord::Base
belongs_to :category
scope :recent, -> { where(created_at: Time.zone.now..3.days.ago) }
end

# 使い方
Category.with_posts

 これで内部結合postsテーブルと内部結合させるSQLが作れる。ロジックが分割されているので、仮にmodelクラスがもっと増えても、scopeの組み合わせでいろんなバリエーションのSQLを作ることができる。

 

 参考サイト

qiita.com

 

ruby-rails.hatenadiary.com

 

 

 

ruby on rails4のおさらい(MVC)

railsのおさらいです。私の環境では、railsは4.1.1でした。

 

controllerのaction定義の仕方

 

def new  
@project = Project.new
end

 

またbefore_actionを指定することで、showやindexアクションの前に特定のアクションをフックできます。

 

before_action :set_article 

def show
end

def edit
end

def set_article
@article = Article.find(params[:id])
end

 

privateキーワードを使う

rubyのprivateキーワードはJavaなどとは違い、親で指定したprivateなメソッドに子供クラスからアクセスできます。

 

private  
def undo_link
if can? :revert, :versions
version = @comment.versions.scoped.last
view_context.link_to("undo", revert_version_path(version), :method => :post) if can? :revert, version
end
end

 

 

Rails4のrequire,permit

rails4ではストロングパラメータというメソッドが追加されました。(require, permit)

 

def user_params
params.require(:user).permit(:name, :email)
end

 requireを指定することで特定のキーを条件にフィルタをかけられます。またpermitを使えば取得したいレコードだけ取得できます。

 

viewのフォームの書き方の一例

 

<%= form_for @project do |f| %>  
<p>
<%= f.label :title %><br>               
<%= f.text_field :title %>
<% if @project.errors.any? %>
<%= @project.errors.messages[:title][0] %>
<% end %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>

※ @で始まる変数をインスタンス変数という。インスタンス変数は頭文字は小文字という規則がある。

  • <%= @project.errors.messages[:title][0] %>で使われるエラーメッセージはmodelに書くことになる。
  • <%= form_for @project do |f| %>  のfは別にどんな名前でもいい。ここで使われている@projectはcontrollerのアクションで生成した変数、例えば、@project = Project.find(params[:id]) を参照している。

 

modelクラスの記述 

 

class Project < ActiveRecord::Base
has_many :projects
validates :title,
presence: { message: "入力してください" },
length: { minimum: 3, message: "3文字以上必要"}
def self.search(search) #self.でクラスメソッドとしている
if search # Controllerから渡されたパラメータが!= nilの場合は、titleカラムを部分一致検索
Project.where(['name LIKE ?', "%#{search}%"])
else
Project.all #全て表示。
end
end
end


 上記のようにアソシエーションの指定やバリデーションルールを定義する。ActiveRecordのBaseクラスを継承しているのでORMが使える。

 

 

javascript雑記(scrollTopとかiterableとか)

javascriptjquery)を書いていて、いろいろ気になったところをまとめます。

 

Chromeで仕様変更になってたところ

普段Chromeしか使わないので、他のブラウザとの仕様の差とかあんまり意識してコーディングする機会ないのですが、他のブログ記事読んでて気になったので下記にまとめます。

 

 $('body').scrollTopが効かなくなってたみたい

      

$('body').scrollTop();

Chromeでは上記値は常に0を返します。(Chrome51.0で検証)

Firefoxと同じ仕様みたいです。つまり、$('body').scrollTopがきかなくなり、htmlタグを読むタイミングでscrollTopが効くようになってみたいです。

 

 

HTML CollectionとNodeListのイテレータサポートについて

 Arrayはiterableかつ長さを持ちます。

 

if(typeof array[Symbol.iterator] === "function")
alert('true'); //trueがアラートされる

古いChromeとかだと、イテレータが使えないので、下記のような処理が必要かと思います。 

 

let arrayIterator = Array.prototype[Symbol.iterator];
if (!(Symbol.iterator in HTMLCollection.prototype))
HTMLCollection.prototype[Symbol.iterator] = arrayIterator;
if (!(Symbol.iterator in NodeList.prototype))
NodeList.prototype[Symbol.iterator] = arrayIterator;

C#で非同期でダウンロードする処理を書く

C#で非同期処理を書く際の文法やルールをまとめてみました。非同期処理にはasync/awaitを使いますが、これが使えるようになったのはC#5.0からです。

 

同期処理でファイルをダウンロードする処理

 
using System.IO;
using System.Net;

public static string ReadFromUrl(Uri url)
{
      using (WebClient webClient = new WebClient())
      {
             using (Stream stream = webClient.OpenRead(url))
             {
                    TextReader tr = new StreamReader(stream, Encoding.UTF8, true);
                    string body = tr.ReadToEnd();
 
                    return body;
              }
       }
}

public static void Download()
{
        Uri url = new Uri("https://github.com/hoge/master/README.md");
        string body = ReadFromUrl(url);
        Console.WriteLine(body);
}

StreamReaderで文字列として読み取ります。非同期に書き換えると、下記のようになります。

 

 
 

using System.IO;
using System.Net;
using System.Threading.Tasks;

public static async Task<string> ReadFromUrlAsync(Uri url)
{
    using (WebClient webClient = new WebClient())
    {

       using (Stream stream = await webClient.OpenReadTaskAsync(url))
       {
           TextReader tr = new StreamReader(stream, Encoding.UTF8, true);
           string body = await tr.ReadToEndAsync();

 

           return body;
        }
             }
}

public static async Task DownloadAsync()
{
           Uri url = new Uri("https://github.com/hoge/master/README.md");
           string body = await ReadFromUrlAsync(url);
           Console.WriteLine(body);
}

 

意外と非同期に書き換えるやり方は単純で「非同期だよ」と書いてやることがまずやることみたいです。それは前提として、非同期処理は呼び出し元から末端まで、「全て非同期メソッドで実装する必要がある」と言う事です。

 

非同期処理の挙動

非同期処理を行うメソッドはそれが実行中でもUI側は動かすことができます。例えば、非同期メソッドでwaitする処理が走っても、UI側まで待機になって固まることはないわけですね。

 

非同期処理を書く際のポイント

  • メソッドの中で、awaitキーワードを使うためには、そのメソッド定義時にasyncキーワードを付けてやる必要があります。
  • 同期させたい処理の戻り値がstringなら、それに合わせて非同期処理の戻り値はTask<string>にする必要がある。
  • Task=非同期ではない。非同期処理ではないメソッドをTaskを使って書くこともできる。
  • 非同期メソッドの戻り値は、Task型かTask<T>型かvoid型にしてやる必要がある。voidはほぼ使わない。
  • 返されたTask<T>をawaitすると、目的の文字列が得られる。

awaitキーワードの効果は、「指定したTaskの完了を待つ」「そして、その結果を取り出す」です。 

 

非同期処理の怖い所

 非同期処理で扱うTaskはただの処理の集合体で、ポリシーとしては「書かれている処理が順番どおり実行できればOK」なので、どのスレッドが実行したかとかお構いなしなわけです。というかTaskはただの作業手順書なので、そもそもスレッドの存在など気にしていません。

 なので、デッドロックやスレッドの実行順序するプログラムが別途必要となります。

 

デッドロックになるパターン

そもそもawaitとは「〜を待つ」という意味で、このキーワードが付いたメソッドは呼び出し元のメソッドの完了を待ってから実行されます。なので、呼び出される側でTask.Wait()をした場合、呼び出し側を待機中にしてしまうので結果、デッドロックとなってしまいます。

 

デッドロック対策

  • 今後非同期処理に改修する可能性があるならTask.Wait()は使わない
  • とにもかくにも、awaitを使う際には、必ずConfigureAwait(false)を書く。
 
 
private async void Page_Load(object sender, EventArgs e)
{
          await HeavyProcessingAsync().ConfigureAwait(false);
}

 

Task.Wait()使わなくても、WhileループやResultプロパティの参照とかで余裕でデッドロックになるみたいです。(Result プロパティは、タスクが終了するまで呼び出し元のスレッドをブロックします。)

ConfigureAwait(false)をつければ、特定のスレッドに戻らなくても良いので、デッドロックを防げます。しかし、ListなどのUI処理では特定のスレッドに戻る必要があるので、そこだけ留意すればよいらしいです。

 

参考サイト

qiita.com

qiita.com

方法: タスクから値を返す | Microsoft Docs