Slide 1

Slide 1 text

POLO WORKING WITH REAL WORLD DATA IN DEVELOPMENT @nettofarah 1 / 39

Slide 2

Slide 2 text

nettofarah github.com/nettofarah @nettofarah http://bit.ly/polo-rb 2 / 39

Slide 3

Slide 3 text

Real World Production Data in Development 3 / 39

Slide 4

Slide 4 text

Because Managing Data is hard Especially across Diļ¬€erent environments 4 / 39

Slide 5

Slide 5 text

Data in Rails.env.dev is... Ugly, Incomplete, Weird and Biased 5 / 39

Slide 6

Slide 6 text

Some Consequences of Inconsistent Data 6 / 39

Slide 7

Slide 7 text

!" !", # # misalignment, form validation, special characters $ $ 7 / 39

Slide 8

Slide 8 text

The Worst Consequence 8 / 39

Slide 9

Slide 9 text

Rails.env.dev will look % 9 / 39

Slide 10

Slide 10 text

But how do people solve these problems? 10 / 39

Slide 11

Slide 11 text

We can test it in PRODUCTION 11 / 39

Slide 12

Slide 12 text

Rails.env.staging ? 12 / 39

Slide 13

Slide 13 text

Custom db/migrations for data 13 / 39

Slide 14

Slide 14 text

rake db:seed 14 / 39

Slide 15

Slide 15 text

CSVs and Spreadsheets 15 / 39

Slide 16

Slide 16 text

16 / 39

Slide 17

Slide 17 text

Sample Database Snapshots 17 / 39

Slide 18

Slide 18 text

Your good ol' ActiveRecord::Associations to .sql 18 / 39

Slide 19

Slide 19 text

POLO lets you turn this... 19 / 39

Slide 20

Slide 20 text

class Chef < ActiveRecord::Base has_many :recipes has_many :ingredients, through: :recipes end class Recipe < ActiveRecord::Base has_many :recipes_ingredients has_many :ingredients, through: :recipes_ingredients end class Ingredient < ActiveRecord::Base end class RecipesIngredient < ActiveRecord::Base belongs_to :recipe belongs_to :ingredient end 20 / 39

Slide 21

Slide 21 text

Into this... 21 / 39

Slide 22

Slide 22 text

inserts = Polo.explore(Chef, 1) # Chef -> ActiveRecord::Base object # 1 -> Database ID. (Chef with ID 1) INSERT INTO `chefs` (`id`, `name`) VALUES (1, 'Netto') 22 / 39

Slide 23

Slide 23 text

It works with associations too 23 / 39

Slide 24

Slide 24 text

inserts = Polo.explore(Chef, 1, :recipes) # :recipes -> # ActiveRecord::Associations::HasManyAssociation # # a Chef has many Recipes INSERT INTO `chefs` (`id`, `name`) VALUES (1, 'Netto') INSERT INTO `recipes` (`id`, `title`, `num_steps`, `chef_id`) VALUES (1, 'Turkey Sandwich', NULL, 1) INSERT INTO `recipes` (`id`, `title`, `num_steps`, `chef_id`) VALUES (2, 'Cheese Burger', NULL, 1) 24 / 39

Slide 25

Slide 25 text

It also works with nested associations 25 / 39

Slide 26

Slide 26 text

inserts = Polo.explore(Chef, 1, { :recipes => :ingredients }) # { :recipes => :ingredients } -> # load every recipe and ingredientes ... INSERT INTO `recipes` (`id`, `title`, `num_steps`, `chef_id`) VALUES (1, 'Turkey Sandwich', NULL, 1) INSERT INTO `recipes` (`id`, `title`, `num_steps`, `chef_id`) VALUES (2, 'Cheese Burger', NULL, 1) INSERT INTO `recipes_ingredients` (`id`, `recipe_id`, `ingredient_id`) VALUES (1, 1, 1) INSERT INTO `recipes_ingredients` (`id`, `recipe_id`, `ingredient_id`) VALUES (2, 1, 2) ... INSERT INTO `ingredients` (`id`, `name`, `quantity`) VALUES (1, 'Turkey', 'a lot') INSERT INTO `ingredients` (`id`, `name`, `quantity`) VALUES (2, 'Cheese', '1 slice') ... 26 / 39

Slide 27

Slide 27 text

& & Show me the magic! 27 / 39

Slide 28

Slide 28 text

Collect all the objects lib/polo/collector.rb#L16 28 / 39

Slide 29

Slide 29 text

asn = ActiveSupport::Notifications # Extracted so this can fit in a slide asn.subscribed(collector, 'sql.active_record') do # Set up ActiveRecord::Preloader base_finder = @base_class. includes(@dependency_tree) .where(id: @id) # Store SELECTS in some global storage collect_sql(@base_class, base_finder.to_sql) end 29 / 39

Slide 30

Slide 30 text

' ' Transform them to SQL lib/polo/sql_translator.rb#51 30 / 39

Slide 31

Slide 31 text

attributes = record.attributes keys = attributes.keys.map do |key| "`#{key}`" end values = attributes.map do |key, value| column = record.column_for_attribute(key) attribute = cast_attribute(record, column, value) connection.quote(attribute) end joined_keys = keys.join(', ') joined_values = values.join(', ') table_name = record.class.table_name "INSERT INTO `#{table_name}`" + " (#{joined_keys}) VALUES (#{joined_values})" 31 / 39

Slide 32

Slide 32 text

PROFIT! ( ( 32 / 39

Slide 33

Slide 33 text

I need your help! ) ) 33 / 39

Slide 34

Slide 34 text

on Github http://github.com/IFTTT/polo 34 / 39

Slide 35

Slide 35 text

Pull Requests * * 35 / 39

Slide 36

Slide 36 text

PostgreSQL, SQLite and Oracle support (advanced features only, basic usage still works) 36 / 39

Slide 37

Slide 37 text

ActiveRecord < 3.2 support 37 / 39

Slide 38

Slide 38 text

Check out some other really cool Open Source projects http://ifttt.github.io 38 / 39

Slide 39

Slide 39 text

Questions? 39 / 39