ActiveModelのベストプラクティスを考える
ActiveModelとは
ActiveModelとは こちら にあるように Modelと直接紐付けないような、フロントのフォームに密接に関わるバリデーションを実装する場合に便利な Module。
課題
ActiveModel
を利用シーンの一つとして、一つフォームで複数のモデルを扱うことがある。
その際によく迷うことがある。
- モデルのバリデーションが
ActiveModel
とApplicationRecord
のモデルとで重複する - データの作成と編集で
ActiveModel
のintialize
にどうやって、何を渡すか(ここらへん情報が検索してもあまり見つからなかった)
解決方法
そこで、とりあえず考えた解決方法
ActiveModel
に対象のモデル群を内包するfind
,save
,valid?
を実装ActiveModel
のバリデーションはフォーム固有のものだけを実装し、モデルで実装されているバリデーションはモデルに任せるController
はcreate
,update
はフォームの内容を,edit
の場合は保存されている内容からフォームを作成できるようにするsave
時にvalid?
を実行し、内包するモデルのバリデーションエラーとフォームのバリデーションエラーを全てフォームのエラーに追加する
以下イメージ。実際の動作確認はしてないです
class HogeController < ApplicationController der new @hoge_form = HogeForm.new() end der create @hoge_form = HogeForm.new(params[:hoge_form]) if @hoge_form.save ... else ... end end def edit @hoge_form = HogeForm.find(parmas['id']) end def update ... end ... calss HogeForm include ActiveModel attr_accessor :hoge_a, :foo_b validates :hoge_a, presence: true validates :foo_b, presence: true class << self def find(id) hoge = Hoge.find id new(hoge: hoge.attributes, foo: hoge.foo.attributes) end end def intialize(params = {}) @hoge = Hoge.new(params[:hoge]) @foo = Foo.new(params[:foo]) end def valid?(context = nil) super @hoge.valid? @hoge.errors.each do |k, v| errros.add(k, v) end @foo.valid? @foo.errors.each do |k, v| ... end def save return unless valid? return false unless @hoge.save @foo.save end end
関連のおすすめ本
- 作者: 掌田津耶乃
- 出版社/メーカー: 秀和システム
- 発売日: 2016/12/17
- メディア: 単行本
- この商品を含むブログを見る
Mechanizeでリンクをスクレイピング
Mechanize を利用したときのメモ。
Mechanize
を試した理由
- ちょっと前は anemone を利用していたが、最近はメンテしてないみたい。
- 内部で
Nokogiri
を利用しているので、学習・導入コストが個人的に低かった。
よく利用する関数と戻り値の型をメモ
> mechanize = Mechanize.new > search_page = mechanize.get(url) > search_page.class => Mechanize::Page > search_page.links.first.class => Mechanize::Page::Link > search_page.links.second.uri => #<URI::Generic /a/b.html> > search_page.links.second.resolved_uri => #<URI::HTTP http://hoge.jp/a/b.html> > div = search_page.search('.hoge') > div.class => Nokogiri::XML::NodeSet > div.first.class => Nokogiri::XML::Element
利用例
links
関数でドキュメント内の a
タグを簡単に取得できる。
Mechanize
をラッパーしたリンク取集クラス
class Scraper attr_accessor :mechanize def initialize @mechanize = Mechanize.new end def scraping_links(url:, links_pattern:, sleep: 1) result_links = {} loop do Rails.logger.debug(url) search_page = mechanize.get(url) target_links = search_page.links.select { |link| link.uri.to_s =~ links_pattern } links = target_links.map { |link| [link.resolved_uri.to_s, link] }.to_h result_links.merge!(links) next_link = block_given? ? yield(search_page) : nil break if next_link.blank? url = next_link.resolved_uri.to_s sleep(sleep) end result_links end def search_document(url) search_page = mechanize.get(url) yield(search_page) end end
呼び出し例
# http://hoge.jp ページ内の /foo/ のリンクを全て取得したい場合 links = Scraper.new.scraping_links(url: 'http://hoge.jp', links_pattern: /foo/) # http://hoge.jp ページ内の /foo/ のリンクをページングして全て取得したい場合。この場合は「次へ」のリンクがなくなるまでクロール links = Scraper.new.scraping_links(url: 'https://hoge.co.jp/foos/?page=1', links_pattern: /foos\/[A-Z]/) do |search_page| search_page.links.find { |link| link.text == '次へ' } end
おすすめ本
Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例
- 作者: 佐々木拓郎,るびきち
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2014/08/22
- メディア: 単行本
- この商品を含むブログ (10件) を見る
データを集める技術 最速で作るスクレイピング&クローラー (Informatics&IDEA)
- 作者: 佐々木拓郎
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2016/11/29
- メディア: 単行本
- この商品を含むブログを見る
- 作者: Ryan Mitchell,嶋田健志,黒川利明
- 出版社/メーカー: オライリージャパン
- 発売日: 2016/03/18
- メディア: 大型本
- この商品を含むブログ (2件) を見る
ransackのinをカンマ区切りでする
やりたいこと
ransack を利用して、 in
の検索をカンマ区切りで指定できるようにする。
手順
Controller
class HogeController < ApplicationController ... def index @q = Hoge.ransack(ransack_queries) @hoges = @q.result.page(params[:page]) end ... def ransack_queries queries = params[:q] if queries.present? && queries[:id_in].present? # ページネーション等で2回目のアクセスが来た場合には既にarrayになっているので return。 return queries if queries[:id_in].is_a?(Array) queries[:id_in] = queries[:id_in].split(',').map(&:strip) end queries end ...
View
= search_form_for(@q) do |f| ... = f.search_field(:id_in, class: 'form-control', value: params[:q].try(:fetch, :id_in, nil).try(:join, ','))
参考
関連する書籍
- 作者: 掌田津耶乃
- 出版社/メーカー: 秀和システム
- 発売日: 2016/12/17
- メディア: 単行本
- この商品を含むブログを見る
gretel+bootstra4でパンくず
やりたいこと
bootstrap4 の breadcrumb でパンくずをつくる。
課題
<% breadcrumb :issue, @issue %>
ヘルパーのオプションでは li
タグにclassを指定ができない。
解決方法
manually で対応する
手順
Gemfile
gem 'gretel'
インストール
$ bundle install --path=vendor/bundle $ ./bin/rails g gretel:install create config/breadcrumbs.rb
パンくずを設定
$ vim config/breadcrumbs.rb
view
... <% breadcrumbs.tap do |links| %> <% if links.any? %> <div class='container'> <ol class='breadcrumb'> <% links.each do |link| %> <li class='breadcrumb-item<%= ' active' if link.current? %>'> <%= link_to_unless(link.current?, link.text, link.url) %> </li> <% end %> </ol> </div> <% end %> <% end %> ...
メモ
<% breadcrumbs do |links| %>
と実装すると、[Gretel] Calling
breadcrumbswith a block has been deprecated and will be removed in Gretel version 4.0. Please use
tapinstead. Example:
とログが出力されたので、tap
で対応。
完成
werckerでpushが正常にフックされなかった時の対応。
enumerizeで検索select box
ransack
で検索フォームを作成する場合のenumerize
を利用したselect boxの作成について。
課題
enumerize
で検索用のselect boxを作成したいが options
メソッドだと、
> Product.kind.options => [["キット", "kit"], ["おもちゃ", "toy"]]
となってしまう、やりたいことは enumerize
の in
で指定したvalueを利用したい。
理想は、
[["キット", 1], ["おもちゃ", 2]]
としたい。
解決方法
locale
ja: enumerize: product: kind: kit: キット toy: おもちゃ
モデル
class Product < ApplicationRecord extend Enumerize enumerize :kind, in: { kit: 1, toy: 2 }, predicates: { prefix: true }, scope: true end
ヘルパー
module ProductsHelper def select_kinds Product.kind.find_values(*Product.kind.values.map(&:to_sym)).map { |kind| [kind.text, kind.value] } end end
view
<%= f.select(:kind_eq, select_kinds) %>
<select name="q[kind_eq]" id="q_kind_eq"> <option value="1">キット</option> <option value="2">おもちゃ</option> </select>
となる。
最後に
enumerize :kind, in: { kit: 1, toy: 2 }
の in で指定しているhashを定数にするという案もあるが、localeのこととかを考えると、この実装が良いと感じた。
デザイン知識がないプログラマーが「見つけやすさのデザイン」を考える
ここまで で Railsアプリケーション構築のフロントエンドの設定まで完了。ここからはWebアプリケーションのサイト構成とページ構成を検討する。
検討にあたっては、サイト・ページ構成をーから構築した経験がないので、取っ掛かりとして 情報アーキテクチャ 第4版 ―見つけやすく理解しやすい情報設計 の内容を参考に進めてみた。
最初のサイト・ページ構成案 (「シンプルすぎる」情報モデル)
ザックリ前提・要件
- だれ向け:電子工作に興味がある人が訪れるサイト
- どんなサイト:電子工作にまつわる製品が網羅できるページがある
サイト構成
ページ
Bootstrapを学習するついでに、ワイヤーフレームではなく、いきなりviewを作成。
メイン
製品一覧
製品詳細
と、これでは全然イケてないので改修を試みる。
ユーザの「情報ニーズ」を考える
「ユーザーが何を求めているのか?」を考え、その特性を分類し整理してみる。
それにより、
ユーザーの主要情報ニーズと情報探索行動の典型を知ること。ユーザがシステムに何を求めているかの理解。
を体感。
本書から引用。
- 既知情報検索:探しているものが何か、誰に聞けばいいか、どこで探せばいいのかを知っている
- 探求探索:自分が本当に探しているものを把握してない。ユーザは検索やブラウジングのプロセスで自分が何を求めているかを知る。「正しい」答えに対する明確な期待もなければ、探しているものをはっきりと知る必要もない。ユーザはいくつかよい結果を得られれば満足で、それを踏み台として次の検索を繰り返す
- 全数探索:「あらゆる物を手に入れたい」。ユーザーは特定のトピックに関する何もかも、一つ残らず探している。ユーザーは多様な言い方で探しているものを表現でき、さまざまな用語を駆使して忍耐強く検索を続ける。他の情報ニーズに比べてより多くの検索結果を辛抱強く渡り歩く
- 再検索:これまでに見つけた便利な情報の断片を再検索する。「あとで読む」サービスを使って、もっと時間のある時にページに戻る
で、そこから自分なりに解釈。
分類 | ザックリと表現 | 必要な機能 |
---|---|---|
既知情報検索 | 能動的な人 | フリーワード検索 |
探求探索 | 目的がふんわり | 選択肢や何かの括りでの検索 |
全数探索 | とにかく見る | ??? |
再検索 | 再訪者 | お気に入り |
当初の自分が想定したユーザーとその動きを考えると、「探求探索」が該当するユーザーのような気がしてきた。 本来は全分類について検討するのだろうが、まずは「探求探索」に対してどうすれば良いかを考える。
そこで、
- オーガニック検索で「電子工作 キット 入門」のようなワードで流入
- メイン or 製品一覧にランディング。
- 興味が湧く製品詳細に導く
というアウトラインを決めて、「探求探索」のユーザーの目的(ゴール)を設定する。
そうした時に、現状の構成では、製品に興味をもってもらい、サイトを回遊するたのフックが弱いと感じた。 そこで製品の「括り」のような取っ掛かりが良い気がした。
改変後
製品を「特徴」で分類し、分類での絞込ができるようにしてみる。
- メインに「特徴」導線を追加。特徴での絞込結果に遷移
- 製品一覧に「特徴」での検索を追加
サイト構成
ページ
メイン
製品一覧
という、大したことない改修ですが、なんか漠然と導線を作ったのではなく、体系的に自分で考えて実装をしたことで「 構成を真剣に考える 」良い経験になった気がする。
最後に
ユーザーの主要情報ニーズと情報探索行動の典型を知ること。ユーザがシステムに何を求めているかの理解。
とりあえず、情報アーキテクチャ 第4版 ―見つけやすく理解しやすい情報設計 からヒントをえて、気づいた点から対応。 これから更に読み進めて
- 情報の組織化
- ラベリング
- ナビゲーション
- 検索
という要素を検討していく。
本書は若干内容が発散的にも感じる部分もあるが、情報アーキテクチャという観点から、サイトを構成する前提、要素整理やアプローチの方法等のアイデアを得るには良い本だと思った。
こういった事に疎かった自分にとっては新たな気づきが得られた。
近い内容で 情報アーキテクチャ 第4版 ―見つけやすく理解しやすい情報設計 も参考になりそう。
関連する本
情報アーキテクチャ 第4版 ―見つけやすく理解しやすい情報設計
- 作者: Louis Rosenfeld,Peter Morville,Jorge Arango,篠原稔和,岡真由美
- 出版社/メーカー: オライリージャパン
- 発売日: 2016/11/18
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
- 作者: 坂本貴史,宮崎綾子,長谷川恭久
- 出版社/メーカー: ワークスコーポレーション
- 発売日: 2011/03/29
- メディア: 単行本
- 購入: 12人 クリック: 167回
- この商品を含むブログ (10件) を見る