Upgrade to Pro — share decks privately, control downloads, hide ads and more …

アプリを書くときみんなModelはどうしてるの? って話

acidlemon
December 16, 2015

アプリを書くときみんなModelはどうしてるの? って話

Gotanda.pm #7 vs Yokohama.pm #13 (2015/12/16)のスライドです

acidlemon

December 16, 2015
Tweet

More Decks by acidlemon

Other Decks in Technology

Transcript

  1. ޒ൓ాͷΈͳ͞Μ͜Μ͹Μ͸ w ԣ඿ͷձ͔ࣾΒ΍͖ͬͯ·ͨ͠ ॅ·͍͸ח૔Ͱ͢  w 1FSM(PMBOH$ ϓϩάϥϚʔ كʹ3VCZͱ+BWBTDSJQU 

    w 8FBSFIJSJOHTFFXXXLBZBDDPNSFDSVJU !BDJEMFNPO .BTBUPTIJ,BXB[PF 4PGUXBSF&OHJOFFS BU,":"$*OD
  2. ΧϠοΫͷ8FCαʔϏε w 1FSMͩͱ͜Ε·Ͱ͜Μͳߏ੒Ͱͨ͠ w 8"'͸"SL $BUBMZTUMJLF QPXFSFECZ  w 03.͸%#*Y$MBTT


    Ұ෦ྫ֎༗Γʜ-PCJ͸5FOHͰ͢  w 0014ZTUFN͸.PVTF w 0CKFDU$POUBJOFSʹͳΜ͔৭ʑԡ͠ࠐΜͰΔ
 ͨͱ͑͹%#3FEJT઀ଓͷӬଓԽͳͲ
  3. $POUSPMMFS‎4FSWJDF # MyApp::Web::Dispatcher.pm base 'MyApp::Controller'; post 'api/game/start' => "Game#start"; #

    MyApp::Controller::Game.pm sub start { my ($class, $c) = @_; # FormValidator::LiteͰݕূ͢Δ(লུ) my $game_id = $c->req->parameter('game_id'); my $player_id = $c->session->get('player_id'); # Service Endpoint΁౤͛Δ my ($result, $err); try { $result = MyApp::Service::Game->start( game_id => $game_id, player_id => $player_id, ); } catch { $err = $_; }; $c->render_json({ error => $err }) if $err; return $c->render_json({ result => $result }); }
  4. 4FSWJDF‎.PEFM w 4FSWJDFͷ࢓ࣄ w ύϥϝʔλΛݩʹ.PEFMΦϒδΣΫτΛੜ੒ w .PEFMΦϒδΣΫτͷϩδοΫΛݺͼग़͢ w ϩδοΫͷ࣮ߦ݁ՌΛجʹɺ"1*࢓༷ʹଈ͠ ͨ໭Γ஋Λ࡞Δ

    ༨ஊੲ͸ࣾ಺ͰʮҰͭͷ"1*&OEQPJOUʹରԠͯ͠Ұͭͷ.PEFMͷ&OUSZ1PJOUΛ༻ҙ͢΂͖͡ΌͶ ʯͬͯ ओுͯͨ͠Μ͚ͩͲɺΈΜͳʮ͑ʔʯͬͯײ͡Ͱߏͬͯ͘Εͳ͔ͬͨͷͰ4FSWJDF͍ͬͯ͏໊લۭؒʹ෼͚ͨ
  5. 4FSWJDF‎.PEFM # MyApp::Service::Game.pm sub start { my ($class, %args) =

    @_; my $game = MyApp::Model::Game->new( id => $args{game_id} ); my $player = MyApp::Model::Player->new( id => $args{player_id} ); # ήʔϜʹ༩͑ΔϥϯμϜύϥϝʔλΛ༻ҙ state $generator = Math::Random::MT->new; my $randval = $gen->rand(); # ήʔϜ։࢝ͩʔ! my $result = $game->start( player => $player, seed => $randval, ); return $result; }
  6. ༨ஊͷଓ͖ w લϖʔδͷྫɺઃܭͷ࢓ํͱͯ͠௨Γ͋Γ·͢ w ࣾ಺ͩͱجຊతʹޙऀʹͳ͍ͬͯΔ͚Ͳɺݸਓతʹ͸޷͖Ͱ͸ແ͍͔ͳʔ w 1MBZFS͕ߦಈͷओମ͔ͩΒͱ͍͏ͷ͕ޙऀʹͳͬͯΔཧ༝ͳΜ͚ͩͲɺ ͦΕΛݴ͍ग़͢ͱηογϣϯඞਢͷ"1*͸શ෦ϓϨΠϠʔ͕ૢ࡞͢ΔͷͰ ͦ͏͍͏ݺͼग़͠ʹͳͬͯ͠·͏ 1MBZFS͕ංେԽ͢Δ

     w ࣮ࡍʹͦͷϓϩδΣΫτͷίʔυΛௐ΂ͯΈͨͱ͜Ζ6TFS ͷύοέʔ δ͕ݸ͘Β͍͋ͬͯ.PEFMͷίʔυͷ͕6TFS ͩͬͨ w ͦ͏͍͏ྫ͸4FSWJDFʹ෼཭͢Ε͹͍͍͔ͳͱࢥͬͯ͜ͷ࿩Λ͍ͯ͠·͢ my $result = $game->start( player => $player, seed => $randval, ); my $result = $player->game->start( game => $game, seed => $randval, ); 1MBZFSΛҾ਺ʹ(BNFΛݺͿ (BNFΛҾ਺ʹ1MBZFS(BNFΛݺͿ
  7. .PEFMͷྫɺ۩ମతʹ w 3PXͱͷύοέʔδ w %#ͷ3PXΛIBTͯ͠Ӆณ͢Δύλʔϯ w %#3FEJTΛ࢖ͬͨϩδοΫύοέʔδ w VTFSςʔϒϧΛFNBJMͰݕࡧɺॏෳνΣοΫΈ͍ͨͳέʔε
 %#্Ͳ͔͜ʹैଐ͍ͯ͠Δ

    FNBJMΛओମͱͯ͠ѻ͏ w ֎෦"1*Λ༏͘͠แΈࠐΉύοέʔδ w ՝ۚݕূ"1*΍1VTI௨஌EBFNPOΛୟ͘ͳͲ w ಛఆͷ৚݅Λຬͨ͢σʔλू߹શମΛѻ͏ύοέʔδ w ϥϯΩϯάσʔλͳͲ
  8. 3PXͱͷͷྫ # MyApp::Model::Player.pm use Moo; has id => ( is

    => 'ro', ); has _row => ( is => 'lazy', default => sub { my $self = shift; my $db = MyApp->context->db; # Teng my $row = $db->single('player', { id => $self->id }); return $row; }; ); sub userinfo { my $self = shift; my $hash = $self->_row->get_columns; delete $hash->{api_token}; # ΈΜͳʹ͸φΠγϣͩΑ return $hash; }
  9. 3PXͱͷͷྫ ଓ͖ # MyApp::Model::Player.pm (ଓ͖) # ৽نϓϨΠϠʔͷੜ੒͸ΫϥεϝιουͰ΍Δ͍ͧ sub register {

    my ($class, %args) = @_; my $row = MyApp::DB::Table::Player->insert( name => $args{name}, gender => $args{gender}, age => $args{age}, rank => 1, exp => 0, ); # ΦϒδΣΫτΛ࡞ͬͯฦ͢ return $class->new( id => $row->id, _rows => $row, ); } # usage my $new_player = MyApp::Model::Player->register( name => 'acidlemon', gender => 'male', age => 0x21 );
  10. ϩδοΫύοέʔδ # MyApp::Model::Email.pm use Moo; use Carp; use Digest::SHA qw(hmac_sha256_hex);

    has email => ( is => 'ro', required => 1, ); sub find_player { my $self = shift; my $row = MyApp::DB::Table::Player->single( email => $self->email ); croak 'cannot find user: ' . $self->email; return MyApp::Model::User->new( id => $row->id, _row => $row ); } sub calc_email_digest_hash { my $self = shift; state $salt = 'salt_from_hakata'; my $digest = hmac_sha256_hex($self->email, $salt); return $digest; }
  11. ֎෦"1*Λ༏͘͠แΈࠐΉ # MyApp::Model::VerifyPurchase.pm use Moo; use Furl; use JSON::XS; has

    furl => ( is => 'lazy', default => sub { Furl->new( timeout => 10 ); }, ); sub verify_ios { my ($self, $receipt_b64) = @_; my $payload = { 'receipt-data' => $receipt_b64 }; my $res = $self->furl->post( 'https://buy.itunes.apple.com/verifyReceipt', [], encode_json $payload, ); if ($res->status != 200) { croak 'HTTP Status Error: ' . $res->status; } my $res_json = eval { decode_json $res->body }; # ҎԼলུ… }
  12. σʔλू߹Λѻ͏ # MyApp::Model::Season::Ranking.pm use Moo; use MyApp::Model::LeaderBoard; # Redis::LeaderBoardΛwrapͨ͠΍ͭ has

    season_id => ( is => 'ro', required => 1, ); has leader_board => ( is => 'lazy', default => sub { my $leader_board = MyApp::Model::LeaderBoard->new( key => sprintf('season:ranking:%s', $self->season_id), ); } );
  13. σʔλू߹Λѻ͏ ଓ͖ # MyApp::Model::Season::Ranking.pm (ଓ͖) # Redis͔ΒRankingΛऔ͖ͬͯͯDBʹೖΕͯ֬ఆ͢Δ! sub fix_ranking {

    my $self = shift; my $rankings = $self->leader_board->rankings( offset => 0, limit => 3000, ); my $txn = MyApp::DB->txn_scope; for my $player_score (@$rankings) { MyApp::DB::Table::PlayerSeasonRanking->insert( season_id => $self->season_id, player_id => $player_score->{member}, rank => $player_score->{rank}, score => $player_score->{score}, ); } # ΄͔ʹ΋ͳΜ͔ใु͋͛ͨΓͱ͔… $txn->commit; }