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

A Survey of Surprisingly Difficult Things – RailsConf 2017

A Survey of Surprisingly Difficult Things – RailsConf 2017

Many seemingly simple "real-world" things end up being much more complicated than anticipated, especially if it's a developer's first time dealing with that particular thing. Classic examples include money and currency, time, addresses, human names, and so on. We will survey a number of these common areas and the state of best practices, or lack thereof, for handling them in Rails.

Alex Boster

April 26, 2017
Tweet

Other Decks in Programming

Transcript

  1. What things do I mean? Commonplace things from the real

    world that appear simple to model at first glance – but turn out not to be. The obvious implementation is likely to cause problems and to do so sooner than expected.
  2. Motivation Help junior developers avoid a few pitfalls Most developers

    should know about these things Following best practices will also make your app be more inclusive
  3. Timey Wimey Types Migration Ruby Class PostgreSQL MySQL time only

    :time Time TIME TIME date only :date Date DATE DATE date & time :datetime DateTime TIMESTAMP DATETIME duration N/A :range (PgSQL) Float (secs) Range INTERVAL TIME TSRANGE TIME
  4. Timezones There are half-hour timezones There are quarter-hour timezones Daylight

    Saving Time • A place may start (or stop) observing DST • A place may change the DST schedule • A place may have a 2 hour DST change A place may change timezones entirely
  5. tzdata Database of timezone data used in many systems, including

    Unix-like systems All geographic timezones since 1970 Tracks daylight saving time changes Historical data (with caveats)
  6. UTC Stands for neither Coordinated Universal Time nor Temps universel

    coordonné Not a timezone but every timezone has an offset from UTC As a rule, time values should be stored in UTC Theoretically, offsets go from UTC-12 to UTC+12, but don’t
  7. Timezones In general, if not explicitly provided, the timezone could

    be set by: • OS default timezone (usually UTC) • Database default timezone (usually UTC) • Application default timezone (defaults to UTC in Rails, but should be configured)
  8. Timezones Keep your system and database in UTC Rails will

    store DateTimes in UTC Timezone-aware methods in Rails will use the application’s default Store users’ timezones on your User models and always use that in your views
  9. Time irb(main):012:0> Time.zone.now => Tue, 25 Apr 2017 00:20:30 UTC

    +00:00 irb(main):013:0> Time.zone => #<ActiveSupport::TimeZone:0x007fa973635028 @name="UTC", @utc_offset=nil, @tzinfo=#<TZInfo::DataTimezone: Etc/UTC>>
  10. Timezone aware methods An event at 1pm PDT and an

    event at 4pm EDT occur at the same time. z = Time.zone.parse('Wed, 06 Jul 2016 13:00:00 PDT -07:00') => Wed, 06 Jul 2016 13:00:00 PDT -07:00 y = Time.zone.parse('Wed, 06 Jul 2016 16:00:00 EDT -04:00') => Wed, 06 Jul 2016 13:00:00 PDT -07:00 z == y => true
  11. Timezone aware methods See “WORKING WITH TIME ZONES IN RUBY

    ON RAILS” 10.hours.from_now 7.days.ago Time.zone.parse(string_containing_timestamp) Time.strptime( "2015-08-27T12:09:36Z", "%Y-%m-%dT%H:%M:%S%z").in_time_zone Time.current Time.current.utc.iso8601 a_time_instance.in_time_zone a_time_instance.in_time_zone("Eastern Time(US & Canada)")
  12. Dates Dates do not have timezone information Dates are perfectly

    valid, but be aware of datetime to date conversions Examples: Birthdays Start and end days on “all-day” calendar events Ask yourself if the day in question changes if you are in Beijing versus Toronto.
  13. Dates Do not store dates as a DateTime like: Thu,

    07 Jul 2016 00:00:00 +0000 Be very careful of DateTime to Date conversion. Be very careful of Date to DateTime conversion.
  14. Time and date classes irb(main):010:0> Date.current => Tue, 25 Apr

    2017 irb(main):011:0> Date.today => Mon, 24 Apr 2017
  15. Timezone aware methods See “WORKING WITH TIME ZONES IN RUBY

    ON RAILS” Date.current Date.current.in_time_zone
  16. Avoid See “WORKING WITH TIME ZONES IN RUBY ON RAILS”

    Time.now Time.parse("2015-08-27T12:09:36Z") Time.strptime("2015-08-27T12:09:36Z", "%Y-%m-%dT%H:%M:%S%z") Date.today
  17. Rubocop Style linters will help catch use of non-timezone aware

    methods Make them part of your workflow and CI process Hound, Farcy, and similar can be required to merge PR’s
  18. Falsehoods Programmers Believe About Names People have exactly one canonical

    full name. People have exactly one full name which they go by. People have, at this point in time, exactly one canonical full name. People have, at this point in time, one full name which they go by. People’s names do not change.
  19. Falsehoods Programmers Believe About Names People’s names change, but only

    at a certain enumerated set of events. People’s names are assigned at birth. People’s names are written in ASCII. People’s names are written in any single character set. People’s names are all mapped in Unicode code points. Two different systems containing data about the same person will use the same name for that person.
  20. Remember Validate as little as possible If you can avoid

    First Name, Last Name, consider doing so – maybe use Given Name, Family Name It is impossible to guarantee “real names” are used Use Unicode (easy in modern Rails) Do not assume even US-based users have ASCII names
  21. USPS Addresses There are many more variations on addresses than

    you might expect, including: • Rural routes • Military addresses • Puerto Rice and territories • Multiline recipient information RR 4 BOX 19-1A
  22. USPS Addresses There are many more variations on addresses than

    you might expect, including: • Rural routes • Military addresses • Puerto Rico and territories • Multiline recipient information APO AE 09001-5275
  23. USPS Addresses There are many more variations on addresses than

    you might expect, including: • Rural routes • Military addresses • Puerto Rice and territories • Multiline recipient information Urbanization Condominium Name Building Number Residential Name
  24. USPS Addresses There are many more variations on addresses than

    you might expect, including: • Rural routes • Military addresses • Puerto Rice and territories • Multiline recipient information BOB SMITH GENERAL MANAGER 12TH FLOOR MAILSTOP 31-A ACME CORP
  25. US Addresses Standardized addresses may reduce the presence of special

    characters, but do not eliminate them. This is a valid last line. Note the city name: PLUMMER’S LANDING KY 41093 This is a valid street address. Note the / : 1234 1/2 MAIN ST
  26. US Zip Codes Preclude international addresses, including Canada “Zip code”

    is a US-specific term Zip codes do not map to states 100% of the time: 02861, 42223, 59221, 63673, 71749, 73949, 81137, 84536, 86044, 86515, 88063, 89439 & 97635
  27. Validating Addresses USPS has a database of postal addresses These

    are not always the same as physical/ delivery addresses Entire towns or communities have no physical addresses in the USPS database … yet UPS / FedEx will deliver there
  28. Monetary Values Never, ever use Float You may use DECIMAL

    values, as in: create_table :products do |t| t.decimal :price, :decimal, :precision => 12, :scale => 2 end
  29. Issues with DECIMALs Ruby: render json: @product JSON: { price:

    12.15 } Javascript: currentProduct.price * discount
  30. Just store cents Using integers everywhere avoids rounding errors Instead

    store price_in_cents as an INTEGER in the database and only convert that upon display (or whatever you need instead of cents – some currencies are not divided into 100ths) Failure to convert to displayed units is usually obvious Name your fields with the units, e.g. _in_cents
  31. Email Addresses Email addresses can now contain a large subset

    of Unicode characters, e.g. [email protected] There are now many top level domains, e.g. .horse You can validate there is a @ and a . And you can use a verification email
  32. Internationalization Internationalization Put all strings in config/locales from the beginning

    Localization Add locale to User model and always use it, even if it’s US-en to start
  33. Payments & Credit Cards Payments and Credit Cards PCI compliance

    Don’t store CC info Client browser should send sensitive data directly to third party, not your servers Consider using webhooks Timeouts are a problem
  34. Recurring Events Recurring Calendar Events • Read RFC 5545 Consider

    • Most recurring events have no end date • Rules can be like “every month on the second to last Thursday” • Individual instance of a recurring event can be edited or moved or cancelled
  35. In Conclusion Avoid over-validation Even “US only” products will have

    global-like problems Be culturally aware – remember your experience isn’t universal and probably isn’t typical Don’t assume
  36. References 1. “WORKING WITH TIME ZONES IN RUBY ON RAILS”,

    https://www.varvet.com/blog/working- with-time-zones-in-ruby-on-rails/ 2. “Falsehoods Programmers Believe About Names”, https://www.kalzumeus.com/ 2010/06/17/falsehoods-programmers-believe-about-names/ 3. “Falsehoods programmers believe about time”, http://infiniteundo.com/post/ 25326999628/falsehoods-programmers-believe-about-time