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

Basecamp Next: Code Spelunking

Basecamp Next: Code Spelunking

Heard about the big Basecamp launch this March? Wondering what's new, how it's shaping Rails, and the tech behind it? We're going to go over some the practices and patterns in the new Basecamp's code base and you can learn how to improve your app with them.

Nick Quaranto

April 25, 2012
Tweet

More Decks by Nick Quaranto

Other Decks in Programming

Transcript

  1. echo "Installing libraries..." { (gem list -i bundler || gem

    install bundler) && rbenv rehash; bundle install && rbenv rehash } >&3 2>&1
  2. if [ -z "$KEEPDB" ]; then echo "Reloading the database"

    { # lots o’ rake tasks } >&3 2>&1 fi
  3. class ApplicationController include CurrentAccount include CurrentPerson include ExceptionNotification include Ajax

    include Mobile include EnsureCompatibleBrowser include WrongContentTypeFallback
  4. access-toggle account_name_cancel account_name_form account_name_header account_name_header_name account_name_link activate add_calendar add_to_all_calendars add_to_all_projects

    admin_permission alt_date_field assigned_to assignee_name assignee_options attachments_required autoresize autosave backbone_collection backbone_model bounce bounce_nav bucket_selector caching_classic_projects calendar_accesses calendar_alert calendar_event_drag_area calendar_invitees calendar_todo can_create_projects_permissi on collapse_on_click complete confirm confirm_action create_project date_entry date_picker delete dirty_tracking document_version_link due_date edit edit_bucket edit_calendar_event_form edit_calendar_todo edit_identity edit_project_header editable_field_prompt email_preferences enlargeable expand_exclusively expand_on_click expandable expandable expand_exclusively file_drop_target filter_and filter_due format_timeline grant-all has_hover_content input_change_emitter invite invite_notice invitees jump_to_month jump_to_new_calendar_event lazy_invitees lazy_load_subscribers link_container load_assignee_options load_completed_todos member members migrate_account move_action move_operation_form navigate new new_calendar_event new_group new_message new_project new_project_dialog new_subgroup new_todolist new_upload no_due_date no_reset notification_email nubbin pending_attachments project_palette project_star read_only refresh_timeline remove remove_duplicates remove_group remove_invitee remove_member rename_group resend_invitation resubscribe reveal_event_subsc revoke revoke-all save_document save_group scroll_content scroll_forward scroll_reverse scroll_view select_on_focus select_suggestion set_start_of_week show_all_todos show_link_if_acces slide slider sortable sortable_container sortable_handle
  5. access-toggle account_name_cancel account_name_form account_name_header account_name_header_name account_name_link activate add_calendar add_to_all_calendars add_to_all_projects

    admin_permission alt_date_field assigned_to assignee_name assignee_options attachments_required autoresize autosave backbone_collection backbone_model bounce bounce_nav bucket_selector caching_classic_projects calendar_accesses calendar_alert calendar_event_drag_area calendar_invitees calendar_todo can_create_projects_permissi on collapse_on_click complete confirm confirm_action create_project date_entry date_picker delete dirty_tracking document_version_link due_date edit edit_bucket edit_calendar_event_form edit_calendar_todo edit_identity edit_project_header editable_field_prompt email_preferences enlargeable expand_exclusively expand_on_click expandable expandable expand_exclusively file_drop_target filter_and filter_due format_timeline grant-all has_hover_content input_change_emitter invite invite_notice invitees jump_to_month jump_to_new_calendar_event lazy_invitees lazy_load_subscribers link_container load_assignee_options load_completed_todos member members migrate_account move_action move_operation_form navigate new new_calendar_event new_group new_message new_project new_project_dialog new_subgroup new_todolist new_upload no_due_date no_reset notification_email nubbin pending_attachments project_palette project_star read_only refresh_timeline remove remove_duplicates remove_group remove_invitee remove_member rename_group resend_invitation resubscribe reveal_event_subsc revoke revoke-all save_document save_group scroll_content scroll_forward scroll_reverse scroll_view select_on_focus select_suggestion set_start_of_week show_all_todos show_link_if_acces slide slider sortable sortable_container sortable_handle
  6. out the wazoo! keeps javascript logic out of views stops

    tying behavior to css classes still easy to query for
  7. use custom events! _.bind() is your new best friend trigger

    custom events for what you need keep namespacing consistent
  8. Stacker basics intercepts most clicks on <a> requests the page

    via $.ajax renders the response as a new “page” smart about errors too!
  9. page:change fired when a new “page” is loaded install views

    hide/show elements based on roles lazily load data
  10. page:update fired after change, on every ajaxSuccess install views, behavior

    moved loaded data into place stop “busy” animations
  11. { "id": 963979453, "title": "By Jove!", "content": "I’ve figured it

    out!", "created_at": "2012-03-27T13:19:29-05:0 "updated_at": "2012-03-27T13:53:24-05:0 "last_updater": { "id": 149087659, "name": "Sherlock Holmes" }, "comments": [ ] }
  12. why not to_json? keep view data in the views use

    partials! public vs private JSON
  13. class Api::DocumentsController < Api::BaseController def update @document.update_attributes! document_params render :show

    end def document_params params.required(:document). permit(:title, :content) end end
  14. class Api::DocumentsController < Api::BaseController def update @document.update_attributes! document_params render :show

    end def document_params params.required(:document). permit(:title, :content) end end
  15. class Api::DocumentsController < Api::BaseController def update @document.update_attributes! document_params render :show

    end def document_params params.required(:document). permit(:title, :content) end end
  16. class Api::DocumentsController < Api::BaseController def update @document.update_attributes! document_params render :show

    end def document_params params.required(:document). permit(:title, :content) end end
  17. why not attr_accessible? controller, not a model problem can still

    share via concerns easier to return error, status codes
  18. log EVERYTHING unique ID per request account ID for log

    splitting controller, actions on each SQL query
  19. def compute_tags(env) request = ActionDispatch::Request.new(env) @tags.collect do |tag| case tag

    when Proc tag.call(request) when Symbol request.send(tag) else tag end end end
  20. def compute_tags(env) request = ActionDispatch::Request.new(env) @tags.collect do |tag| case tag

    when Proc tag.call(request) when Symbol request.send(tag) else tag end end end
  21. def compute_tags(env) request = ActionDispatch::Request.new(env) @tags.collect do |tag| case tag

    when Proc tag.call(request) when Symbol request.send(tag) else tag end end end
  22. def compute_tags(env) request = ActionDispatch::Request.new(env) @tags.collect do |tag| case tag

    when Proc tag.call(request) when Symbol request.send(tag) else tag end end end
  23. def compute_tags(env) request = ActionDispatch::Request.new(env) @tags.collect do |tag| case tag

    when Proc tag.call(request) when Symbol request.send(tag) else tag end end end
  24. Account Load (0.3ms) SELECT `accounts`.* FROM `accounts` WHERE `accounts`.`queenbee_id` =

    1234567890 LIMIT 1 /*application:BCX, controller:project_imports,action:show*/
  25. MORE logging? Know where slow queries came from Immediately identify

    web or job query MySQL, sqlite, maybe Postgres now?