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

Ruby after Rails

Ernie Miller
November 18, 2014

Ruby after Rails

What happens to Ruby when Rails dies?

Ruby rode the Rails rocketship to worldwide renown. While a handful of developers were writing Ruby code before Rails came along, many (if not most) of us owe Rails a debt of gratitude for taking Ruby mainstream, thus allowing us to make a living writing code in a language we love.

However, the application design preferences expressed by Rails are falling out of favor. Our apps have more complex domain logic that becomes burdensome to manage by following "The Rails Way."

Is that it, then? Does transitioning from Rails mean leaving Ruby behind?

Ernie Miller

November 18, 2014
Tweet

More Decks by Ernie Miller

Other Decks in Programming

Transcript

  1. THE LOCAL ANIMAL POLISH SAUSAGE / BRAISED PORK / FRIED

    EGG MUSTARD + MOLASSES GLAZE / ARUGULA
  2. FUN FACT: In the mid-1990s, Poland had a severe dragon

    infestation. Open season was declared, and dragon hunting became a popular sport. The dragon population was hunted to extinction, but the surplus of meat sent Polish sausage production to record highs. Some of these sausages survive to this day.
  3. FUN FACT: In the mid-1990s, Poland had a severe dragon

    infestation. Open season was declared, and dragon hunting became a popular sport. The dragon population was hunted to extinction, but the surplus of meat sent Polish sausage production to record highs. Some of these sausages survive to this day. * Neither fun nor factual nature of Fun Facts are guaranteed. Use at your own risk. *
  4. =

  5. = /

  6. =

  7. +

  8. + =

  9. – Dad “I owe you an apology. I used to

    think you were lazy. Now I realize you were just selective.”
  10. sub _parse_predicate_in_handler { my( $flag, $score)= @_[1..2]; $_[0]=~ s{( ($REG_STRING)

    # strings |\@($REG_TAG_NAME)(\s* $REG_MATCH \s* $REG_REGEXP) # @att and regexp |\@($REG_TAG_NAME)(?=\s*(?:[><=!])) # @att followed by a comparison operator |\@($REG_TAG_NAME) # @att (not followed by a comparison operator) |=~|!~ # matching operators |([><]=?|=|!=)(?=\s*[\d+-]) # test before a number |([><]=?|=|!=) # test, other cases |($REG_FUNCTION) # no arg functions # this bit is a mess, but it is the only solution with this half-baked parser |(string\(\s*$REG_NAME\s*\)\s*$REG_MATCH\s*$REG_REGEXP) # string( child)=~ /regexp/ |(string\(\s*$REG_NAME\s*\)\s*$REG_COMP\s*$REG_STRING) # string( child) = "value" (or other test) |(string\(\s*$REG_NAME\s*\)\s*$REG_COMP\s*$REG_NUMBER) # string( child) = nb (or other test) |(and|or) # |($REG_NAME(?=\s*(and|or|$))) # nested tag name (needs to be after all other unquoted strings) |($REG_TAG_IN_PREDICATE) # nested tag name (needs to be after all other unquoted strings) )} { my( $token, $str, $att_re_name, $att_re_regexp, $att, $bare_att, $num_test, $alpha_test, $func, $str_regexp, $str_test_alpha, $str_test_num, $and_or, $tag) = ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14); $score->{predicates}++; # store tests on text (they are not always allowed) if( $func || $str_regexp || $str_test_num || $str_test_alpha ) { $flag->{test_on_text}= 1; } if( defined $str) { $token } elsif( $tag) { qq{(\$elt->{'$ST_ELT'} && \$elt->{'$ST_ELT'}->has_child( '$tag'))} } elsif( $att) { $att=~ m{^#} ? qq{ (\$elt->{'$ST_ELT'} && \$elt->{'$ST_ELT'}->{att}->{'$att'})} : qq{\$elt->{'$att'}} } elsif( $att_re_name) { $att_re_name=~ m{^#} ? qq{ (\$elt->{'$ST_ELT'} && \$elt->{'$ST_ELT'}->{att}->{'$att_re_name'}$att_re_regexp)} : qq{\$elt->{'$att_re_name'}$att_re_regexp} } # for some reason Devel::Cover flags the following lines as not tested. They are though. elsif( $bare_att) { $bare_att=~ m{^#} ? qq{(\$elt->{'$ST_ELT'} && defined(\$elt->{'$ST_ELT'}->{att}->{'$bare_att'}))} : qq{defined( \$elt->{'$bare_att'})} } elsif( $num_test && ($num_test eq '=') ) { "==" } # others tests are unchanged elsif( $alpha_test) { $PERL_ALPHA_TEST{$alpha_test} } elsif( $func && $func=~ m{^string}) { "\$elt->{'$ST_ELT'}->text"; } elsif( $str_regexp && $str_regexp =~ m{string\(\s*($REG_TAG_NAME)\s*\)\s*($REG_MATCH)\s*($REG_REGEXP)}) { "defined( _first_n { \$_->text $2 $3 } 1, \$elt->{'$ST_ELT'}->_children( '$1'))"; } elsif( $str_test_alpha && $str_test_alpha =~ m{string\(\s*($REG_TAG_NAME)\s*\)\s*($REG_COMP)\s*($REG_STRING)}) { my( $tag, $op, $str)= ($1, $2, $3); $str=~ s{(?<=.)'(?=.)}{\\'}g; # escape a quote within the string $str=~ s{^"}{'}; $str=~ s{"$}{'}; "defined( _first_n { \$_->text $PERL_ALPHA_TEST{$op} $str } 1, \$elt->{'$ST_ELT'}->children( '$tag'))"; } elsif( $str_test_num && $str_test_num =~ m{string\(\s*($REG_TAG_NAME)\s*\)\s*($REG_COMP)\s*($REG_NUMBER)}) { my $test= ($2 eq '=') ? '==' : $2; "defined( _first_n { \$_->text $test $3 } 1, \$elt->{'$ST_ELT'}->children( '$1'))"; } elsif( $and_or) { $score->{tests}++; $and_or eq 'and' ? '&&' : '||' ; } else { $token; } }gexs; } Twig.pm, L1765-1823
  11. sub _parse_predicate_in_handler { my( $flag, $score)= @_[1..2]; $_[0]=~ s{( ($REG_STRING)

    # strings |\@($REG_TAG_NAME)(\s* $REG_MATCH \s* $REG_REGEXP) # @att and regexp |\@($REG_TAG_NAME)(?=\s*(?:[><=!])) # @att followed by a comparison operator |\@($REG_TAG_NAME) # @att (not followed by a comparison operator) |=~|!~ # matching operators |([><]=?|=|!=)(?=\s*[\d+-]) # test before a number |([><]=?|=|!=) # test, other cases |($REG_FUNCTION) # no arg functions # this bit is a mess, but it is the only solution with this half-baked parser |(string\(\s*$REG_NAME\s*\)\s*$REG_MATCH\s*$REG_REGEXP) # string( child)=~ /regexp/ |(string\(\s*$REG_NAME\s*\)\s*$REG_COMP\s*$REG_STRING) # string( child) = "value" (or other test) |(string\(\s*$REG_NAME\s*\)\s*$REG_COMP\s*$REG_NUMBER) # string( child) = nb (or other test) |(and|or) # |($REG_NAME(?=\s*(and|or|$))) # nested tag name (needs to be after all other unquoted strings) |($REG_TAG_IN_PREDICATE) # nested tag name (needs to be after all other unquoted strings) )} { my( $token, $str, $att_re_name, $att_re_regexp, $att, $bare_att, $num_test, $alpha_test, $func, $str_regexp, $str_test_alpha, $str_test_num, $and_or, $tag) = ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14); $score->{predicates}++; # store tests on text (they are not always allowed) if( $func || $str_regexp || $str_test_num || $str_test_alpha ) { $flag->{test_on_text}= 1; } if( defined $str) { $token } elsif( $tag) { qq{(\$elt->{'$ST_ELT'} && \$elt->{'$ST_ELT'}->has_child( '$tag'))} } elsif( $att) { $att=~ m{^#} ? qq{ (\$elt->{'$ST_ELT'} && \$elt->{'$ST_ELT'}->{att}->{'$att'})} : qq{\$elt->{'$att'}} } elsif( $att_re_name) { $att_re_name=~ m{^#} ? qq{ (\$elt->{'$ST_ELT'} && \$elt->{'$ST_ELT'}->{att}->{'$att_re_name'}$att_re_regexp)} : qq{\$elt->{'$att_re_name'}$att_re_regexp} } # for some reason Devel::Cover flags the following lines as not tested. They are though. elsif( $bare_att) { $bare_att=~ m{^#} ? qq{(\$elt->{'$ST_ELT'} && defined(\$elt->{'$ST_ELT'}->{att}->{'$bare_att'}))} : qq{defined( \$elt->{'$bare_att'})} } elsif( $num_test && ($num_test eq '=') ) { "==" } # others tests are unchanged elsif( $alpha_test) { $PERL_ALPHA_TEST{$alpha_test} } elsif( $func && $func=~ m{^string}) { "\$elt->{'$ST_ELT'}->text"; } elsif( $str_regexp && $str_regexp =~ m{string\(\s*($REG_TAG_NAME)\s*\)\s*($REG_MATCH)\s*($REG_REGEXP)}) { "defined( _first_n { \$_->text $2 $3 } 1, \$elt->{'$ST_ELT'}->_children( '$1'))"; } elsif( $str_test_alpha && $str_test_alpha =~ m{string\(\s*($REG_TAG_NAME)\s*\)\s*($REG_COMP)\s*($REG_STRING)}) { my( $tag, $op, $str)= ($1, $2, $3); $str=~ s{(?<=.)'(?=.)}{\\'}g; # escape a quote within the string $str=~ s{^"}{'}; $str=~ s{"$}{'}; "defined( _first_n { \$_->text $PERL_ALPHA_TEST{$op} $str } 1, \$elt->{'$ST_ELT'}->children( '$tag'))"; } elsif( $str_test_num && $str_test_num =~ m{string\(\s*($REG_TAG_NAME)\s*\)\s*($REG_COMP)\s*($REG_NUMBER)}) { my $test= ($2 eq '=') ? '==' : $2; "defined( _first_n { \$_->text $test $3 } 1, \$elt->{'$ST_ELT'}->children( '$1'))"; } elsif( $and_or) { $score->{tests}++; $and_or eq 'and' ? '&&' : '||' ; } else { $token; } }gexs; } Twig.pm, L1765-1823 (of 14006)
  12. sub _parse_predicate_in_handler { my( $flag, $score)= @_[1..2]; $_[0]=~ s{( ($REG_STRING)

    # strings |\@($REG_TAG_NAME)(\s* $REG_MATCH \s* $REG_REGEXP) # @att and regexp |\@($REG_TAG_NAME)(?=\s*(?:[><=!])) # @att followed by a comparison operator |\@($REG_TAG_NAME) # @att (not followed by a comparison operator) |=~|!~ # matching operators |([><]=?|=|!=)(?=\s*[\d+-]) # test before a number |([><]=?|=|!=) # test, other cases |($REG_FUNCTION) # no arg functions # this bit is a mess, but it is the only solution with this half-baked parser |(string\(\s*$REG_NAME\s*\)\s*$REG_MATCH\s*$REG_REGEXP) # string( child)=~ /regexp/ |(string\(\s*$REG_NAME\s*\)\s*$REG_COMP\s*$REG_STRING) # string( child) = "value" (or other test) |(string\(\s*$REG_NAME\s*\)\s*$REG_COMP\s*$REG_NUMBER) # string( child) = nb (or other test) |(and|or) # |($REG_NAME(?=\s*(and|or|$))) # nested tag name (needs to be after all other unquoted strings) |($REG_TAG_IN_PREDICATE) # nested tag name (needs to be after all other unquoted strings) )} { my( $token, $str, $att_re_name, $att_re_regexp, $att, $bare_att, $num_test, $alpha_test, $func, $str_regexp, $str_test_alpha, $str_test_num, $and_or, $tag) = ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14); $score->{predicates}++; # store tests on text (they are not always allowed) if( $func || $str_regexp || $str_test_num || $str_test_alpha ) { $flag->{test_on_text}= 1; } if( defined $str) { $token } elsif( $tag) { qq{(\$elt->{'$ST_ELT'} && \$elt->{'$ST_ELT'}->has_child( '$tag'))} } elsif( $att) { $att=~ m{^#} ? qq{ (\$elt->{'$ST_ELT'} && \$elt->{'$ST_ELT'}->{att}->{'$att'})} : qq{\$elt->{'$att'}} } elsif( $att_re_name) { $att_re_name=~ m{^#} ? qq{ (\$elt->{'$ST_ELT'} && \$elt->{'$ST_ELT'}->{att}->{'$att_re_name'}$att_re_regexp)} : qq{\$elt->{'$att_re_name'}$att_re_regexp} } # for some reason Devel::Cover flags the following lines as not tested. They are though. elsif( $bare_att) { $bare_att=~ m{^#} ? qq{(\$elt->{'$ST_ELT'} && defined(\$elt->{'$ST_ELT'}->{att}->{'$bare_att'}))} : qq{defined( \$elt->{'$bare_att'})} } elsif( $num_test && ($num_test eq '=') ) { "==" } # others tests are unchanged elsif( $alpha_test) { $PERL_ALPHA_TEST{$alpha_test} } elsif( $func && $func=~ m{^string}) { "\$elt->{'$ST_ELT'}->text"; } elsif( $str_regexp && $str_regexp =~ m{string\(\s*($REG_TAG_NAME)\s*\)\s*($REG_MATCH)\s*($REG_REGEXP)}) { "defined( _first_n { \$_->text $2 $3 } 1, \$elt->{'$ST_ELT'}->_children( '$1'))"; } elsif( $str_test_alpha && $str_test_alpha =~ m{string\(\s*($REG_TAG_NAME)\s*\)\s*($REG_COMP)\s*($REG_STRING)}) { my( $tag, $op, $str)= ($1, $2, $3); $str=~ s{(?<=.)'(?=.)}{\\'}g; # escape a quote within the string $str=~ s{^"}{'}; $str=~ s{"$}{'}; "defined( _first_n { \$_->text $PERL_ALPHA_TEST{$op} $str } 1, \$elt->{'$ST_ELT'}->children( '$tag'))"; } elsif( $str_test_num && $str_test_num =~ m{string\(\s*($REG_TAG_NAME)\s*\)\s*($REG_COMP)\s*($REG_NUMBER)}) { my $test= ($2 eq '=') ? '==' : $2; "defined( _first_n { \$_->text $test $3 } 1, \$elt->{'$ST_ELT'}->children( '$1'))"; } elsif( $and_or) { $score->{tests}++; $and_or eq 'and' ? '&&' : '||' ; } else { $token; } }gexs; } Twig.pm, L1765-1823 (of 14006) search
  13. sub _parse_predicate_in_handler { my( $flag, $score)= @_[1..2]; $_[0]=~ s{( ($REG_STRING)

    # strings |\@($REG_TAG_NAME)(\s* $REG_MATCH \s* $REG_REGEXP) # @att and regexp |\@($REG_TAG_NAME)(?=\s*(?:[><=!])) # @att followed by a comparison operator |\@($REG_TAG_NAME) # @att (not followed by a comparison operator) |=~|!~ # matching operators |([><]=?|=|!=)(?=\s*[\d+-]) # test before a number |([><]=?|=|!=) # test, other cases |($REG_FUNCTION) # no arg functions # this bit is a mess, but it is the only solution with this half-baked parser |(string\(\s*$REG_NAME\s*\)\s*$REG_MATCH\s*$REG_REGEXP) # string( child)=~ /regexp/ |(string\(\s*$REG_NAME\s*\)\s*$REG_COMP\s*$REG_STRING) # string( child) = "value" (or other test) |(string\(\s*$REG_NAME\s*\)\s*$REG_COMP\s*$REG_NUMBER) # string( child) = nb (or other test) |(and|or) # |($REG_NAME(?=\s*(and|or|$))) # nested tag name (needs to be after all other unquoted strings) |($REG_TAG_IN_PREDICATE) # nested tag name (needs to be after all other unquoted strings) )} { my( $token, $str, $att_re_name, $att_re_regexp, $att, $bare_att, $num_test, $alpha_test, $func, $str_regexp, $str_test_alpha, $str_test_num, $and_or, $tag) = ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14); $score->{predicates}++; # store tests on text (they are not always allowed) if( $func || $str_regexp || $str_test_num || $str_test_alpha ) { $flag->{test_on_text}= 1; } if( defined $str) { $token } elsif( $tag) { qq{(\$elt->{'$ST_ELT'} && \$elt->{'$ST_ELT'}->has_child( '$tag'))} } elsif( $att) { $att=~ m{^#} ? qq{ (\$elt->{'$ST_ELT'} && \$elt->{'$ST_ELT'}->{att}->{'$att'})} : qq{\$elt->{'$att'}} } elsif( $att_re_name) { $att_re_name=~ m{^#} ? qq{ (\$elt->{'$ST_ELT'} && \$elt->{'$ST_ELT'}->{att}->{'$att_re_name'}$att_re_regexp)} : qq{\$elt->{'$att_re_name'}$att_re_regexp} } # for some reason Devel::Cover flags the following lines as not tested. They are though. elsif( $bare_att) { $bare_att=~ m{^#} ? qq{(\$elt->{'$ST_ELT'} && defined(\$elt->{'$ST_ELT'}->{att}->{'$bare_att'}))} : qq{defined( \$elt->{'$bare_att'})} } elsif( $num_test && ($num_test eq '=') ) { "==" } # others tests are unchanged elsif( $alpha_test) { $PERL_ALPHA_TEST{$alpha_test} } elsif( $func && $func=~ m{^string}) { "\$elt->{'$ST_ELT'}->text"; } elsif( $str_regexp && $str_regexp =~ m{string\(\s*($REG_TAG_NAME)\s*\)\s*($REG_MATCH)\s*($REG_REGEXP)}) { "defined( _first_n { \$_->text $2 $3 } 1, \$elt->{'$ST_ELT'}->_children( '$1'))"; } elsif( $str_test_alpha && $str_test_alpha =~ m{string\(\s*($REG_TAG_NAME)\s*\)\s*($REG_COMP)\s*($REG_STRING)}) { my( $tag, $op, $str)= ($1, $2, $3); $str=~ s{(?<=.)'(?=.)}{\\'}g; # escape a quote within the string $str=~ s{^"}{'}; $str=~ s{"$}{'}; "defined( _first_n { \$_->text $PERL_ALPHA_TEST{$op} $str } 1, \$elt->{'$ST_ELT'}->children( '$tag'))"; } elsif( $str_test_num && $str_test_num =~ m{string\(\s*($REG_TAG_NAME)\s*\)\s*($REG_COMP)\s*($REG_NUMBER)}) { my $test= ($2 eq '=') ? '==' : $2; "defined( _first_n { \$_->text $test $3 } 1, \$elt->{'$ST_ELT'}->children( '$1'))"; } elsif( $and_or) { $score->{tests}++; $and_or eq 'and' ? '&&' : '||' ; } else { $token; } }gexs; } Twig.pm, L1765-1823 (of 14006) search replace
  14. –Larry Wall, Programming Perl “We will encourage you to develop

    the three great virtues of a programmer: laziness, impatience, and hubris.”
  15. $ python3 Python 3.4.1 (default, May 19 2014, 13:10:29) [GCC

    4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import this The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those! >>>
  16. $ python3 Python 3.4.1 (default, May 19 2014, 13:10:29) [GCC

    4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import this The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those! >>>
  17. $ python3 Python 3.4.1 (default, May 19 2014, 13:10:29) [GCC

    4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import this The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those! >>> quit Use quit() or Ctrl-D (i.e. EOF) to exit >>> quit() $
  18. “Ruby stole everything good from Perl; [...] then Matz took

    the best of list processing from Lisp, and the best of OO from Smalltalk and other languages, and the best of iterators from CLU, and pretty much the best of everything from everyone. And he somehow made it all work together so well that you don't even notice that it has all that stuff.” –Steve Yegge, Tour de Babel (2004) http://steve.yegge.googlepages.com/tour-de-babel
  19. – Tony Arcieri “Celluloid solves some of the synchronization problems

    of multithreaded programs, but not all of them. It’s still possible to share objects sent in messages between Celluloid actors, and it’s possible for concurrent mutations in these objects go unnoticed. I don’t think I can solve these problems effectively without VM-level support in the form of the aforementioned proposed features to core Ruby.” http://tonyarcieri.com/2012-the-year-rubyists-learned-to-stop-worrying-and-love-the-threads
  20. – Tony Arcieri “Eventually you’re in a place where you’re

    trying to build a jet engine out of silly putty.”
  21. –Matz (2001) http://www.linuxdevcenter.com/pub/a/linux/2001/11/29/ruby.html “I believe people want to express themselves

    when they program. They don't want to fight with the language. Programming languages must feel natural to programmers. I tried to make people enjoy programming and concentrate on the fun and creative part of programming when they use Ruby.”