Slide 1

Slide 1 text

Ruby and Rails: The Bad Parts Volodymyr Melnyk Ruby On, Kharkiv 2015

Slide 2

Slide 2 text

1 minute of self- advertisement • Software craftsman with ~7 experience (~5y experience in Ruby). • Atheist • Libertarian and anarcho- capitalist. • Scientist and skeptic. • A man with business / economics mind.

Slide 3

Slide 3 text

Ruby’s Good Parts • It has high abilities in meta- programming • It’s good for Domain-Specific Languages • It allows to write prototypes very fast • It has a huge ecosystem with tons of gems and development tools.

Slide 4

Slide 4 text

Ruby’s Bad Parts or why Ruby isn’t brilliant

Slide 5

Slide 5 text

Ruby isn’t Static- typed Ruby has no typed signatures. This means that you need to do all type checks manually. x = 5 y = “string" lambda = ->{ x + y } # you will not get an # error until execution # next code line: lambda.call

Slide 6

Slide 6 text

Ruby has an unclear concept of Module In Ruby a module is a class which can’t have instances and can be included into another class or another class can be extended with it.

Slide 7

Slide 7 text

module InstanceMethods; end module ClassMethods; end class C include InstanceMethods extend ClassMethods end C.ancestors #=> [C, InstanceMethods, Object, Kernel, BasicObject] C.singleton_class.ancestors #=> [ # #, ClassMethods, #, # , Class, Module, Object, Kernel, # BasicObject # ]

Slide 8

Slide 8 text

Proc & Lambda In Ruby we have a Proc class and two types of its instances (it’s strange), but actually we need only one of them (as for me, I prefer lambdas).

Slide 9

Slide 9 text

p1 = Proc.new { |a, b| a + b } p2 = Proc.new { |a, b| return a + b } l1 = ->(a, b) { a + b } l2 = ->(a, b) { return a + b } p1.class # Proc l1.class # Proc p1.lambda? # false l1.lambda? # true def mtd(pobj, a, b = nil) args = b ? [a, b] : [a] puts "method code 1" puts pobj.call *args puts "method code 2" end

Slide 10

Slide 10 text

mtd p1, 1
 # method code 1
 # TypeError: nil can't be coerced into Fixnum mtd p1, 1, 3
 # method code 1
 # 4
 # method code 2 mtd p2, 1
 # method code 1
 # TypeError: nil can't be coerced into Fixnum mtd p2, 1, 3
 # method code 1
 # LocalJumpError: unexpected return

Slide 11

Slide 11 text

mtd l1, 1
 # method code 1
 # ArgumentError: wrong number of arguments (1 for 2) mtd l1, 1, 3
 # method code 1
 # 4
 # method code 2 mtd l2, 1
 # method code 1
 # ArgumentError: wrong number of arguments (1 for 2) mtd l2, 1, 3
 # method code 1
 # 4
 # method code 2

Slide 12

Slide 12 text

Ruby Standard Library is a ghetto Ruby Standard Library has a huge number of low-quality and useless libraries.

Slide 13

Slide 13 text

Abbrev require 'abbrev' %w{ car cone }.abbrev #=> {
 “car” => "car",
 “ca" => "car",
 “cone" => "cone",
 “con” => "cone",
 “co” => "cone"
 }

Slide 14

Slide 14 text

Delegator class SimpleDelegator < Delegator
 def initialize obj
 super
 @delegate_sd_obj = obj
 end def __getobj__
 @delegate_sd_obj
 end def __setobj__ obj
 @delegate_sd_obj = obj
 end
 end

Slide 15

Slide 15 text

Alternate syntax Ruby has a lot of alternate syntaxes for different things. Because of that Ruby code can look very differently.

Slide 16

Slide 16 text

{…} vs do … end
 ->() {…}, -> {}, lambda {|| }, lambda {} def mtd(a, b), def mtd a, b 
 0..10, 0…10
 %w{}, %w[], %w(), %w//, %w``, %w"", %w||
 %w[], %W[], %i[], %i[]
 //, %r//, %s[]
 '', ""


Slide 17

Slide 17 text

&& versus and n = 1 && n + 1 # NoMethodError: undefined # method `+' for nil n = 1 #=> 1 n = 2 && n + 1 #=> 2 n = 2 && n + 1 #=> 3 n = 2 && n + 1 #=> 4 n = 2 && n + 1 #=> 5 n #=> 5 n = 1 and n + 1 #=> 2 n = 1 #=> 1 n = 2 and n + 1 #=> 3 n = 2 and n + 1 #=> 3 n = 2 and n + 1 #=> 3 n = 2 and n + 1 #=> 3 n #=> 2

Slide 18

Slide 18 text

|| versus or n = 1
 n = nil || n + 2 #=> 3 n = 1
 n = nil or n + 2
 # NoMethodError: undefined
 # method `+' for nil

Slide 19

Slide 19 text

Values against expressions You should know!

Slide 20

Slide 20 text

Boolean Algebra Fail If && and || are boolean operators, why their in/out arguments aren’t booleans? true && "string" #=> "string" 1 || nil #=> 1

Slide 21

Slide 21 text

Ruby is not that strongly-typed language.

Slide 22

Slide 22 text

nil I call it my billion-dollar mistake. – Tony Hoare, inventor of ALGOL nil is • more code • checks • special cases • error handling
 • more specs / tests
 • more errors

Slide 23

Slide 23 text

Rails’es Bad Parts or why Ruby сan’t “Choo-choo!”

Slide 24

Slide 24 text

Polymorphic associations At first glance polymorphic associations look pretty well, but their usage result in a lot of problems when business logic is growing.

Slide 25

Slide 25 text

# Article
 has_many :comments, as: :commentable # Photo
 has_many :comments, as: :commentable # Comment
 belongs_to :commentable, polymorphic: true Comment.joins(:commentable) #Exception

Slide 26

Slide 26 text

Good schema • Article has one ArticleCommentable • ArticleCommentable belongs to Commentable • Commentable has many Comments • Comment belongs to Commentable

Slide 27

Slide 27 text

Leak of abstraction ActiveRecord forces us to use implementation details in business logic (associations, queries, joins, etc.) while what we need is to separate them from each other.

Slide 28

Slide 28 text

Solution: DDD Use Aggregate pattern to define associations and dependencies. Use Aggregate’s Root Entity to request associated entities.

Slide 29

Slide 29 text

Single Table Inheritance STI is the way to make troubles instead of relations (tables) and the superclass makes no sense in most cases.

Slide 30

Slide 30 text

Subclasse’s logic differences from its superclass, so you can’t use superclass to get a superset of all records in many cases. class Article < Publication
 has_many :comments
 
 # You can’t do this:
 Publication.order(published_at: :desc)
 .includes(:comments)
 .where(comments: {approved: true})

Slide 31

Slide 31 text

More problems resulted by STI • We can’t use DB-layer validation and constraints (uniqueness and NOT NULL, for example). • We need an additional “type” column for metadata, but AR models don’t protect it as a metadata-column. • Totally misused cases of STI, like states, statuses, roles (something changeable). class ProcessedOrder < Order
 end
 
 class Admin < User
 end

Slide 32

Slide 32 text

Good scheme • Article shouldn’t be inherited from Publication, but Article (Aggregate) is a composition over Publication and other entities. • ProcessedOrder shouldn’t be inherited from Order, but Order (Aggregate) is a composition over OrderItems, Delivery, Payment, etc. • Admin shouldn’t be inherited from User, but Admin (Aggregate) is a composition over User and other related entities.

Slide 33

Slide 33 text

Compositional approach is awesome!

Slide 34

Slide 34 text

ActiveRecord is a Golden Hammer ActiveRecord as a pattern became a golden hammer. It is good for some limited purposes, but we should not use it in every case.

Slide 35

Slide 35 text

ActiveRecord is good for mapping a tuple to an object, but bad for everything else. The most bestest alts: • Vanilla SQL and PORO • DataMapper

Slide 36

Slide 36 text

ActiveRecord’s poor architecture What AR models are responsible for? • Querying • Persistency • Associations • Validation • Business logic • Triggering Isn’t that too much?

Slide 37

Slide 37 text

Unit tests are a dream Rails developers don’t write unit- tests for models, because model has a lot of responsibilities. 
 
 Here mock. There stub. There one more mock. Mock, mock, mock, … Stub. validate :title, presence: true has_many :comments, dependent def add_comment(attrs)
 comments.create attrs
 end before :create, :assign_slug

Slide 38

Slide 38 text

Pseudo (surrogate) keys ActiveRecord and Rails force you to use pseudo-keys instead of natural keys. In most cases you have no ID or anything of the kind. Your records should be identified with a natural key even if it could be composite.

Slide 39

Slide 39 text

Follow me • Home page
 http://www.volodymyr-melnyk.com • IT Blog
 http://www.itrampage.com • Me on Facebook
 https://www.facebook.com/egotraumatic

Slide 40

Slide 40 text

Thank you
 for your attention