RailsConf 2015 - Breaking Down the Barrier: Demystifying Contributing to Rails

RailsConf 2015 - Breaking Down the Barrier: Demystifying Contributing to Rails

http://confreaks.tv/videos/railsconf2015-breaking-down-the-barrier-demystifying-contributing-to-rails

Contributing to Rails for the first time can be terrifying. In this lab I’ll make contributing to Rails more approachable by going over the contributing guidelines and technical details you need to know. We’ll walk through traversing the source code with tools such as CTags, source_location and TracePoint. Additionally, we’ll create reproduction scripts for reporting issues and learn advanced git commands like bisect and squash. At the end of this session you’ll have the confidence to fix bugs and add features to Ruby on Rails.

C44e1f7e22c3f23cff7bc130871047ef?s=128

Eileen M. Uchitelle

April 21, 2015
Tweet

Transcript

  1. CLONE THE REPO Grab a USB or clone the railsconf_scripts

    repo from https://github.com/eileencodes/railsconf_scripts.git $ cd railsconf_scripts # mac/linux users $ bundle install --local # windows users not using a linux VM $ git checkout windows $ bundle install --local
  2. BREAKING DOWN THE Demystifying Contributing to Ruby on Rails BARRIER:

  3. EILEEN M. UCHITELLE Programmer at Basecamp ! eileencodes.com " @eileencodes

    # @eileencodes
  4. None
  5. Kingston, New York

  6. Arya @aryadog

  7. PREREQUISITES

  8. GOALS

  9. to Rails can be INTIMIDATING. CONTRIBUTING

  10. to Rails open source can be is INTIMIDATING. CONTRIBUTING #

  11. FORMAT

  12. CLONE THE REPO Grab a USB or clone the railsconf_scripts

    repo from github.com/eileencodes/railsconf_scripts $ cd railsconf_scripts/ # mac/linux users $ bundle install --local # windows users not using a linux VM $ git checkout windows $ bundle install --local
  13. ENVIRONMENT REQUIREMENTS

  14. ENVIRONMENT $ Ruby version manager (rbenv, rvm, chruby) $ Ruby

    2.2.2
  15. None
  16. % Ruby version manager (rbenv, rvm, chruby) % Ruby 2.2.2

    $ Databases for Active Record $ MySQL $ PostgreSQL $ SQLite3 ENVIRONMENT
  17. % Ruby version manager (rbenv, rvm, chruby) % Ruby 2.2.2

    % Databases for Active Record % MySQL % PostgreSQL % SQLite3 $ Git & a GitHub Account ENVIRONMENT
  18. RUNNING THE TEST SUITE

  19. None
  20. RUNNING TESTS $ cd actionpack $ rake test Finished in

    3.237796s, 861.0796 runs/ s, 4508.3137 assertions/s.
 2788 runs, 14597 assertions, 0 failures, 0 errors, 0 skips
  21. ACTIVE RECORD $ cd activerecord $ rake test:sqlite3 $ rake

    test:mysql2 $ rake test:mysql $ rake test:postgresql
  22. $ ruby -Ilib:test path/to/test_file.rb RUN A TEST FILE

  23. $ ruby -Ilib:test path/to/test_file.rb -n test_name_of_test RUN A SINGLE TEST

  24. RUN A SINGLE TEST $ ARCONN=mysql2 ruby -Ilib:test path/ to/test_file.rb

    -n test_name_of_test
  25. $ bundle exec rake TEST=path/to/ test_file.rb -n test_name RUN A

    SINGLE TEST
  26. $ cd activerecord/ $ rake test:sqlite3 $ ruby -Ilib:test test/cases/

    reflection_test.rb $ ARCONN=mysql2 ruby -Ilib:test test/ cases/reflection_test.rb -n test_columns RUNNING TESTS
  27. GUIDELINES FOR OPENING AN ISSUE

  28. OPENING AN ISSUE $ Be clear and include the Rails

    version
  29. OPENING AN ISSUE % Be clear and include the Rails

    version $ Don’t open security issues on the issues tracker
  30. security@rubyonrails.org

  31. OPENING AN ISSUE % Be clear and include the Rails

    version % Don’t open security issues on the issues tracker $ Don’t open issues that aren’t a bug in Rails
  32. OPENING AN ISSUE % Be clear and include the Rails

    version % Don’t open security issues on the issues tracker % Don’t open issues that aren’t a bug in Rails $ Don’t open a separate issue if you have a PR
  33. OPENING AN ISSUE % Be clear and include the Rails

    version % Don’t open security issues on the issues tracker % Don’t open issues that aren’t a bug in Rails % Don’t open a separate issue if you have a PR $ Don’t open feature request tickets
  34. OPENING AN ISSUE % Be clear and include the Rails

    version % Don’t open security issues on the issues tracker % Don’t open issues that aren’t a bug in Rails % Don’t open a separate issue if you have a PR % Don’t open feature request tickets % Do include a test script or application
  35. Examples of executable scripts github.com/rails/rails/blob/master/ guides/bug_report_templates TEST SCRIPTS

  36. $ cd railsconf_scripts/ $ open ar_script_example.rb TEST SCRIPTS

  37. require 'active_record' require 'minitest/autorun' require 'logger' ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')

    ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Schema.define do create_table :projects do |t| t.string :name end end class Project < ActiveRecord::Base validates_presence_of :name end class BugTest < Minitest::Test def test_assert project = Project.create! assert_not_operator project, :valid? end end
  38. require 'active_record' require 'minitest/autorun' require 'logger' ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')

    ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Schema.define do create_table :projects do |t| t.string :name end end class Project < ActiveRecord::Base validates_presence_of :name end class BugTest < Minitest::Test def test_assert project = Project.create! assert_not_operator project, :valid? end end
  39. require 'active_record' require 'minitest/autorun' require 'logger' ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')

    ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Schema.define do create_table :projects do |t| t.string :name end end class Project < ActiveRecord::Base validates_presence_of :name end class BugTest < Minitest::Test def test_assert project = Project.create! assert_not_operator project, :valid? end end
  40. require 'active_record' require 'minitest/autorun' require 'logger' ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')

    ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Schema.define do create_table :projects do |t| t.string :name end end class Project < ActiveRecord::Base validates_presence_of :name end class BugTest < Minitest::Test def test_assert project = Project.create! assert_not_operator project, :valid? end end
  41. require 'active_record' require 'minitest/autorun' require 'logger' ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')

    ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Schema.define do create_table :projects do |t| t.string :name end end class Project < ActiveRecord::Base validates_presence_of :name end class BugTest < Minitest::Test def test_assert project = Project.create! assert_not_operator project, :valid? end end
  42. require 'active_record' require 'minitest/autorun' require 'logger' ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')

    ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Schema.define do create_table :projects do |t| t.string :name end end class Project < ActiveRecord::Base validates_presence_of :name end class BugTest < Minitest::Test def test_assert project = Project.create! assert_not_operator project, :valid? end end
  43. require 'active_record' require 'minitest/autorun' require 'logger' ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')

    ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Schema.define do create_table :projects do |t| t.string :name end end class Project < ActiveRecord::Base validates_presence_of :name end class BugTest < Minitest::Test def test_assert project = Project.create! assert_not_operator project, :valid? end end
  44. TEST SCRIPTS $ cd railsconf_scripts/ $ bundle exec ruby ar_script_example.rb

  45. GUIDELINES FOR OPENING A PULL REQUEST

  46. OPENING A PR $ Open PR’s against master

  47. OPENING A PR % Open PR’s against master $ We

    don’t accept “cosmetic changes”
  48. OPENING A PR % Open PR’s against master % We

    don’t accept “cosmetic changes” $ Write clear and descriptive commit messages
  49. Short (50 chars or less) summary of changes More detailed

    explanatory text, if necessary. Wrap it to about 72 characters or so. In some contexts, the first line is treated as the subject of an email and the rest of the text as the body. The blank line separating the summary from the body is critical (unless you omit the body entirely); tools like rebase can get confused if you run the two together. Further paragraphs come after blank lines. - Bullet points are okay, too - Typically a hyphen or asterisk is used for the bullet, preceded by a single space, with blank lines in between, but conventions vary here
  50. OPENING A PR % Open PR’s against master % We

    don’t accept “cosmetic changes” % Write clear and descriptive commit messages $ Be prepared to squash your commits
  51. OPENING A PR % Open PR’s against master % We

    don’t accept “cosmetic changes” % Write clear and descriptive commit messages % Be prepared to squash your commits $ Don’t ping constantly or litter PR’s with +1’s
  52. None
  53. OPENING A PR % Open PR’s against master % We

    don’t accept “cosmetic changes” % Write clear and descriptive commit messages % Be prepared to squash your commits % Don’t ping constantly or litter PR’s with +1’s $ We <3 documentation PR’s
  54. commit 02d3a253610eaf9c80587913b366e3fa0f56b71f Author: eileencodes <eileencodes@gmail.com> Date: Sat Oct 4 11:19:37

    2014 -0400 [ci skip] Clarify deletion strategies for collection proxies For detailed testing of behavior see: https://gist.github.com/eileencodes/ 5b0a2fe011dcff6203fe This shows destroy_all always destroys records and fires callbacks. It will never use nullify or delete_all delete_all's behavior varies greatly based on `hm` vs `hm:t` and deletion strategy.
  55. OPENING A PR % Open PR’s against master % We

    don’t accept “cosmetic changes” % Write clear and descriptive commit messages % Be prepared to squash your commits % Don’t ping constantly or litter PR’s with +1’s % We <3 documentation PR’s $ Keep it simple
  56. OPENING A PR % Open PR’s against master % We

    don’t accept “cosmetic changes” % Write clear and descriptive commit messages % Be prepared to squash your commits % Don’t ping constantly or litter PR’s with +1’s % We <3 documentation PR’s % Keep it simple $ Use Benchmark/ips
  57. $ cd railsconf_scripts/ $ open benchmark_ips_example.rb BENCHMARK/IPS

  58. # railsconf_scripts/benchmark_ips_example.rb require 'benchmark/ips' ARRAY = (1..100).to_a def slow ARRAY.shuffle.first

    end def fast ARRAY.sample end Benchmark.ips do |x| x.report('slow') { slow } x.report('fast') { fast } x.compare! end
  59. # railsconf_scripts/benchmark_ips_example.rb require 'benchmark/ips' ARRAY = (1..100).to_a def slow ARRAY.shuffle.first

    end def fast ARRAY.sample end Benchmark.ips do |x| x.report('slow') { slow } x.report('fast') { fast } x.compare! end
  60. # railsconf_scripts/benchmark_ips_example.rb require 'benchmark/ips' ARRAY = (1..100).to_a def slow ARRAY.shuffle.first

    end def fast ARRAY.sample end Benchmark.ips do |x| x.report('slow') { slow } x.report('fast') { fast } x.compare! end
  61. # railsconf_scripts/benchmark_ips_example.rb require 'benchmark/ips' ARRAY = (1..100).to_a def slow ARRAY.shuffle.first

    end def fast ARRAY.sample end Benchmark.ips do |x| x.report('slow') { slow } x.report('fast') { fast } x.compare! end
  62. $ bundle exec ruby benchmark_ips_example.rb Calculating ------------------------------------- slow 26.801k i/100ms

    fast 131.410k i/100ms ------------------------------------------------- slow 343.594k (± 5.4%) i/s - 1.715M fast 6.348M (±11.8%) i/s - 31.276M Comparison: fast: 6347552.5 i/s slow: 343594.2 i/s - 18.47x slower
  63. $ cd railsconf_scripts/ $ bundle exec ruby BENCHMARK/IPS benchmark_ips_example.rb

  64. OPENING A PR % Open PR’s against master % We

    don’t accept “cosmetic changes” % Write clear and descriptive commit messages % Be prepared to squash your commits % Don’t ping constantly or litter PR’s with +1’s % We <3 documentation PR’s % Keep it simple % Use Benchmark/ips $ Write tests
  65. WORKING WITH DEPRECATIONS

  66. # nodoc example from activerecord/lib/active_record/ reflection.rb:588 class HasManyReflection < AssociationReflection

    # :nodoc: def initialize(name, scope, options, active_record) super(name, scope, options, active_record) end def macro; :has_many; end def collection?; true; end end
  67. # Deprecation from activerecord/lib/active_record/tasks/ # database_tasks.rb:207 def load_schema_for(*args) ActiveSupport::Deprecation.warn(<<-MSG.squish) This

    method was renamed to `#load_schema` and will be removed in the future. Use `#load_schema` instead. MSG load_schema(*args) end
  68. TRAVERSING UNFAMILIAR CODE

  69. None
  70. # actionview/lib/action_view/template/error.rb:118 def source_location if line_number "on line ##{line_number} of

    " else 'in ' end + file_name end
  71. SOURCE LOCATION $ cd railsconf_scripts/ $ open source_location.rb

  72. class Project < ActiveRecord::Base has_many :projects end class Comment <

    ActiveRecord::Base belongs_to :project end class BugTest < Minitest::Test def test_delete_all post = Post.create!(title: "Post title", content: "Lots of content") comment = Comment.create!(content: "I am a comment", post_id: post.id) assert 1, post.comments.count post.comments.delete_all assert 0, post.comments.count end end
  73. class Project < ActiveRecord::Base has_many :projects end class Comment <

    ActiveRecord::Base belongs_to :project end class BugTest < Minitest::Test def test_delete_all post = Post.create!(title: "Post title", content: "Lots of content") comment = Comment.create!(content: "I am a comment", post_id: post.id) assert 1, post.comments.count puts post.comments.method(:delete_all).source_location assert 0, post.comments.count end end
  74. SOURCE LOCATION $ cd railsconf_scripts/ $ bundle exec ruby source_location.rb

  75. (0.0ms) begin transaction SQL (0.1ms) INSERT INTO "comments" ("content", "post_id")

    VALUES (?, ?) [["content", "I am a comment"], ["post_id", 1]] (0.0ms) commit transaction (0.1ms) SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 1]] .../activerecord/lib/active_record/associations/ collection_proxy.rb 442 (0.0ms) SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 1]]
  76. # activerecord/lib/active_record/associations/ collection_proxy.rb:442 def delete_all(dependent = nil) @association.delete_all(dependent) end

  77. CTags # OS X $ brew install ctags # Debian-based

    linux $ sudo apt-get install exuberant-ctags # Red Hat-based linux $ sudo yum install ctags
  78. CTags $ cd path/to/rails/ $ ctags -R .

  79. CTags $ cd path/to/rails/ $ ctags -R -f .git/tags .

  80. CTags Sublime: github.com/SublimeText/CTags TextMate: github.com/textmate/ ctags.tmbundle

  81. # .vimrc map <Leader>rt :!ctags --tag-relative --extra=+f - Rf.git/tags --exclude=.git,pkg

    —languages=- javascript,sql<CR><CR> set tags+=.git/tags
  82. CTags $ CTRL + ]

  83. CTags $ :ts

  84. CTags $ :ts $ N + ENTER

  85. None
  86. # activerecord/lib/active_record/reflection.rb:314 def association_primary_key(klass = nil) options[:primary_key] || primary_key(klass ||

    self.klass) end
  87. # activerecord/lib/active_record/reflection.rb:314 def association_primary_key(klass = nil) puts caller options[:primary_key] ||

    primary_key(klass || self.klass) end
  88. .../through_association.rb:48:in `construct_join_attributes' .../has_many_through_association.rb:169:in `through_records_for' .../has_many_through_association.rb:180:in `block in delete_through_records' .../has_many_through_association.rb:179:in `each'

    .../has_many_through_association.rb:179:in `delete_through_records' .../has_many_through_association.rb:154:in `delete_records' .../has_many_through_association.rb:124:in `delete_or_nullify_all_records' .../collection_association.rb:214:in `delete_all' .../collection_proxy.rb:443:in `delete_all’ ar_test_scripts/ar_rails_test.rb:85:in `<main>'
  89. .../activerecord/lib/active_record/associations/ through_association.rb:48:in `block in construct_join_attributes’

  90. def construct_join_attributes(*records) ensure_mutable if source_reflection.association_primary_key(reflection.klass) == reflection.klass.primary_key join_attributes = {

    source_reflection.name => records } else join_attributes = { source_reflection.foreign_key => records.map { |record| record.send(source_reflection.association_primary_key( reflection.klass)) } } end if options[:source_type] join_attributes[source_reflection.foreign_type] = records.map { |record| record.class.base_class.name } [...]
  91. CALLER $ cd railsconf_scripts/ $ open puts_caller.rb

  92. ActiveRecord::Schema.define do create_table :projects do |t| t.string :name end end

    class Project < ActiveRecord::Base after_create :call_me def call_me puts "======== i am a callback ========" end end class BugTest < Minitest::Test def test_create project = Project.create!(name: "whatever") end end
  93. create_table :projects do |t| t.string :name end end class Project

    < ActiveRecord::Base after_create :call_me def call_me puts caller puts "======== i am a callback ========" end end class BugTest < Minitest::Test def test_create project = Project.create!(name: "whatever") end end
  94. CALLER $ bundle exec ruby puts_caller.rb

  95. .../activesupport/lib/active_support/callbacks.rb:428:in `block in make_lambda' .../activesupport/lib/active_support/callbacks.rb:229:in `call' .../activesupport/lib/active_support/callbacks.rb:229:in `block in halting_and_conditional'

    .../activesupport/lib/active_support/callbacks.rb:502:in `call' .../activesupport/lib/active_support/callbacks.rb:502:in `block in call' .../activesupport/lib/active_support/callbacks.rb:502:in `each' .../activesupport/lib/active_support/callbacks.rb:502:in `call' .../activesupport/lib/active_support/callbacks.rb:90:in `run_callbacks' .../activerecord/lib/active_record/callbacks.rb:305:in `_create_record' .../activerecord/lib/active_record/timestamp.rb:57:in `_create_record' .../activerecord/lib/active_record/persistence.rb:506:in `create_or_update' .../activerecord/lib/active_record/callbacks.rb:301:in `block in create_or_update' .../activesupport/lib/active_support/callbacks.rb:86:in `run_callbacks' .../activerecord/lib/active_record/callbacks.rb:301:in `create_or_update' .../activerecord/lib/active_record/persistence.rb:151:in `save!' [...]
  96. .../activesupport/lib/active_support/callbacks.rb: 428:in `block in make_lambda'

  97. def make_lambda(filter) case filter when Symbol lambda { |target, _,

    &blk| target.send filter, &blk } when String l = eval "lambda { |value| #{filter} }" lambda { |target, value| target.instance_exec(value, &l) } when Conditionals::Value then filter when ::Proc if filter.arity > 1 return lambda { |target, _, &blk| raise ArgumentError unless block target.instance_exec(target, block, &filter) } end if filter.arity <= 0 lambda { |target, _| target.instance_exec(&filter) } else lambda { |target, _| target.instance_exec(target, &filter) } [...]
  98. def test_reject_if_method_without_arguments Pirate.accepts_nested_attributes_for :ship, reject_if: :new_record? pirate = Pirate.new(catchphrase: "Stop

    wastin' me time") pirate.ship_attributes = { name: 'Black Pearl' } assert_no_difference('Ship.count') { pirate.save! } end
  99. None
  100. def test_reject_if_method_without_arguments Pirate.accepts_nested_attributes_for :ship, reject_if: :new_record? pirate = Pirate.new(catchphrase: "Stop

    wastin' me time") tp = TracePoint.new(:call) do |*args| p args end tp.enable pirate.ship_attributes = { name: 'Black Pearl' } tp.disable assert_no_difference('Ship.count') { pirate.save! } end
  101. def test_reject_if_method_without_arguments Pirate.accepts_nested_attributes_for :ship, reject_if: :new_record? pirate = Pirate.new(catchphrase: "Stop

    wastin' me time") tp = TracePoint.new(:call) do |*args| p args end tp.enable pirate.ship_attributes = { name: 'Black Pearl' } tp.disable assert_no_difference('Ship.count') { pirate.save! } end
  102. def test_reject_if_method_without_arguments Pirate.accepts_nested_attributes_for :ship, reject_if: :new_record? pirate = Pirate.new(catchphrase: "Stop

    wastin' me time") tp = TracePoint.new(:call) do |*args| p args end tp.enable pirate.ship_attributes = { name: 'Black Pearl' } tp.disable assert_no_difference('Ship.count') { pirate.save! } end
  103. def test_reject_if_method_without_arguments Pirate.accepts_nested_attributes_for :ship, reject_if: :new_record? pirate = Pirate.new(catchphrase: "Stop

    wastin' me time") tp = TracePoint.new(:call) do |*args| p args end tp.enable pirate.ship_attributes = { name: 'Black Pearl' } tp.disable assert_no_difference('Ship.count') { pirate.save! } end
  104. def test_reject_if_method_without_arguments Pirate.accepts_nested_attributes_for :ship, reject_if: :new_record? pirate = Pirate.new(catchphrase: "Stop

    wastin' me time") tp = TracePoint.new(:call) do |*args| p args end tp.enable pirate.ship_attributes = { name: 'Black Pearl' } tp.disable assert_no_difference('Ship.count') { pirate.save! } end
  105. `ship_attributes='@.../activerecord/lib/active_record/nested_attributes.rb:347 `assign_nested_attributes_for_one_to_one_association'@.../activerecord/lib/active_record/ nested_attributes.rb:382 `nested_attributes_options'@.../activesupport/lib/active_support/core_ext/class/attribute.rb:106 `nested_attributes_options'@.../activesupport/lib/active_support/core_ext/class/attribute.rb:86 `with_indifferent_access'@.../activesupport/lib/active_support/core_ext/hash/indifferent_access.rb:8 `new_from_hash_copying_default'@.../activesupport/lib/active_support/hash_with_indifferent_access.rb:75 `initialize'@.../activesupport/lib/active_support/hash_with_indifferent_access.rb:58 `update'@.../activesupport/lib/active_support/hash_with_indifferent_access.rb:127 `convert_key'@.../activesupport/lib/active_support/hash_with_indifferent_access.rb:258

    `convert_value'@.../activesupport/lib/active_support/hash_with_indifferent_access.rb:262 `ship'@.../activerecord/lib/active_record/associations/builder/association.rb:110 `association'@.../activerecord/lib/active_record/associations.rb:149 `association_instance_get’@.../activerecord/lib/active_record/associations.rb:189 `_reflect_on_association'@.../activerecord/lib/active_record/reflection.rb:109 `_reflections'@.../activesupport/lib/active_support/core_ext/class/attribute.rb:86 `association_class'@.../activerecord/lib/active_record/reflection.rb:435 `macro'@.../activerecord/lib/active_record/reflection.rb:588 `require'@.../activesupport/lib/active_support/dependencies.rb:272 `load_dependency'@.../activesupport/lib/active_support/dependencies.rb:236 `load?'@.../activesupport/lib/active_support/dependencies.rb:311 `mechanism'@.../activesupport/lib/active_support/core_ext/module/attribute_accessors.rb:60 `constant_watch_stack'@.../activesupport/lib/active_support/core_ext/module/attribute_accessors.rb:60 `watching?'@.../activesupport/lib/active_support/dependencies.rb:99 `delegate'@.../activesupport/lib/active_support/core_ext/module/delegation.rb:151 `include?'@/Users/eileen/.rbenv/versions/2.2.0/lib/ruby/2.2.0/set.rb:211 `initialize'@.../activerecord/lib/active_record/associations/association.rb:24 `check_validity!'@.../activerecord/lib/active_record/reflection.rb:331 `check_validity_of_inverse!’@.../activerecord/lib/active_record/reflection.rb:335 ...
  106. `ship_attributes='@.../activerecord/lib/active_record/ nested_attributes.rb:347

  107. def generate_association_writer(association_name, type) generated_association_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1 if

    method_defined?(:#{association_name}_attributes=) remove_method(:#{association_name}_attributes=) end def #{association_name}_attributes=(attributes) assign_nested_attributes_for_#{type} _association(:#{association_name}, attributes) end eoruby end
  108. TRACE POINT $ cd railsconf_scripts/ $ open trace_point.rb

  109. class User < ActiveRecord::Base has_one :avatar accepts_nested_attributes_for :avatar end class

    Avatar < ActiveRecord::Base belongs_to :user end class BugTest < Minitest::Test def test_trace_point user = User.new(name: "My Name") user.avatar_attributes = { name: "I am a file name" } user.save! end end
  110. end class Avatar < ActiveRecord::Base belongs_to :user end class BugTest

    < Minitest::Test def test_trace_point user = User.new(name: "My Name") tp = TracePoint.new(:call) do |*args| p args end user.avatar_attributes = { name: "I am a file name" } user.save! end end
  111. class Avatar < ActiveRecord::Base belongs_to :user end class BugTest <

    Minitest::Test def test_trace_point user = User.new(name: "My Name") tp = TracePoint.new(:call) do |*args| p args end tp.enable user.avatar_attributes = { name: "I am a file name" } tp.disable user.save! end end
  112. TRACE POINT $ bundle exec ruby trace_point.rb

  113. `avatar_attributes='@.../activerecord/lib/active_record/ nested_attributes.rb:347

  114. ADVANCED GIT COMMANDS

  115. GIT $ git add . $ git commit -m‘Commit message’

    $ git checkout practicing-git
  116. GIT $ git add upstream $ git bisect $ git

    reset --[soft|hard] $ git rebase -i $ git reflog
  117. GIT REMOTES $ cd path/to/rails/ $ git remote add upstream

    https://github.com/rails/rails.git
  118. GIT REMOTES $ cd path/to/rails/ $ git remote remove upstream

    $ git remote add upstream https://github.com/rails/rails.git
  119. GIT REMOTES $ git pull —-rebase upstream master

  120. GIT REMOTES $ git pull —-rebase upstream master $ git

    push origin master
  121. GIT BISECT $ git bisect

  122. GIT BISECT $ git bisect start

  123. GIT BISECT $ git bisect bad $ git bisect good

    v4.2.rc3
  124. None
  125. Practice Git Bisect C1 C2 C3 C4 C5 C6 M3

    M4 M0
  126. AMENDING COMMITS $ git commit --amend

  127. GIT RESET $ git reset --soft HEAD@{1}

  128. GIT RESET $ git reset --soft HEAD~N

  129. GIT RESET $ git reset HEAD

  130. Git Reset M1 M2 M3 M4 M0 master HEAD Practice

  131. GIT REBASE -I $ git rebase -i master

  132. GIT

  133. 1 pick e92e34f Refactor existing code 2 pick 80601e8 Oops

    add in missing code 3 pick 8d91f76 Remove extra piece I left behind 4 pick 933bd58 Fix bug reported in #9895 5 pick 99302ef Clean up changes 6 pick 5cc3c5a Fix spelling mistake in documentation 7 8 # Rebase 0131d99..5cc3c5a onto 0131d99 9 # 10 # Commands: 11 # p, pick = use commit 12 # r, reword = use commit, but edit the commit message 13 # e, edit = use commit, but stop for amending 14 # s, squash = use commit, but meld into previous commit 15 # f, fixup = like "squash", but discard this commit's log 16 # x, exec = run command (the rest of the line) using shell
  134. 1 pick e92e34f Refactor existing code 2 pick 80601e8 Oops

    add in missing code 3 pick 8d91f76 Remove extra piece I left behind 4 pick 933bd58 Fix bug reported in #9895 5 pick 99302ef Clean up changes 6 pick 5cc3c5a Fix spelling mistake in documentation 7 8 # Rebase 0131d99..5cc3c5a onto 0131d99 9 # 10 # Commands: 11 # p, pick = use commit 12 # r, reword = use commit, but edit the commit message 13 # e, edit = use commit, but stop for amending 14 # s, squash = use commit, but meld into previous commit 15 # f, fixup = like "squash", but discard this commit's log 16 # x, exec = run command (the rest of the line) using shell
  135. 1 reword e92e34f Refactor existing code 2 pick 80601e8 Oops

    add in missing code 3 pick 8d91f76 Remove extra piece I left behind 4 pick 933bd58 Fix bug reported in #9895 5 pick 99302ef Clean up changes 6 pick 5cc3c5a Fix spelling mistake in documentation 7 8 # Rebase 0131d99..5cc3c5a onto 0131d99 9 # 10 # Commands: 11 # p, pick = use commit 12 # r, reword = use commit, but edit the commit message 13 # e, edit = use commit, but stop for amending 14 # s, squash = use commit, but meld into previous commit 15 # f, fixup = like "squash", but discard this commit's log 16 # x, exec = run command (the rest of the line) using shell
  136. 1 reword e92e34f Refactor existing code 2 pick 80601e8 Oops

    add in missing code 3 edit 8d91f76 Remove extra piece I left behind 4 pick 933bd58 Fix bug reported in #9895 5 pick 99302ef Clean up changes 6 pick 5cc3c5a Fix spelling mistake in documentation 7 8 # Rebase 0131d99..5cc3c5a onto 0131d99 9 # 10 # Commands: 11 # p, pick = use commit 12 # r, reword = use commit, but edit the commit message 13 # e, edit = use commit, but stop for amending 14 # s, squash = use commit, but meld into previous commit 15 # f, fixup = like "squash", but discard this commit's log 16 # x, exec = run command (the rest of the line) using shell
  137. 1 reword e92e34f Refactor existing code 2 pick 80601e8 Oops

    add in missing code 3 edit 8d91f76 Remove extra piece I left behind 4 pick 933bd58 Fix bug reported in #9895 5 squash 99302ef Clean up changes 6 pick 5cc3c5a Fix spelling mistake in documentation 7 8 # Rebase 0131d99..5cc3c5a onto 0131d99 9 # 10 # Commands: 11 # p, pick = use commit 12 # r, reword = use commit, but edit the commit message 13 # e, edit = use commit, but stop for amending 14 # s, squash = use commit, but meld into previous commit 15 # f, fixup = like "squash", but discard this commit's log 16 # x, exec = run command (the rest of the line) using shell
  138. 1 reword e92e34f Refactor existing code 2 fixup 80601e8 Oops

    add in missing code 3 edit 8d91f76 Remove extra piece I left behind 4 pick 933bd58 Fix bug reported in #9895 5 squash 99302ef Clean up changes 6 pick 5cc3c5a Fix spelling mistake in documentation 7 8 # Rebase 0131d99..5cc3c5a onto 0131d99 9 # 10 # Commands: 11 # p, pick = use commit 12 # r, reword = use commit, but edit the commit message 13 # e, edit = use commit, but stop for amending 14 # s, squash = use commit, but meld into previous commit 15 # f, fixup = like "squash", but discard this commit's log 16 # x, exec = run command (the rest of the line) using shell
  139. 1 reword e92e34f Refactor existing code 2 fixup 80601e8 Oops

    add in missing code 3 edit 8d91f76 Remove extra piece I left behind 4 pick 933bd58 Fix bug reported in #9895 5 squash 99302ef Clean up changes 6 pick 5cc3c5a Fix spelling mistake in documentation 7 8 # Rebase 0131d99..5cc3c5a onto 0131d99 9 # 10 # Commands: 11 # p, pick = use commit 12 # r, reword = use commit, but edit the commit message 13 # e, edit = use commit, but stop for amending 14 # s, squash = use commit, but meld into previous commit 15 # f, fixup = like "squash", but discard this commit's log 16 # x, exec = run command (the rest of the line) using shell
  140. Practice M1 M2 M3 M4 F1 F2 F1 F2 M0

    Interactive Rebase
  141. FORCE PUSH $ git push -f origin your-branch

  142. GIT REFLOG $ git reflog

  143. None
  144. GIT REFLOG $ git reflog

  145. 628c168 HEAD@{0}: rebase -i (finish): returning to refs/heads/your-branch 628c168 HEAD@{1}:

    rebase -i (start): checkout master 628c168 HEAD@{2}: reset: moving to 628c168 bc503a1 HEAD@{3}: rebase -i (finish): returning to refs/heads/your-branch bc503a1 HEAD@{4}: rebase -i (pick): Add Person model and migration 1ad2ff2 HEAD@{5}: rebase -i (pick): Improve README section on git reflog 026457d HEAD@{6}: rebase -i (start): checkout master 633f620 HEAD@{7}: rebase -i (finish): returning to refs/heads/your-branch 633f620 HEAD@{8}: rebase -i (pick): Improve README section on git reflog 19f41a7 HEAD@{9}: commit (amend): Add Person model and migration 50718b1 HEAD@{10}: rebase -i (edit): Add Person model and migration 026457d HEAD@{11}: rebase -i (reword): Update README with sections on rebase and c8c6000 HEAD@{12}: rebase -i (reword): Update README with sections on rebase and 628fbd2 HEAD@{13}: rebase -i (squash): Update README description section 455dab0 HEAD@{14}: rebase -i (fixup): # This is a combination of 2 commits. 4392779 HEAD@{15}: rebase -i (start): checkout master 628c168 HEAD@{16}: rebase -i (finish): returning to refs/heads/your-branch 628c168 HEAD@{17}: rebase -i (start): checkout master 628c168 HEAD@{18}: rebase -i (finish): returning to refs/heads/your-branch 628c168 HEAD@{19}: rebase -i (start): checkout master 628c168 HEAD@{20}: rebase: aborting 1f935f7 HEAD@{21}: rebase -i (edit): Add Person model and migration
  146. GIT REFLOG $ git reset -—hard 628c168

  147. 628168 628168 50718b bc503a1 C1 C2 C3 C4 C0 Git

    Reflog Practice
  148. FINDING ISSUES TO WORK ON

  149. WHAT TO WORK ON $ Test release candidates and that

    master branch
  150. WHAT TO WORK ON % Test release candidates and that

    master branch $ Fix documentation & work on WIP Guides
  151. WHAT TO WORK ON % Test release candidates and that

    master branch % Fix documentation & work on WIP Guides $ Focus on your strengths
  152. WHAT TO WORK ON % Test release candidates and that

    master branch % Fix documentation & work on WIP Guides % Focus on your strengths $ Work on ActiveJob or WebConsole
  153. WHAT TO WORK ON % Test release candidates and that

    master branch % Fix documentation & work on WIP Guides % Focus on your strengths % Work on ActiveJob or WebConsole $ Review open pull requests
  154. WHAT TO WORK ON % Test release candidates and that

    master branch % Fix documentation & work on WIP Guides % Focus on your strengths % Work on ActiveJob or WebConsole % Review open pull requests $ Refactoring methods and tests
  155. WHAT TO WORK ON % Test release candidates and that

    master branch % Fix documentation & work on WIP Guides % Focus on your strengths % Work on ActiveJob or WebConsole % Review open pull requests % Refactoring methods and tests $ Watch the issues tracker
  156. WHAT WE COVERED % Getting set up % Guidelines for

    opening issues % Writing test scripts % Guidelines for opening pull requests % Git % Bisect, Interactive Rebase, Reflog % Traversing unfamiliar code % source_location, CTags, puts caller, TracePoint % Finding issues to work on
  157. Thank You!

  158. EILEEN M. UCHITELLE Programmer at Basecamp ! eileencodes.com " @eileencodes

    # @eileencodes Slides: speakerdeck.com/eileencodes