メタプログラミングRubyメモ
Object#extend
module MyModule def my_method; 'hello'; end end
を使いたいときに
class MyClass class << self include MyModule …. end end
↓
class MyClass extend MyModule …. end
これでもMyClassの特異メソッドとしてmy_methodを呼べる。
( MyClass.my_method #=> 'hello' )
falseの時なにか決まったものを返すとき、三項演算子じゃなくていいよ!的な
class MyClass def initialize @hoge = false end def hoge? @hoge || false end end irb(main):009:0> MyClass.new => #<MyClass:0x007ff499890fe0 @hoge=false> irb(main):010:0> a = MyClass.new => #<MyClass:0x007ff49987a8d0 @hoge=false> irb(main):011:0> a.hoge? => false
って感じでいける。or演算子やから当たり前やけど、こう書けてなかったな、、っていう自分のレベルの低さを感じたのでメモ
alias :m(新しいメソッド名) :my_method(古いメソッド名)
★アラウンドエイリアス
module Kernel alias gem_original_require require def require(path) gem_original_require path rescue LoadError => load_error if load_error.message =~ /#{Regexp.escape path}\z/ and spec = Gem.searcher.find(path) then Gem.activate(spec.name, "= #{spec.version}") gem_original_require path else raise load_error end end end
エイリアスは元のメソッドを変更するのではなく、新しいメソッドを定義して元のメソッドの名前をつける。
なので元のものをラップして新しいのに飛ばすようなことができてアラウンドエイリアスと呼ぶ。
クラスマクロ
クラスマクロはクラス定義の中で使えるクラスメソッド
class Book def self.deprecate(old_method, new_method) .. end deprecate :GetTitle, :title end
attr_accessorとかがそう。
module CheckedAttributes def self.included(base) base.extend ClassMethods # baseにはincludeしたクラスが入ってくるのでそれにたいしてClassMethodsを渡してる。 # こうすることで渡ってきたクラスのクラスメソッドにClassMethodsを設定できる end module ClassMethods def attr_checked(attribute, &validation) define_method "#{attribute}=" do |value| raise 'Invalid attribute' unless validation.call(value) instance_variable_set("@#{attribute}", value) end define_method attribute do instance_variable_get "@#{attribute}" end end end end
これで
class Person include CheckedAttributes attr_checked :age do |v| v >= 18 end end
とかできるようになるわけ。
RSpecでsessionを使用したテスト(ログイン機能)
内容としてはajaxのテストを書こうとして、それにはsessionが必要というような場合です。(渡しの場合、ログインしていないとajax通信ができないという仕様でした。)
ここけっこう困って、前回こういったsessionを使うテストを実装した際にはgrapeというRails向けのREST APIのフレームワークを使っていたのでこれはまた別問題でした。(grapeはrackを使ってsession設定できます)
想定したアプローチは2つあって
controller specを使ってテスト
postで実際にログインさせてからテストを実行
1つ目はやってみたのですがテスト内ではsessionを使えたのですがアプリ内での引き継ぎができなくてうまくいきませんでした、が、ブログ書きながらちょろちょろ調べて見てると出来そうな雰囲気もあるのでまた次回試してみます。。
2つ目が今回の実装でやったのですが、挙動的にはこっちのほうがいいのかなと。
require 'spec_helper' describe CalendarController do before do @user = create(:user) post '/login', {:username => @user.name, :password => @user.password} end context 'create' do … it do xhr :post, 'hoge/fuga', {:hoge => 1, :fuga => 'fuga'} expect(response.body).to eq({:success => true, :data => {…}.to_json}) response.code.should == "200" end end end
というような感じです。
実際のログインユーザーが行う処理という流れに沿っている分テスト的には良いかなと思いました。
xhrでajax通信を使ったgetやpostが行えます。その後に中身とステータスコード200をチェックしているというような形です。
ただ、普通にsession[:calendar_id]みたいなのを使いたいというような時は1つ目のアプローチが必要と思いますので、また解決し次第ブログに書きたいと思います。
Ruby(Rails)からExcelで文字化けしないCSVファイル作成
CSVファイル作成でハマったのでメモ。
今回作ろう思っていたのはリンクになっているボタンを押したらCSVファイルがダウンロードされるという単純なものです。
HTML載せようと思いましたがはてブ内での書き方わからないので省略、、
バージョンはRuby2.0, Rails4です。
controllerは下記のような感じで処理はmodelに。
# controllers/management_controller.rb def event_csv respond_to do |format| format.csv { send_data Event.event_csv(start_date, end_date), :filename => "events_#{start_date}-#{end_date}.csv" } end end
もともとはここで:type => ""でcontent-typeをcsvと指定していたのですがそれではうまくいきませんでした。
modelは下記
# models/event.rb class Event < ActiveRecord::Base class << self def event_csv(start_date, end_date) data = self.all csv_data = CSV.generate do |csv| csv << %w(ユーザー 所属 合計) # headerになる部分を先にpush data.each do |record| csv << [record[:username], record[:belonging], record[:sum]] end end csv_data end end end
ここでもまだ文字コード指定はしていません。
CSV.generate自体ウェブ上にあまり情報がなくて、しかもdocumentを見るとgenerate('', :encoding => 'sjis')みたいな感じで:encodingで指定できるよ!って書いてあるのですが できませんでした。。(文字コードは上記の場合、csv_data.encodeで調べられます。)
なのでさらに戻ってcontrollerから最後吐き出すときにafter_filterでnkfを使ってShift_JISにエンコードするという方法で解決しました。
class ApplicationController < ActionController::Base after_filter :cahnge_charset_to_sjis, :if => csv? ... private def csv? return request.original_url.match(/csv/) ? true : flase end def change_charset_to_sjis require 'nkf' response.body = NKF::nkf('-Ws', response.body) headers["Content-Type"] = "text/csv; charset=Shift_JIS" end end end
パスがcsvだったらsjisに文字コードを変えるメソッドを通してるって感じです。ここでheader["Content-Type"]も設定しています。
ちなみに念のためconfig/routes.rbにはこんな感じで書いてます。
get 'management/event_csv/(:start_date)/(:end_date)', :to => 'management#event_csv'
controllerの中でrespond_toでcsvを指定しているので上記のパスに.csvでアクセスするとcsvファイルがダウンロードされる、という感じです。(ex. /management/event_csv.csv)
文字化けは相変わらず辛い・・・。
うまく行かなかったところはもうちょい追求しなければ。
もっと良い実装があればぜひコメント下さい!m(_ _)m
プライベートプロジェクトでのリーン・スタートアップ by ffab0
先日、友人と2人で立ち上げたffab0というチームで
最初のサービス"PositionStrategy"をリリースした。
http://position-strategy.ffab-0.com/
今回はサービス企画からファーストリリースまでの1ヶ月間をまとめてみた。
仕事をしながらプライベートでサービスを出したい、学生同士で友人となにかサービスを作りたいと考えている方たちの参考に一部でもなれば。それと自分用に備忘録としても。すべて隠さずに書いているつもり。
目次はこんな感じ。
- サービス概要 ~ 宣伝も兼ねて ~
- 前提 ~ 開発環境と連絡手段などチームマネジメント ~
- 1週目 ~ 企画、そして挫折。新たなメンバー ~
- 2週目 ~ 企画MTG。表参道での誓い ~
- 3週目 ~ 2人で開発することによる成功と失敗。本業による多忙・・ ~
- 4週目 ~ リリースまでの長い道のり ~
■サービス概要
サービスの概要を説明しておくと、最近よく「LINEの売上が前年比◯◯%アップ!」や「グリーがついに赤字転落」など、決算や業績に関するニュースがピックアップされるが、それが実感としてどの程度すごいのか、どの程度良くないことなのかつかみにくいと思う。(少なくともビジネス感覚の低い僕はそうです。。)
そこを解決するために、代表的な財務指標をいくつかピックアップし、グラフ化して、前年はどうだった、他企業と比べてどうか、業界的にはどうなのかなどが視覚的に、直感的に理解できてるようにしたツールです。それを見る人の立場、役割によって様々な戦略を立てる手助けになりえるのではないかと考えています。
■前提 ~ 開発環境と連絡手段などチームマネジメント~
今回のPositionStrategyの使っている技術をざっと上げると
- Ruby on Rails 4.0
- Ruby2.0
- coffeescript
- chartJS
- MySQL5.6
- slim
- unicorn
- nginx
ざっとこんな感じ。コードの管理はgitホスティングサービスのbitbucketを使った。
チームのマネジメントという点では僕は東京で友人Kは大阪だったので基本的には
LINEとTwitterで相談、それでも難しい場合はGoogleハングアウトで会話+画面共有しながら相談という感じ。
また、土日など時間が取れる時に1日ハッカソンみたいな感じでずっとハングアウトつなぎっぱなしで開発をしていた。
ドキュメントはGoogleドライブ。
こんな感じで1ヶ月間で企画からリリースまで。
それでは次から1ヶ月間を振り返っていく。文章が稚拙なのでおもしろくないかも。。
■1週目 ~ 企画、そして挫折。新たなメンバー ~
このサービスは兼ねてから僕が考えていて勉強も兼ねてひとりで作ろうと思っていた。そこに同期のプログラマーが乗ってきたので2人でやることになったのだが、ここで最初の大きな挫折が。
- 仕事を言い訳にできてしまう
- 2人ともデザインのセンスがなかった
- 同期はユーザビリティよりもシステムに関心がいっている時期だった
- サービスを創れる状態になかった
同期同士なのでお互い忙しいのがわかるのと、仕事内容も把握していて催促できないことで甘えが出てしまっていた。さらにふたりともデザインセンスがほぼ皆無だった(僕はデザインが好きなだけに非常に残念だ笑)。同期はシステムやアクセスをどうさばくかというところに強い関心があったが、2人で創るにはその前の段階でクリアしなければならないことが多すぎた。どっちが悪いでもなかったがサービスを創るチームではなかったのだというのが結論だと感じている。
そんな中、救世主の友人Kがジョインしてくれた。どうやって呼び込んだかというと、この挫折しきった状態を大いに包み隠してビジョンだけ情熱的に話して呼び込んだ(笑) もちろんKがこのサービスに共感してくれたのが大きかったのと、高校時代からの友人で彼となら遠隔でも(Kは大阪在住で僕は東京)できるとお互いが思ったことが大きかった。実はKとは前に一度サービスを作ったことがあってある程度次はもっとうまくやれるとお互いに思いがあったこともある。
■2週目 ~ 企画MTG。表参道での誓い ~
Kと僕でやることになった時のチームのスペックとしては
- 僕はコード(主にサーバーサイドだがマークアップも)は多少書ける。ビジネスを考えるのは好き。
- Kはデザインが得意でマークアップもできる。サーバーサイドはほぼ素人だがターミナルで作業できるのとgitは教えた。ビジネスを考えるのは好き。
この2週目にKがたまたま東京に来る機会があったので、2泊3日の合宿で企画MTGと最初の環境構築をした。ふたりで振り返ってもここが重要なポイントだった。何が今回の成功(?)の鍵だったかというと、初回リリースの機能を洗い出して期限と担当を決めたこと。これは非常に重要なのだが、これをするためにはお互いに厚い信頼がないと仕事でない場面ではうまくいかないと思う。これを2人が守り切ったからこそ1ヶ月でのリリースができた。
もともと信頼はあったが、この合宿の最後に表参道でチーム論と人生について2人で話し合った。そしてffab0をスタートすることに決めた。これで2人でやっていく結束がさらに固まったように思う。ffab0がどうなるかは一定の結論が出た段階でまた書きたい。
■3週目 ~ 2人で開発することによる成功と失敗。本業による多忙・・ ~
ここは少し技術的な苦労が多かった。。まずKがgitを扱ったことがなかったのと、僕の伝え方がいまいちだったのが相まってコンフリクトからの激しいマージの嵐(笑) JSは最初Kに任せていたが途中から僕が巻き取ったのだがリファクタがかなり大変だった。。ただ、動くところまでやってくれていたので動きを見なくても綺麗に書きなおしてディレクトリ構造を綺麗にすればいい感じだったので助かったが。 あとは途中からKにもサーバーサイドで簡単なところは任せたのだが僕の中でのコーディング規約みたいなものを全く伝えていなかったのでお互いに苦労がうまれた。。ただbitbucketを使ってコミットを追いながらコメントしていたので修正はなんとかできた(それほどお互いに交わり合う実装が無いと思ったのでブランチ切ったりプルリクやったりはしなかった)
さらに仕事でのリリースがあってこっちに使える時間があまりなかった。ただそれは先ほど重要といった期限を守るという絶対の約束を守るという気持ちでなんとかした。自分が大変でも相方がちゃんとサービスを前進させてくれているというのはかなり気持ち的にも助けになる。
■4週目 ~ リリースまでの長い道のり ~
これは僕がrailsのassets piplineでつまったせいがかなりの比重を占める・・・。。それからどうしてもバグがでまくる。最後はずっとハングアウトで画面共有しながらお互いに指摘・説明・修正を繰り返してリリースまでこぎつけた、という感じだった。1年ほど前にCTOに「サービスは出来たと思ったところでだいたい2割くらい。あと8割残ってる」と言われたことがあるのでが、まさに今回もそんな感じだった・・。ここはちゃんとドキュメント化して無理なくできるようにしたい。
----
こんな感じの1ヶ月間でした。これからはリーンな感じで徐々にこのPositionStrategyをブラッシュアップしつつ、ffab0は1ヶ月1サービスを目標にしているので来月あたりまた新たなサービスを世に出せればと思っています。(ちなみに今週はこのブログに書いた企画MTGを再び実施予定)
...
最後に、シェアの時代なのでKPT法で振り返った僕らの反省会を載せておきます
KPT:
【K】
役割分担(デザインとサーバーサイド)
マイルストーンの置き方(この日までにこうしようという期日をしっかり定めて守った)
お互いが進めてる感がわかった(ツイッターとか通して)
ハングアウト
合宿形式で一旦認識を深め合ったのが良かった
サーバーサイド担当からのデザインに対する感想・指摘・アドバイスがあったこと
Kがrailsとコンソールを触れるようになったこと
slimいいね
【P】
gitのやりとりがうまくいってない(Kがもっと覚えるべき by K)
cssとslimをもっときれいに書く。今後はsassで書く
cssの構造を練ってからとりかかるべきだった
認識のすり合わせができていない(コードレベルでのズレが多くて書きなおすことが多々)
モデルやコントローラーの書き方(K)
本番リリース
いらないgemとか早めに消しとくべき
本番を意識した書き方ができていない&むずい。認識がなかった。
【T】
もっとKがサーバーサイド触る
ブランチ切ってプルリクを機能させる
技術をパッケージ化したい
年末年始合宿
ビジネスをもう少し考えてやりたい(マネタイズ、集客など)
⇛ 作るときにどういうビジネスモデルで行くのか、どういう集客方法でやるのかを
考えてから作る
Amon2でTest::mysqld
Amon2使ってるといいながらAmon2に関係すること書いてなかったので。。
環境:CentOS6.4
Perl5.16.3 ( plenv )
Amon2 ( flavor=basic )
MySQL5.6.10
Test::mysqldのインストール
$ cpanm Test::mysqld
※ 実はここは超ハマったのですがまだちゃんと原因究明をできていないので後日。。
configにテスト環境を書いていきます。dbnameのところをTest::mysqldが生成するものを代入できるように下記のような感じで環境変数を受け取るようにしておきます。
$vim config/test.pl 1 use File::Spec; 2 use File::Basename qw(dirname); 3 +{ 4 'DBI' => [ 5 $ENV{TEST_DSN}, 6 'root', 7 '', 8 +{ 9 mysql_enable_utf8 => 1, 10 } 11 ], 12 };
次にtディレクトリ下にテストファイルを作ります。
$ vim t/test_mysql.t 1 use strict; 2 use warnings; 3 use utf8; 4 use Test::More; 5 use DBI; 6 use Test::mysqld; 7 use App名; 8 use Data::Dumper; 9 use FindBin::libs; 10 11 BEGIN{ $ENV{PLACK_ENV} = 'test' }; 12 13 my $c = App名->bootstrap; 14 15 use_ok("Test::mysqld"); 16 17 my $mysqld = Test::mysqld->new( 18 my_cnf => { 19 'skip-networking' =>'', 20 } 21 ) or plan skip_all => $Test::mysqld::errstr; 22 23 $ENV{TEST_DSN} = $mysqld->dsn; 24 25 my $sql = "$FindBin::Bin/../sql/mysql.sql"; 26 system "mysql --socket $mysqld->{my_cnf}{socket} -uroot test < $sql";
11行目のBEGINで環境変数に'test'を代入してtest.plを参照するようにしています。
Amon2は自分の環境を$ENV{PLACK_ENV}で持っているのでそこに代入しています。
23行目で先ほどのconfigファイルで設定したところに$mysqld->dsnを代入していますが、これがTest::mysqldが自動生成してくれるdbの名前であったりsocketであったりの情報です。
26行目でシステムのコマンドを使ってデータ(テーブルのcreate情報など)を代入していますが、ここでソケットを設定しないと普段使っているmysqlのソケットを見に行って普段のDBの方のデータが変わってしまったりして元も子もないので注意です。
※ちなみに調べるとこういう現象もあるらしいので注意してください。
1スクリプト内で環境変数 PLACK_ENV を切り替える際の注意
あとは普通にアプリのルートディレクトリでproveなりperl -Ilibなりして実行してみてください。
crontabでplenvとcartonを使ってperlファイルを定期実行
めちゃめちゃハマったので備忘録も兼ねてメモ
環境
OS:CentOS6.4
Perl:5.16.3(plenv使用。システムperlは5.10.1)
cartonを使用している。一般ユーザー名はhomepage
今回はAmon2で作っているアプリのscript/apiディレクトリ内のapi.plファイルをcronで実行しました。
ちなみにアプリのトップディレクトリはこんな感じ。
➜ App git:(dev-branch) ✗ ls Build.PL README.md api_script.sh app.psgi carton.lock config cpanfile lib local script setup.sh sql static t tmpl xt
/etc/crontabのもともとの中身は下記の通り
SHELL=/bin/bash PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root HOME=/ # For details see man 4 crontabs # Example of job definition: # .---------------- minute (0 - 59) # | .------------- hour (0 - 23) # | | .---------- day of month (1 - 31) # | | | .------- month (1 - 12) OR jan,feb,mar,apr ... # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat # | | | | | # * * * * * user-name command to be executed
さて実行させていくわけですが、最初の僕のアプローチ
$ sudo mkdir /var/log/cron.log
でログディレクトリを作っておき(/var/log/cronにはcronを実行したことしかログででないので)アプリのルートディレクトリにシェルファイルを作成
$ vim api.sh export PATH="$HOME/.plenv/bin:$PATH" eval "$(plenv init -)" exec perl -Ilib /アプリルートディレクトリへの絶対パス/script/api/api.pl
そしてcrontabには下記を記述
*/01 * * * * homepage /bin/sh api.sh perl -Ilib /アプリルートディレクトリへの絶対パス/script/api/api.pl > /var/log/cron.log/app.log 2> /var/log/cron.log/app_error.log
で、まずログが出ない(笑)
ownerとgroupがrootで書き込み権限がない状態だったのでcron.logディレクトリを一般ユーザーであるhomepageに変更
Can't locate App.pm in @INC ~~~~
というようなエラー。
ここでplenvで実行するコマンドじゃないしcarton書いてないと気づいたので・・・
シェルスクリプトの中身を変更
$ vim api.sh export PATH="$HOME/.plenv/bin:$PATH" eval "$(plenv init -)" exec plenv exec carton exec perl -Ilib /アプリルートディレクトリへの絶対パス/script/api/api.pl
plenvが無いといわれる。。仕方がないので
export PATH="/home/homepage/.plenv/bin:$PATH"
に変更。
cronが実行されるのを待つと次は
(ちょっとエラーログを紛失してしまったのですが・・・)
Ther is no command carton in plenv
的な、plenvはあるけどplenvのコマンドとしてcartonなんて無いよというエラーが。
記されているエラーログの .plenv/versions/5.16.3/binに行ってlsしてみると
cartonは完全に存在している。。なぜ。。。(T_T)
ちなみにexec plenv exec -- carton exec perl ~~
などとやっても"--"なんてコマンドplenvに無いよと言われる。
ここは超ハマったのですが、何が原因だったのかというとまず
crontab内の
SHELL=/bin/bash PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root HOME=/
ここの部分でHOME=/としているせいでcron実行の時に/.plenv~を見に行ってしまっていました。なのでそこにはcartonも無ければ--もないと。
ちなみにエラーログに
No such file or directory at /home/homepage/.plenv/bin/plenv line 75.
とあるので中身をみてみると
54 sub CMD_exec { 55 home_init(); 56 57 my $bin = shift @ARGV or show_help('exec'); 58 59 my ($version, $file) = detect_version(); 60 61 if ($version eq 'system') { 62 # remove shims path from ENV[PATH]. 63 $ENV{PATH} = join( 64 ':', 65 grep { File::Spec->canonpath($_) ne File::Spec->canonpath("$PLENV_HOME/shims/") } File::Spec->path() 66 ); 67 } else { 68 my $bindir = "$PLENV_HOME/versions/$version/bin"; 69 unless (-x File::Spec->catfile($bindir, $bin)) { 70 die "[plenv] There is no $bin in $bindir.(determined by @{[ $file || '-' ]})\n"; 71 } 72 $ENV{PATH}="$bindir:$ENV{PATH}"; 73 } 74 exec $bin, @ARGV; 75 die $!; 76 }
ここでelseにはいって勝手に/.plenv/~~があるような形になっているっぽいです。
だからThere is no 'carton' in /.plenv~~となっていたわけですね。
このせいでplenvは読み取ってくれているように感じてしまっていたわけです。
と、一旦こんな感じなのですがもうちょっとちゃんと書きに行くと
シェルスクリプトの中を
1 cd /home/homepage/アプリルートまでのパス 2 export PATH="$HOME/.plenv/bin:$PATH" 3 eval "$(plenv init -)" 4 5 exec $*
として、crontabの中身の方にコマンドを書いておくようにできます。
(1行目でcdコマンドを書いているのはアプリルートディレクトリで実行しなければIlibが生きないためです)
SHELL=/bin/bash PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root HOME=/home/homepage 0 0 * * * homepage /bin/sh /アプリルートディレクトリの絶対パス/api.sh plenv exec carton exec -Ilib -- perl /アプリルートディレクトリの絶対パス/script/api/api.pl > /var/log/cron.log/api.log 2> /var/log/cron.log/api_error.log
といった感じです。
plenvはなかなかというか超ややこしい・・・。ただ普段パスとかをあまり意識できていなかったので勉強になりました。
=== 補足 ===
perlがシステムのバージョンの方を使われているんじゃないかというのに気づいて調べるために使ったクーロンの中身
*/01 * * * * homepage /bin/sh /home/homepage/アプリまでのルート/api.sh perl -V > /home/homepage/test.log 2> /home/homepage/test_error.log
これでシステムperlの方を使っていることがログで確認できました。
@INCの中身 & Perlでリファレンスをメソッドに渡す理由
「Can't locate ~~.pm in @INC」とかでcpanmでモジュール入れるとかいろいろあると思うのですが、その際に@INCの中身ってどうなってるんだろうと思ってました。
アプリのルートディレクトリで
$ perl -Ilib -e "use Data::Dumper; print Dumper \@INC" $VAR1 = [ 'lib', '/home/homepage/.plenv/versions/5.16.3/lib/perl5/site_perl/5.16.3/i686-linux', '/home/homepage/.plenv/versions/5.16.3/lib/perl5/site_perl/5.16.3', '/home/homepage/.plenv/versions/5.16.3/lib/perl5/5.16.3/i686-linux', '/home/homepage/.plenv/versions/5.16.3/lib/perl5/5.16.3', '.' ];
見れました。ちなみにIlibでlibディレクトリのパスを通しているという感じです。ためしに-Ilibを抜いて実行してみるとlibがなくなっていることがわかります。
Rubyやってるときはirbとかrails consoleとかでいろいろ確認できたのですが、perlもperl -eを使えばいろいろできそう。
ちなみにperlのリファレンスもコピーせずにその値を参照しに行くということはわかっていなかったのですが、いざコード内で使うと把握しづらい(データ構造も含めて)と思っていたのですが、少し明かりが。
例えばメソッドに対して配列を引数として渡したいとき
sub example { my ($a, $b) = @_; } my @array = ('hoge', 'fuga'); example('1', @array);
とやってしまうとhogeしか渡らなくてfugaは無視されます。さらにDBから取ってきたものをこの形で渡すと何個引数があってもたりない・・・。
なので
my @array = ('hoge', 'fuga'); example('1', \@array);
と、リファレンスで渡してやることで
sub example { my ($a, $b) = @_; print $b->[0]; }
とかでできるわけですね!
普段なんとなくここはリファレンスでいけたみたいな形でガンガン開発を進めていたのですが、ちゃんと理由が把握出来ました。勉強になりました。。