Rails - ActiveDecoratorのコード読んでみたヘ(^o^)ノ

いつもお世話になります。ActiveDecoratorさま。

@izumin5210さんの「ActiveDecorator読んでみたら超勉強になった」を読んで、そういやコードあんま見てなかったので読んでみた

なるほど編

コードよむ編

# lib/active_decorator/monkey/abstract_controller/rendering.rb
module AbstractController
  module Rendering
    # to_aメソッドのエイリアス↓
    def view_assigns_with_decorator 
      # 既存のto_aメソッドをコール
      hash = view_assigns_without_decorator

      # メソッドを拡張
      hash.values.each do |v|
        ActiveDecorator::Decorator.instance.decorate v
      end
      hash
    end
    
    # :view_assigns => :view_assigns_with_decorator
    # :view_assigns_without_decorator => :view_assigns
    alias_method_chain :view_assigns, :decorator
  end
end

view_assignsは、Controllerで取得したインスタンスなんかが格納されたHashを返しますよー

こんな感じ↓

f:id:murajun1978:20150114225602p:plain

postsはActiveRecord::Relationです(Post.allしたやつ)

testはインスタンス変数に文字列をいれてみた

このHashをActiveDecorator::Decorator

# lib/active_decorator/decorator.rb
module ActiveDecorator
  class Decorator
    include Singleton

    def initialize
      @@decorators = {}
    end

    def decorate(obj)
      [...]
    end
    [...]
  end
end

singletonをMix-inしてるので、ActiveDecorator::Decorator.instance.decorateで呼び出してますよ

decorateメソッド
if obj.is_a?(Array)
  # *2でArrayが引数となるのでここ
  # Arrayの要素(ModelClass)を引数として自身を呼び出してる *3
  obj.each do |r|
    decorate r
  end
elsif defined?(ActiveRecord) && obj.is_a?(ActiveRecord::Relation) && !obj.respond_to?(:to_a_with_decorator)
  # ActiveRecord::Relationならクラスを再オープンしてメソッド定義してる *1
  class << obj
    def to_a_with_decorator
      to_a_without_decorator.tap do |arr|
        # Arrayを引数として自身を呼び出してる *2
        ActiveDecorator::Decorator.instance.decorate arr
      end
    end
    alias_method_chain :to_a, :decorator
  end
else
  # *3で引数がmodelクラスとなるのでここ(ActiveRecord::Relation、Array以外もここ)
  # decoratorのクラス取得してる
  d = decorator_for obj.class
  return obj unless d
  # PostDecoratorモジュールに属してなければextendしてる
  obj.extend d unless obj.is_a? d  
end
decorator_forメソッド
private
def decorator_for(model_class)
  return @@decorators[model_class] if @@decorators.has_key? model_class

  decorator_name = "#{model_class.name}Decorator"
  d = decorator_name.constantize
  unless Class === d
    d.send :include, ActiveDecorator::Helpers
    @@decorators[model_class] = d
  else
    @@decorators[model_class] = nil
  end
rescue NameError
  @@decorators[model_class] = nil
end

ViewContextをごっそり

# lib/active_decorator/view_context.rb
module Filter
  extend ActiveSupport::Concern

  included do
    before_filter do |controller|
      ActiveDecorator::ViewContext.current = controller.view_context
    end
  end
end

helperの拡張

# lib/active_decorator/helpers.rb
module Helpers
  def method_missing(method, *args, &block)
    super
  #TODO need to make sure who raised the error?
  rescue NoMethodError, NameError => original_error
    begin
      # helperでmethod_missingならActiveDecorator::ViewContextから(なければoriginal_errorの例外発生
      ActiveDecorator::ViewContext.current.send method, *args, &block
    rescue NoMethodError, NameError
      raise original_error
    end
  end
end

ViewContextをごっそりとってるから、flashとかもdecoratorで使えるよー

ActiveDecorator::ViewContext.current.flash 
=> #<ActionDispatch::Flash::FlashHash:0x007fda40e27d78 @discard=#<Set: {}>, @flashes={"error"=>"test error"}, @now=nil>
ActiveDecorator::ViewContext.current.flash[:error]
=> "test error"

# app/decorators/post_decorator.rb
module PostDecorator
  def error_flash
    flash[:error]
  end
end

ActiveDecoratorだと読みやすくて勉強にも良いかと思います♪

Happy Hacking٩( ‘ω’ )و

d(゚Д゚ )☆スペシャルサンクス☆( ゚Д゚)b

神戸.rb Meetup #10に行ってきたよヘ(^o^)ノ

今日は神戸.rb Meetup #10に行ってきた。

仕事が押して1時間ほど遅れてしまったけど。。。( ̄▽ ̄;)

みんながそれぞれ興味あることをワイワイ話したり、もくもくしたりと良い感じです。

おやつもあるっ!!!

僕はcocoon(こくーん?)ってgemを使ってみました。

ユーザーストリーにタスクをポンポン追加していくときに使うー

使い方はとても簡単

//= require cocoon
  • fields_forを使って子モデルを追加したりできるようにする
  %h3 Tasks
  #tasks
  .field
    = f.fields_for :tasks do |task|
      = render 'task_fields', f: task
  .links
    = link_to_add_association f, :tasks do
      = icon('plus-square fa-2x')

  .actions
    = f.submit 'Save'

# _task_fields.html.haml
.nested-fields
  .field
    = f.label :name
    = f.text_field :name
    = f.label :description
    = f.text_field :description
    = link_to_remove_association  f do
      = icon('times fa-2x')

10分くらいでできたサンプルがコレ

f:id:murajun1978:20150107012455g:plain

そして懇親会♪

ゆりえちゃんのお話もできたしー.。oO(かわいい)

みなさんお疲れ様でしたヘ(^o^)ノ

Rails - ActiveModel::Callbacksでカスタムコールバックヘ(^o^)ノ

あけましておめでとうございます。

年内で仕事が納まらず、1日からコードをごりごり書いております。。。

さて、ActiveRecord::Callbacksはみなさんおなじみ

before_saveとか良く使いますよね?

でも、今回やりたい事はこういうの

# app/models/comment.rb
class Comment < ActiveRecord::Base
  after_publish :send_mail  #publishメソッドのCallback

  def publish
    [...]
  end

  private
  def send_mail
    Notifier.comment_published(self).deliver_now
  end
end

そこでActiveModel::Callbacksです

ActiveRecordのCallbacksを参考に => active_record/callbacks.rb

# app/models/concerns/comment_callbacks.rb
module CommentCallbacks
  extend ActiveModel::Callbacks
  extend ActiveSupport::Concern

  included do
    define_model_callbacks :publish, only: :after
  end

  private
  def action_after_publish
    puts '- after publish'
  end
end

# app/models/comment.rb
class Comment < ActiveRecord::Base
  include CommentCallbacks

  after_publish :action_after_publish
  
  def publish
    run_callbacks :publish do
      puts '> publish!'
    end
  end
end
$ Comment.new.publish
> publish!
- after publish

ちゃんとCallbackされましたね

もっと良い実装があれば教えてくだされー

そもそも、この実装が良いのかもわからんし...( ̄▽ ̄;)

Happy Hacking٩( ‘ω’ )و

Shinosaka.rb #11を開催したよーヘ(^o^)ノ

今年最後のShinosaka.rbを開催しましたー

DoorKeeper - Shinosaka.rb #11

みなさんのおかげで1年を通して開催することができました。

来年からShinosaka.rbはLTタイムをつくるよー

参加者のなぜ???やRubyに関する困りごとをLTしてもらって、みんなで解決していく感じです。

もちろんペアプロ大会なんかも開催する予定です。

さて、本日のテーマは「Rails 4 Patterns」でした。

スライドはこれ⇒Rails 4 Patterns

僕なりにRailsで開発するときの大事な部分を説明しました。

みんなの意見が聞けてとても勉強になったよ

スライドの最後にでてくる内部リンクのURLを生成するにはこんな感じです

post_path @post, anchor: "post_#{@post.id}"

@ogomrさんと@toshima66さんが発表してくれました

@ogomrさんはRails Tree Cheat Sheetについての発表

発表内容

発表後にShinosaka.rbでgemが誕生する瞬間をみた!⇒cassette-rack

僕も興味ある分野なので、一緒に開発できればと思っていますよ@ogomrさん!w

@toshima66さんはSVG(Scalable Vector Graphics)についての発表

発表内容
  • 画像ファイルだとでっかくすると粗くなるけど、SVGはそうならない
  • XMLなのでコードで書けるぜ! コードでアニメーションとかできるの良いわー.。oO(Shinosaka.rbのロゴつくろ)

初心者の方へのオススメRubyRails

パーフェクトRuby (PERFECT SERIES 6)

パーフェクトRuby (PERFECT SERIES 6)

Ruby on Rails 4 アプリケーションプログラミング

Ruby on Rails 4 アプリケーションプログラミング

そして勉強会より楽しいと噂の懇親会へw(またいっぱい呑んでしまった( ˘ω˘)

Rails Boysなーwww

今年はたくさんの方にお世話になりました。

みなさんが参加してくれるので、1年を通して開催することができました。

大変だったけど、みんなと月1回会えるのを楽しみにがんばれたー( ̆ω ̆≡ ̆ω ̆)

来年もShinosaka.rbを宜しくですヾ( ̄∇ ̄=ノ

Have a nice new year ٩( ‘ω’ )و

DXRubyでプロ生ちゃんを動かしてみたヘ(^o^)ノ

これはプロ生ちゃんAdvent Calendar 2014の18日目の記事ですヘ(^o^)ノ

プロ生ちゃんの素材DXRubyを使って、プロ生ちゃんを動かしてみます。

DXRubyとはゲーム開発初心者向けのDirectXライブラリ

注) 32bit版のRubyでのみ動作します

DirectXということは…そうWindows向けです

僕はMacなので、@wannabe53さんの"DXRuby を Wine から使う"を参考にインストールしました

DXRubyのAdvent Calendarはこっち⇒DXRuby Advent Calendar 2014

↓今回、利用したドット絵はこれ

f:id:murajun1978:20141218002911p:plain

動かしてみる

# pronama.rb
require 'dxruby'
require_relative 'animation' # sampleアプリから
# 画像をロード
image = Image.load_tiles('pronama_chan.png', 3, 4)
pronama_chan = AnimeSprite.new
pronama_chan.x = 0
pronama_chan.y = 0
# アニメーションの設定
pronama_chan.animation_image = image
pronama_chan.add_animation  :advance,    4, [1, 2]
pronama_chan.start_animation :advance

Window.loop do
  pronama_chan.update
  pronama_chan.draw

  # ESCで終了する
  break if Input.key_push?(K_ESCAPE)
end
$ ruby pronama.rb

f:id:murajun1978:20141218005103g:plain

動いてるヘ(^o^)ノ

簡単ですねー

それでは矢印キーで上下左右に動かしてみましょう

# pronama.rb
(...) 
  pronama_chan.add_animation   :retreat,    10, [10, 11]
  pronama_chan.add_animation   :move_left,  10, [4, 5]
  pronama_chan.add_animation   :move_right, 10, [7, 8]
  pronama_chan.start_animation :advance

Window.loop do
  # 下矢印キーで前進する
  if Input.key_push?(K_DOWN)
    pronama_chan.start_animation(:advance)
    pronama_chan.y += 5
  end

  # 上矢印キーで後退する
  if Input.key_push?(K_UP)
    pronama_chan.start_animation(:retreat)
    pronama_chan.y -= 5
  end

  # 左矢印キーで左へ
  if Input.key_push?(K_LEFT)
    pronama_chan.start_animation(:move_left)
    pronama_chan.x -= 5
  end

  # 右矢印キーで右へ
  if Input.key_push?(K_RIGHT)
    pronama_chan.start_animation(:move_right)
    pronama_chan.x += 5
  end

  pronama_chan.update
  pronama_chan.draw

  # ESCで終了する
  break if Input.key_push?(K_ESCAPE)
end

f:id:murajun1978:20141218011116g:plain

矢印キーを押すとプロ生ちゃんが動いてますヘ(^o^)ノ

どうですか?

簡単にRubyでサンプル作れちゃいましたねー

サンプルには実際のゲームなんかも同封されているので、興味のある方は是非!

Happy Hacking٩( ‘ω’ )و

サンプルコード => GitHub - pronama_game

d(゚Д゚ )☆スペシャルサンクス☆( ゚Д゚)b

Shinosaka.rb #10を開催したよーヘ(^o^)ノ #shinosakarb

f:id:murajun1978:20141126231819j:plain

Shinosaka.rb #10

今回のお題はLitaであそうぼうーです( ˘ω˘)

同じ日に第1回 Hubot×ChatOps勉強会が開催されていたので...

Hubot先生はJavaScript

仕事でも、ProductでなければRubyでがんばりたい!(開発環境とかチャットとか

LitaRubyってことは、そう既存のGemが使えるのだヘ(^o^)ノ

HipChatSlackとか使おうと思ったけど、今回はIdobataを使わせていただきました。

みなさんのまとめブログがすばらしい(オレもがんばる?

いや!ここはエコシステム方式で...

全然、適当じゃないw

勉強会のあとも触ってくれてるのうれしいヽ(´ー`)ノ

Kobe.rbでいつもお世話になってる@tanaka4410さん♪

GitHub - lita-sample

ボードと連携して、温度をタイマーでループさせて表示したり面白かった。

懇親会では、子供のような僕とOやんにボードについてやさしく教えてくれましたw

@meltediceさんは為替を表示するhandler作ってましたー

RubyMotionがちょっとお高いから、買い時を知らせてくれたら面白いねーって話してましたw

GitHub - lita-foreign-exchange

@honeniqさんはWikiから画像検索して表示するhandlerをつくってたよー

GitHub - lita-sample-photo

@kytiken_code さんはTwitterのタイムラインから画像検索して、ツイートするのを作ったみたい

GitHub - TwitMakichan

なぜ、まきちゃんなのかは触れないでおきましょうw

みんな楽しんでくれたみたいヘ(^o^)ノ

chat botは全然触る機会がなかったので、僕も楽しめた。

HipChatSlackなどと連携すると、もっと楽しくなる。

slackは独自ドメインが必要だったので、Shinosakarb.orgを取得して設定しているとこ。

メールサーバも立てる準備もしてるから、次回から使えるかもね。

Litaもたてて、みんなでどんどん拡張していくのも楽しそう。(HPだれか...w

次回は、今年最後のShinosaka.rb 12月20日(土)です。Shinosaka.rb #11

Rails4 Patternsについて盛り上がる予定です

Shinosaka.rbも今年1月から1年間続けることができました。(まだ、1回残ってるけど

今までたくさんの人が参加してくれました。

ホントに感謝です。ありがとーヘ(^o^)ノ

そして、これからも宜しくお願いします。

Happy Life Hacking With Ruby٩( ‘ω’ )و

追記

2014/11/27 slackの登録に独自ドメインは必要ないです><(あっても良いですが

Rails - touchオプションでupdated_atを更新するヘ(^o^)ノ

class Post < ActiveRecord::Base
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :post
end
pry(main)> Post.find(1)
+----+-------------+------+-------------------------+-------------------------+
| id | title       | body | created_at              | updated_at              |
+----+-------------+------+-------------------------+-------------------------+
| 1  | sample post |      | 2014-11-21 13:54:14 UTC | 2014-11-21 13:54:14 UTC |
+----+-------------+------+-------------------------+-------------------------+

pry(main)> Post.find(1).comments.first.update(body: 'change comment')
pry(main)> Post.find(1)
+----+-------------+------+-------------------------+-------------------------+
| id | title       | body | created_at              | updated_at              |
+----+-------------+------+-------------------------+-------------------------+
| 1  | sample post |      | 2014-11-21 13:54:14 UTC | 2014-11-21 13:54:14 UTC |
+----+-------------+------+-------------------------+-------------------------+

commentを更新しても、Postのupdated_atは変更されないですよね?

では、touchオプションを指定してみましょう

class Comment < ActiveRecord::Base
  belongs_to :post,  touch: true
end

もう一度、commentを更新してみましょう

pry(main)> Post.find(1).comments.first.update(body: ' touch: true')
pry(main)> Post.find(1)
+----+-------------+------+-------------------------+-------------------------+
| id | title       | bosy | created_at              | updated_at              |
+----+-------------+------+-------------------------+-------------------------+
| 1  | sample post |      | 2014-11-21 13:54:14 UTC | 2014-11-21 14:05:16 UTC |
+----+-------------+------+-------------------------+-------------------------+

pry(main)> Post.find(1).comments.first
+----+-------------+-------------------------+-------------------------+---------+
| id | body        | created_at              | updated_at              | post_id |
+----+-------------+-------------------------+-------------------------+---------+
| 1  | touch: true | 2014-11-21 13:54:14 UTC | 2014-11-21 14:05:16 UTC | 1       |
+----+-------------+-------------------------+-------------------------+---------+

Postのupdated_atも更新されましたー

Viewでrender partialをキャッシュする場合とかに使えそうですね。

Happy Hacking٩( ‘ω’ )و