[{"data":1,"prerenderedAt":3139},["ShallowReactive",2],{"post:\u002F2021\u002F01\u002F13\u002Flink-shortener-part-1":3},{"id":4,"title":5,"body":6,"date":3131,"description":15,"extension":3132,"meta":3133,"navigation":346,"path":3134,"seo":3135,"stem":3136,"subtitle":3137,"__hash__":3138},"posts\u002F2021\u002F01\u002F13\u002Flink-shortener-part-1.md","Link Shortener: Part 1",{"type":7,"value":8,"toc":3129},"minimark",[9,17,30,33,36,39,42,45,50,53,104,107,116,168,171,192,244,247,262,265,278,281,296,299,304,310,1111,1114,1116,1120,1123,1152,1155,1269,1272,1286,1289,1383,1386,1389,1547,1550,1567,1570,1572,1576,1579,1598,1607,1610,1613,1628,1631,1700,1703,3008,3018,3021,3043,3100,3103,3106,3111,3113,3117,3125],[10,11,12],"p",{},[13,14],"img",{"alt":15,"src":16},"","https:\u002F\u002Fmybigplunge.com\u002Fwp-content\u002Fuploads\u002F2018\u002F04\u002Furl-shortener-theplungedaily.jpg",[10,18,19],{},[20,21,22,23],"strong",{},"You can find the github repo for this project ",[24,25,29],"a",{"href":26,"rel":27},"https:\u002F\u002Fgithub.com\u002Fconorbr\u002Furl_shortener",[28],"nofollow","here",[10,31,32],{},"Well, It's been some time since my last post. And I'm well aware very few people, if any actually reads these. So lets just awkwardly pretend we didn't see each other and go on about our day.",[10,34,35],{},"so!",[10,37,38],{},"Lets talk url shorteners, url shorteners are a super handy way to grab a long and cumbersome http link and shorten it. This is great when we want to post a link somewhere there's a strict character limit, have to type something manually or are posing as a Nigerian prince looking for some temporary funds to gain access to their wider fortune.",[10,40,41],{},"Link shorteners really are the backbone of the modern age. And what's more they're relativey easy to implement. Just store the long url you want to go to, set an short easy to remember word (or slug) and when the time comes, take the slug and redirect to the corresponding url.",[10,43,44],{},"We're going to create an api allows us to CRUD the absolute beans out of a link record and when we ask politely it will redirect us to the target website. To do this we're going to start with Rails.",[10,46,47],{},[13,48],{"alt":15,"src":49},"https:\u002F\u002Ffiverr-res.cloudinary.com\u002Fimages\u002Fq_auto,f_auto\u002Fgigs\u002F107042204\u002Foriginal\u002Fb981cd0ed3a32f6e94a0ab767688e6a8de2dfa59\u002Fcreate-a-rails-api-for-your-application-or-website.png",[10,51,52],{},"Rails has introduced a very handy API only version of itself, that allows you to ignore all of the clunky views and unnecessary boilerplate and create a super lean api that it just so happens lends itself quite well to this very purpose.",[54,55,59],"pre",{"className":56,"code":57,"language":58,"meta":15,"style":15},"language-shell shiki shiki-themes github-dark","$ rails new link_shortener --api -d postgresql -T && cd link_shortener\n","shell",[60,61,62],"code",{"__ignoreMap":15},[63,64,67,71,75,78,81,85,88,91,94,98,101],"span",{"class":65,"line":66},"line",1,[63,68,70],{"class":69},"svObZ","$",[63,72,74],{"class":73},"sU2Wk"," rails",[63,76,77],{"class":73}," new",[63,79,80],{"class":73}," link_shortener",[63,82,84],{"class":83},"sDLfK"," --api",[63,86,87],{"class":83}," -d",[63,89,90],{"class":73}," postgresql",[63,92,93],{"class":83}," -T",[63,95,97],{"class":96},"s95oV"," && ",[63,99,100],{"class":83},"cd",[63,102,103],{"class":73}," link_shortener\n",[10,105,106],{},"That unsuspecting little line just created our rails api, set the database and for good measure didn't set the default test folders, but more on that later. And for a little added flare it will cd you into the project. But that's just me being fancy.",[10,108,109,110,115],{},"First things first, lets talk tests. In our gemfile we're going to add the ",[24,111,114],{"href":112,"rel":113},"https:\u002F\u002Fgithub.com\u002Frspec\u002Frspec",[28],"rspec-rails gem",".",[54,117,121],{"className":118,"code":119,"language":120,"meta":15,"style":15},"language-ruby shiki shiki-themes github-dark","group :development, :test do\n  #[...]\n  gem 'rspec-rails', '~> 3.5'\nend\n","ruby",[60,122,123,141,148,162],{"__ignoreMap":15},[63,124,125,128,131,134,137],{"class":65,"line":66},[63,126,127],{"class":96},"group ",[63,129,130],{"class":83},":development",[63,132,133],{"class":96},", ",[63,135,136],{"class":83},":test",[63,138,140],{"class":139},"snl16"," do\n",[63,142,144],{"class":65,"line":143},2,[63,145,147],{"class":146},"sAwPA","  #[...]\n",[63,149,151,154,157,159],{"class":65,"line":150},3,[63,152,153],{"class":96},"  gem ",[63,155,156],{"class":73},"'rspec-rails'",[63,158,133],{"class":96},[63,160,161],{"class":73},"'~> 3.5'\n",[63,163,165],{"class":65,"line":164},4,[63,166,167],{"class":139},"end\n",[10,169,170],{},"This is a little shorthand to require the gem in both your development and test environments.",[10,172,173,174,133,177,133,180,183,184,187,188,191],{},"Now add ",[60,175,176],{},"factory_bot_rails",[60,178,179],{},"shoulda_matchers",[60,181,182],{},"faker"," and ",[60,185,186],{},"database_cleaner"," to the ",[189,190],"test",{}," group.",[54,193,195],{"className":118,"code":194,"language":120,"meta":15,"style":15},"group :test do\n  gem 'database_cleaner'\n  gem 'factory_bot_rails'\n  gem 'faker'\n  gem 'shoulda-matchers', '~> 4.0'\nend\n",[60,196,197,205,212,219,226,239],{"__ignoreMap":15},[63,198,199,201,203],{"class":65,"line":66},[63,200,127],{"class":96},[63,202,136],{"class":83},[63,204,140],{"class":139},[63,206,207,209],{"class":65,"line":143},[63,208,153],{"class":96},[63,210,211],{"class":73},"'database_cleaner'\n",[63,213,214,216],{"class":65,"line":150},[63,215,153],{"class":96},[63,217,218],{"class":73},"'factory_bot_rails'\n",[63,220,221,223],{"class":65,"line":164},[63,222,153],{"class":96},[63,224,225],{"class":73},"'faker'\n",[63,227,229,231,234,236],{"class":65,"line":228},5,[63,230,153],{"class":96},[63,232,233],{"class":73},"'shoulda-matchers'",[63,235,133],{"class":96},[63,237,238],{"class":73},"'~> 4.0'\n",[63,240,242],{"class":65,"line":241},6,[63,243,167],{"class":139},[10,245,246],{},"Install these gems by running",[54,248,250],{"className":56,"code":249,"language":58,"meta":15,"style":15},"$ bundle install\n",[60,251,252],{"__ignoreMap":15},[63,253,254,256,259],{"class":65,"line":66},[63,255,70],{"class":69},[63,257,258],{"class":73}," bundle",[63,260,261],{"class":73}," install\n",[10,263,264],{},"This adds the following files which are used for configuration:",[266,267,268,272,275],"ul",{},[269,270,271],"li",{},".rspec",[269,273,274],{},"spec\u002Fspec_helper.rb",[269,276,277],{},"spec\u002Frails_helper.rb",[10,279,280],{},"Create a factories directory (factory bot uses this as the default directory). This is where we’ll define the model factories.",[54,282,284],{"className":56,"code":283,"language":58,"meta":15,"style":15},"$ mkdir spec\u002Ffactories\n",[60,285,286],{"__ignoreMap":15},[63,287,288,290,293],{"class":65,"line":66},[63,289,70],{"class":69},[63,291,292],{"class":73}," mkdir",[63,294,295],{"class":73}," spec\u002Ffactories\n",[297,298],"hr",{},[300,301,303],"h4",{"id":302},"configuration","Configuration",[10,305,306,307],{},"Configure your ",[60,308,309],{},"\u002Fspec\u002Frails_helper.rb",[54,311,313],{"className":118,"code":312,"language":120,"meta":15,"style":15},"# This file is copied to spec\u002F when you run 'rails generate rspec:install'\nrequire 'spec_helper'\nrequire 'rspec\u002Frails'\nrequire 'database_cleaner'\n\nENV['RAILS_ENV'] ||= 'test'\n\nrequire File.expand_path('..\u002Fconfig\u002Fenvironment', __dir__)\n\n# Prevent database truncation if the environment is production\nabort(\"The Rails environment is running in production mode!\") if Rails.env.production?\n\n# require support files\nDir[Rails.root.join('spec\u002Fsupport\u002F**\u002F*.rb')].sort.each { |f| require f }\n\n# [...]\nbegin\n  ActiveRecord::Migration.maintain_test_schema!\nrescue ActiveRecord::PendingMigrationError => e\n  puts e.to_s.strip\n  exit 1\nend\n\n# [...]\nRSpec.configure do |config|\n  # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures\n  config.fixture_path = \"#{::Rails.root}\u002Fspec\u002Ffixtures\"\n\n  config.use_transactional_fixtures = false\n\n  config.before(:suite) do\n    DatabaseCleaner.clean_with(:truncation)\n    Faker::Name.unique.clear # Clears used values for Faker::Name\n    Faker::UniqueGenerator.clear # Clears used values for all generators\n  end\n\n  config.before(:each) do\n    DatabaseCleaner.strategy = :transaction\n  end\n\n  config.before(:each, js: true) do\n    DatabaseCleaner.strategy = :truncation\n  end\n\n  config.before(:each) do\n    DatabaseCleaner.start\n  end\n\n  config.after(:each) do\n    DatabaseCleaner.clean\n  end\n\n  config.before(:all) do\n    DatabaseCleaner.start\n  end\n\n  config.after(:all) do\n    DatabaseCleaner.clean\n  end\n\n  config.infer_spec_type_from_file_location!\n\n  # Filter lines from Rails gems in backtraces.\n  config.filter_rails_from_backtrace!\n\n  config.include FactoryBot::Syntax::Methods\n\n  Shoulda::Matchers.configure do |config|\n    config.integrate do |with|\n      with.test_framework :rspec\n      with.library :rails\n    end\n  end\nend\n\n",[60,314,315,320,328,335,342,348,368,373,400,405,411,441,446,452,497,502,508,514,531,548,565,574,579,584,589,606,612,642,647,660,665,683,701,725,742,748,753,769,784,789,794,817,831,836,841,856,866,871,876,892,902,907,912,928,937,942,947,962,971,976,981,989,994,1000,1008,1013,1034,1039,1058,1072,1084,1095,1101,1106],{"__ignoreMap":15},[63,316,317],{"class":65,"line":66},[63,318,319],{"class":146},"# This file is copied to spec\u002F when you run 'rails generate rspec:install'\n",[63,321,322,325],{"class":65,"line":143},[63,323,324],{"class":139},"require",[63,326,327],{"class":73}," 'spec_helper'\n",[63,329,330,332],{"class":65,"line":150},[63,331,324],{"class":139},[63,333,334],{"class":73}," 'rspec\u002Frails'\n",[63,336,337,339],{"class":65,"line":164},[63,338,324],{"class":139},[63,340,341],{"class":73}," 'database_cleaner'\n",[63,343,344],{"class":65,"line":228},[63,345,347],{"emptyLinePlaceholder":346},true,"\n",[63,349,350,353,356,359,362,365],{"class":65,"line":241},[63,351,352],{"class":83},"ENV",[63,354,355],{"class":96},"[",[63,357,358],{"class":73},"'RAILS_ENV'",[63,360,361],{"class":96},"] ",[63,363,364],{"class":139},"||=",[63,366,367],{"class":73}," 'test'\n",[63,369,371],{"class":65,"line":370},7,[63,372,347],{"emptyLinePlaceholder":346},[63,374,376,378,381,383,386,389,392,394,397],{"class":65,"line":375},8,[63,377,324],{"class":139},[63,379,380],{"class":83}," File",[63,382,115],{"class":96},[63,384,385],{"class":69},"expand_path",[63,387,388],{"class":96},"(",[63,390,391],{"class":73},"'..\u002Fconfig\u002Fenvironment'",[63,393,133],{"class":96},[63,395,396],{"class":83},"__dir__",[63,398,399],{"class":96},")\n",[63,401,403],{"class":65,"line":402},9,[63,404,347],{"emptyLinePlaceholder":346},[63,406,408],{"class":65,"line":407},10,[63,409,410],{"class":146},"# Prevent database truncation if the environment is production\n",[63,412,414,417,419,422,425,428,431,433,436,438],{"class":65,"line":413},11,[63,415,416],{"class":83},"abort",[63,418,388],{"class":96},[63,420,421],{"class":73},"\"The Rails environment is running in production mode!\"",[63,423,424],{"class":96},") ",[63,426,427],{"class":139},"if",[63,429,430],{"class":83}," Rails",[63,432,115],{"class":96},[63,434,435],{"class":69},"env",[63,437,115],{"class":96},[63,439,440],{"class":69},"production?\n",[63,442,444],{"class":65,"line":443},12,[63,445,347],{"emptyLinePlaceholder":346},[63,447,449],{"class":65,"line":448},13,[63,450,451],{"class":146},"# require support files\n",[63,453,455,458,460,463,465,468,470,473,475,478,481,484,486,489,492,494],{"class":65,"line":454},14,[63,456,457],{"class":83},"Dir",[63,459,355],{"class":96},[63,461,462],{"class":83},"Rails",[63,464,115],{"class":96},[63,466,467],{"class":69},"root",[63,469,115],{"class":96},[63,471,472],{"class":69},"join",[63,474,388],{"class":96},[63,476,477],{"class":73},"'spec\u002Fsupport\u002F**\u002F*.rb'",[63,479,480],{"class":96},")].",[63,482,483],{"class":69},"sort",[63,485,115],{"class":96},[63,487,488],{"class":69},"each",[63,490,491],{"class":96}," { |f| ",[63,493,324],{"class":139},[63,495,496],{"class":96}," f }\n",[63,498,500],{"class":65,"line":499},15,[63,501,347],{"emptyLinePlaceholder":346},[63,503,505],{"class":65,"line":504},16,[63,506,507],{"class":146},"# [...]\n",[63,509,511],{"class":65,"line":510},17,[63,512,513],{"class":139},"begin\n",[63,515,517,520,523,526,528],{"class":65,"line":516},18,[63,518,519],{"class":83},"  ActiveRecord",[63,521,522],{"class":96},"::",[63,524,525],{"class":83},"Migration",[63,527,115],{"class":96},[63,529,530],{"class":69},"maintain_test_schema!\n",[63,532,534,537,540,542,545],{"class":65,"line":533},19,[63,535,536],{"class":139},"rescue",[63,538,539],{"class":83}," ActiveRecord",[63,541,522],{"class":96},[63,543,544],{"class":83},"PendingMigrationError",[63,546,547],{"class":96}," => e\n",[63,549,551,554,557,560,562],{"class":65,"line":550},20,[63,552,553],{"class":83},"  puts",[63,555,556],{"class":96}," e.",[63,558,559],{"class":69},"to_s",[63,561,115],{"class":96},[63,563,564],{"class":69},"strip\n",[63,566,568,571],{"class":65,"line":567},21,[63,569,570],{"class":83},"  exit",[63,572,573],{"class":83}," 1\n",[63,575,577],{"class":65,"line":576},22,[63,578,167],{"class":139},[63,580,582],{"class":65,"line":581},23,[63,583,347],{"emptyLinePlaceholder":346},[63,585,587],{"class":65,"line":586},24,[63,588,507],{"class":146},[63,590,592,595,597,600,603],{"class":65,"line":591},25,[63,593,594],{"class":83},"RSpec",[63,596,115],{"class":96},[63,598,599],{"class":69},"configure",[63,601,602],{"class":139}," do",[63,604,605],{"class":96}," |config|\n",[63,607,609],{"class":65,"line":608},26,[63,610,611],{"class":146},"  # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures\n",[63,613,615,618,621,624,627,630,632,634,636,639],{"class":65,"line":614},27,[63,616,617],{"class":96},"  config.",[63,619,620],{"class":69},"fixture_path",[63,622,623],{"class":139}," =",[63,625,626],{"class":73}," \"",[63,628,629],{"class":73},"#{::",[63,631,462],{"class":83},[63,633,115],{"class":73},[63,635,467],{"class":69},[63,637,638],{"class":73},"}",[63,640,641],{"class":73},"\u002Fspec\u002Ffixtures\"\n",[63,643,645],{"class":65,"line":644},28,[63,646,347],{"emptyLinePlaceholder":346},[63,648,650,652,655,657],{"class":65,"line":649},29,[63,651,617],{"class":96},[63,653,654],{"class":69},"use_transactional_fixtures",[63,656,623],{"class":139},[63,658,659],{"class":83}," false\n",[63,661,663],{"class":65,"line":662},30,[63,664,347],{"emptyLinePlaceholder":346},[63,666,668,670,673,675,678,680],{"class":65,"line":667},31,[63,669,617],{"class":96},[63,671,672],{"class":69},"before",[63,674,388],{"class":96},[63,676,677],{"class":83},":suite",[63,679,424],{"class":96},[63,681,682],{"class":139},"do\n",[63,684,686,689,691,694,696,699],{"class":65,"line":685},32,[63,687,688],{"class":83},"    DatabaseCleaner",[63,690,115],{"class":96},[63,692,693],{"class":69},"clean_with",[63,695,388],{"class":96},[63,697,698],{"class":83},":truncation",[63,700,399],{"class":96},[63,702,704,707,709,712,714,717,719,722],{"class":65,"line":703},33,[63,705,706],{"class":83},"    Faker",[63,708,522],{"class":96},[63,710,711],{"class":83},"Name",[63,713,115],{"class":96},[63,715,716],{"class":69},"unique",[63,718,115],{"class":96},[63,720,721],{"class":69},"clear",[63,723,724],{"class":146}," # Clears used values for Faker::Name\n",[63,726,728,730,732,735,737,739],{"class":65,"line":727},34,[63,729,706],{"class":83},[63,731,522],{"class":96},[63,733,734],{"class":83},"UniqueGenerator",[63,736,115],{"class":96},[63,738,721],{"class":69},[63,740,741],{"class":146}," # Clears used values for all generators\n",[63,743,745],{"class":65,"line":744},35,[63,746,747],{"class":139},"  end\n",[63,749,751],{"class":65,"line":750},36,[63,752,347],{"emptyLinePlaceholder":346},[63,754,756,758,760,762,765,767],{"class":65,"line":755},37,[63,757,617],{"class":96},[63,759,672],{"class":69},[63,761,388],{"class":96},[63,763,764],{"class":83},":each",[63,766,424],{"class":96},[63,768,682],{"class":139},[63,770,772,774,776,779,781],{"class":65,"line":771},38,[63,773,688],{"class":83},[63,775,115],{"class":96},[63,777,778],{"class":69},"strategy",[63,780,623],{"class":139},[63,782,783],{"class":83}," :transaction\n",[63,785,787],{"class":65,"line":786},39,[63,788,747],{"class":139},[63,790,792],{"class":65,"line":791},40,[63,793,347],{"emptyLinePlaceholder":346},[63,795,797,799,801,803,805,807,810,813,815],{"class":65,"line":796},41,[63,798,617],{"class":96},[63,800,672],{"class":69},[63,802,388],{"class":96},[63,804,764],{"class":83},[63,806,133],{"class":96},[63,808,809],{"class":83},"js:",[63,811,812],{"class":83}," true",[63,814,424],{"class":96},[63,816,682],{"class":139},[63,818,820,822,824,826,828],{"class":65,"line":819},42,[63,821,688],{"class":83},[63,823,115],{"class":96},[63,825,778],{"class":69},[63,827,623],{"class":139},[63,829,830],{"class":83}," :truncation\n",[63,832,834],{"class":65,"line":833},43,[63,835,747],{"class":139},[63,837,839],{"class":65,"line":838},44,[63,840,347],{"emptyLinePlaceholder":346},[63,842,844,846,848,850,852,854],{"class":65,"line":843},45,[63,845,617],{"class":96},[63,847,672],{"class":69},[63,849,388],{"class":96},[63,851,764],{"class":83},[63,853,424],{"class":96},[63,855,682],{"class":139},[63,857,859,861,863],{"class":65,"line":858},46,[63,860,688],{"class":83},[63,862,115],{"class":96},[63,864,865],{"class":69},"start\n",[63,867,869],{"class":65,"line":868},47,[63,870,747],{"class":139},[63,872,874],{"class":65,"line":873},48,[63,875,347],{"emptyLinePlaceholder":346},[63,877,879,881,884,886,888,890],{"class":65,"line":878},49,[63,880,617],{"class":96},[63,882,883],{"class":69},"after",[63,885,388],{"class":96},[63,887,764],{"class":83},[63,889,424],{"class":96},[63,891,682],{"class":139},[63,893,895,897,899],{"class":65,"line":894},50,[63,896,688],{"class":83},[63,898,115],{"class":96},[63,900,901],{"class":69},"clean\n",[63,903,905],{"class":65,"line":904},51,[63,906,747],{"class":139},[63,908,910],{"class":65,"line":909},52,[63,911,347],{"emptyLinePlaceholder":346},[63,913,915,917,919,921,924,926],{"class":65,"line":914},53,[63,916,617],{"class":96},[63,918,672],{"class":69},[63,920,388],{"class":96},[63,922,923],{"class":83},":all",[63,925,424],{"class":96},[63,927,682],{"class":139},[63,929,931,933,935],{"class":65,"line":930},54,[63,932,688],{"class":83},[63,934,115],{"class":96},[63,936,865],{"class":69},[63,938,940],{"class":65,"line":939},55,[63,941,747],{"class":139},[63,943,945],{"class":65,"line":944},56,[63,946,347],{"emptyLinePlaceholder":346},[63,948,950,952,954,956,958,960],{"class":65,"line":949},57,[63,951,617],{"class":96},[63,953,883],{"class":69},[63,955,388],{"class":96},[63,957,923],{"class":83},[63,959,424],{"class":96},[63,961,682],{"class":139},[63,963,965,967,969],{"class":65,"line":964},58,[63,966,688],{"class":83},[63,968,115],{"class":96},[63,970,901],{"class":69},[63,972,974],{"class":65,"line":973},59,[63,975,747],{"class":139},[63,977,979],{"class":65,"line":978},60,[63,980,347],{"emptyLinePlaceholder":346},[63,982,984,986],{"class":65,"line":983},61,[63,985,617],{"class":96},[63,987,988],{"class":69},"infer_spec_type_from_file_location!\n",[63,990,992],{"class":65,"line":991},62,[63,993,347],{"emptyLinePlaceholder":346},[63,995,997],{"class":65,"line":996},63,[63,998,999],{"class":146},"  # Filter lines from Rails gems in backtraces.\n",[63,1001,1003,1005],{"class":65,"line":1002},64,[63,1004,617],{"class":96},[63,1006,1007],{"class":69},"filter_rails_from_backtrace!\n",[63,1009,1011],{"class":65,"line":1010},65,[63,1012,347],{"emptyLinePlaceholder":346},[63,1014,1016,1018,1021,1024,1026,1029,1031],{"class":65,"line":1015},66,[63,1017,617],{"class":96},[63,1019,1020],{"class":139},"include",[63,1022,1023],{"class":83}," FactoryBot",[63,1025,522],{"class":96},[63,1027,1028],{"class":83},"Syntax",[63,1030,522],{"class":96},[63,1032,1033],{"class":83},"Methods\n",[63,1035,1037],{"class":65,"line":1036},67,[63,1038,347],{"emptyLinePlaceholder":346},[63,1040,1042,1045,1047,1050,1052,1054,1056],{"class":65,"line":1041},68,[63,1043,1044],{"class":83},"  Shoulda",[63,1046,522],{"class":96},[63,1048,1049],{"class":83},"Matchers",[63,1051,115],{"class":96},[63,1053,599],{"class":69},[63,1055,602],{"class":139},[63,1057,605],{"class":96},[63,1059,1061,1064,1067,1069],{"class":65,"line":1060},69,[63,1062,1063],{"class":96},"    config.",[63,1065,1066],{"class":69},"integrate",[63,1068,602],{"class":139},[63,1070,1071],{"class":96}," |with|\n",[63,1073,1075,1078,1081],{"class":65,"line":1074},70,[63,1076,1077],{"class":96},"      with.",[63,1079,1080],{"class":69},"test_framework",[63,1082,1083],{"class":83}," :rspec\n",[63,1085,1087,1089,1092],{"class":65,"line":1086},71,[63,1088,1077],{"class":96},[63,1090,1091],{"class":69},"library",[63,1093,1094],{"class":83}," :rails\n",[63,1096,1098],{"class":65,"line":1097},72,[63,1099,1100],{"class":139},"    end\n",[63,1102,1104],{"class":65,"line":1103},73,[63,1105,747],{"class":139},[63,1107,1109],{"class":65,"line":1108},74,[63,1110,167],{"class":139},[10,1112,1113],{},"Oooft that was a lot. But it's done, its easy from here on out.",[297,1115],{},[300,1117,1119],{"id":1118},"model","Model",[10,1121,1122],{},"Ok, time to hit the model.",[54,1124,1126],{"className":56,"code":1125,"language":58,"meta":15,"style":15},"$ rails g model Link url:string slug:string clicked:integer\n",[60,1127,1128],{"__ignoreMap":15},[63,1129,1130,1132,1134,1137,1140,1143,1146,1149],{"class":65,"line":66},[63,1131,70],{"class":69},[63,1133,74],{"class":73},[63,1135,1136],{"class":73}," g",[63,1138,1139],{"class":73}," model",[63,1141,1142],{"class":73}," Link",[63,1144,1145],{"class":73}," url:string",[63,1147,1148],{"class":73}," slug:string",[63,1150,1151],{"class":73}," clicked:integer\n",[10,1153,1154],{},"This will result in the mirgration file being generated",[54,1156,1158],{"className":118,"code":1157,"language":120,"meta":15,"style":15},"# db\u002Fmigrate\u002F[timestamp]_create_links.rb\nclass CreateLinks \u003C ActiveRecord::Migration[6.0]\n  def change\n    create_table :links do |t|\n      t.string :url\n      t.string :slug\n      t.integer :clicked, default: 0\n\n      t.timestamps\n    end\n  end\nend\n",[60,1159,1160,1165,1187,1195,1208,1219,1228,1246,1250,1257,1261,1265],{"__ignoreMap":15},[63,1161,1162],{"class":65,"line":66},[63,1163,1164],{"class":146},"# db\u002Fmigrate\u002F[timestamp]_create_links.rb\n",[63,1166,1167,1170,1173,1176,1179,1181,1184],{"class":65,"line":143},[63,1168,1169],{"class":139},"class",[63,1171,1172],{"class":69}," CreateLinks",[63,1174,1175],{"class":96}," \u003C ",[63,1177,1178],{"class":69},"ActiveRecord::Migration",[63,1180,355],{"class":96},[63,1182,1183],{"class":83},"6.0",[63,1185,1186],{"class":96},"]\n",[63,1188,1189,1192],{"class":65,"line":150},[63,1190,1191],{"class":139},"  def",[63,1193,1194],{"class":69}," change\n",[63,1196,1197,1200,1203,1205],{"class":65,"line":164},[63,1198,1199],{"class":96},"    create_table ",[63,1201,1202],{"class":83},":links",[63,1204,602],{"class":139},[63,1206,1207],{"class":96}," |t|\n",[63,1209,1210,1213,1216],{"class":65,"line":228},[63,1211,1212],{"class":96},"      t.",[63,1214,1215],{"class":69},"string",[63,1217,1218],{"class":83}," :url\n",[63,1220,1221,1223,1225],{"class":65,"line":241},[63,1222,1212],{"class":96},[63,1224,1215],{"class":69},[63,1226,1227],{"class":83}," :slug\n",[63,1229,1230,1232,1235,1238,1240,1243],{"class":65,"line":370},[63,1231,1212],{"class":96},[63,1233,1234],{"class":69},"integer",[63,1236,1237],{"class":83}," :clicked",[63,1239,133],{"class":96},[63,1241,1242],{"class":83},"default:",[63,1244,1245],{"class":83}," 0\n",[63,1247,1248],{"class":65,"line":375},[63,1249,347],{"emptyLinePlaceholder":346},[63,1251,1252,1254],{"class":65,"line":402},[63,1253,1212],{"class":96},[63,1255,1256],{"class":69},"timestamps\n",[63,1258,1259],{"class":65,"line":407},[63,1260,1100],{"class":139},[63,1262,1263],{"class":65,"line":413},[63,1264,747],{"class":139},[63,1266,1267],{"class":65,"line":443},[63,1268,167],{"class":139},[10,1270,1271],{},"Looks good, lets get that change into the database",[54,1273,1275],{"className":56,"code":1274,"language":58,"meta":15,"style":15},"$ rails db:migrate\n",[60,1276,1277],{"__ignoreMap":15},[63,1278,1279,1281,1283],{"class":65,"line":66},[63,1280,70],{"class":69},[63,1282,74],{"class":73},[63,1284,1285],{"class":73}," db:migrate\n",[10,1287,1288],{},"We're test driven so lets go ahead and write the model specs",[54,1290,1292],{"className":118,"code":1291,"language":120,"meta":15,"style":15},"require 'rails_helper'\n\n# Test suite for Link model\nRSpec.describe Link, type: :model do\n  # Validation tests\n  it { should validate_presence_of(:slug) }\n  it { should validate_presence_of(:url) }\n  it { should validate_numericality_of(:clicked) }\nend\n",[60,1293,1294,1301,1305,1310,1331,1336,1352,1365,1379],{"__ignoreMap":15},[63,1295,1296,1298],{"class":65,"line":66},[63,1297,324],{"class":139},[63,1299,1300],{"class":73}," 'rails_helper'\n",[63,1302,1303],{"class":65,"line":143},[63,1304,347],{"emptyLinePlaceholder":346},[63,1306,1307],{"class":65,"line":150},[63,1308,1309],{"class":146},"# Test suite for Link model\n",[63,1311,1312,1314,1316,1319,1321,1323,1326,1329],{"class":65,"line":164},[63,1313,594],{"class":83},[63,1315,115],{"class":96},[63,1317,1318],{"class":69},"describe",[63,1320,1142],{"class":83},[63,1322,133],{"class":96},[63,1324,1325],{"class":83},"type:",[63,1327,1328],{"class":83}," :model",[63,1330,140],{"class":139},[63,1332,1333],{"class":65,"line":228},[63,1334,1335],{"class":146},"  # Validation tests\n",[63,1337,1338,1341,1344,1346,1349],{"class":65,"line":241},[63,1339,1340],{"class":96},"  it { should ",[63,1342,1343],{"class":69},"validate_presence_of",[63,1345,388],{"class":96},[63,1347,1348],{"class":83},":slug",[63,1350,1351],{"class":96},") }\n",[63,1353,1354,1356,1358,1360,1363],{"class":65,"line":370},[63,1355,1340],{"class":96},[63,1357,1343],{"class":69},[63,1359,388],{"class":96},[63,1361,1362],{"class":83},":url",[63,1364,1351],{"class":96},[63,1366,1367,1369,1372,1374,1377],{"class":65,"line":375},[63,1368,1340],{"class":96},[63,1370,1371],{"class":69},"validate_numericality_of",[63,1373,388],{"class":96},[63,1375,1376],{"class":83},":clicked",[63,1378,1351],{"class":96},[63,1380,1381],{"class":65,"line":402},[63,1382,167],{"class":139},[10,1384,1385],{},"Rspec has some very expressive language, its almost like reading a sentence. And those nifty shoulda matchers we referenced in our gemfile are coming in clutch.",[10,1387,1388],{},"Now these wont pass as is, we're testing model validations that don't exist. So rather than spoil the mood with some red dots lets go ahead and add some validations to the links model",[54,1390,1392],{"className":118,"code":1391,"language":120,"meta":15,"style":15},"class Link \u003C ApplicationRecord\n  before_validation :format_url\n\n  validates_presence_of :url\n  validates_presence_of :slug\n\n  validates_uniqueness_of :slug\n\n  validates :url, format: URI::DEFAULT_PARSER.make_regexp(%w[http https])\n  validates_numericality_of :clicked\n\n  validates_length_of :url, within: 3..255, on: :create\n  validates_length_of :slug, within: 1..255, on: :create\nend\n",[60,1393,1394,1405,1413,1417,1425,1432,1436,1443,1447,1479,1487,1491,1520,1543],{"__ignoreMap":15},[63,1395,1396,1398,1400,1402],{"class":65,"line":66},[63,1397,1169],{"class":139},[63,1399,1142],{"class":69},[63,1401,1175],{"class":96},[63,1403,1404],{"class":69},"ApplicationRecord\n",[63,1406,1407,1410],{"class":65,"line":143},[63,1408,1409],{"class":96},"  before_validation ",[63,1411,1412],{"class":83},":format_url\n",[63,1414,1415],{"class":65,"line":150},[63,1416,347],{"emptyLinePlaceholder":346},[63,1418,1419,1422],{"class":65,"line":164},[63,1420,1421],{"class":96},"  validates_presence_of ",[63,1423,1424],{"class":83},":url\n",[63,1426,1427,1429],{"class":65,"line":228},[63,1428,1421],{"class":96},[63,1430,1431],{"class":83},":slug\n",[63,1433,1434],{"class":65,"line":241},[63,1435,347],{"emptyLinePlaceholder":346},[63,1437,1438,1441],{"class":65,"line":370},[63,1439,1440],{"class":96},"  validates_uniqueness_of ",[63,1442,1431],{"class":83},[63,1444,1445],{"class":65,"line":375},[63,1446,347],{"emptyLinePlaceholder":346},[63,1448,1449,1452,1454,1456,1459,1462,1464,1467,1469,1472,1474,1477],{"class":65,"line":402},[63,1450,1451],{"class":96},"  validates ",[63,1453,1362],{"class":83},[63,1455,133],{"class":96},[63,1457,1458],{"class":83},"format:",[63,1460,1461],{"class":83}," URI",[63,1463,522],{"class":96},[63,1465,1466],{"class":83},"DEFAULT_PARSER",[63,1468,115],{"class":96},[63,1470,1471],{"class":69},"make_regexp",[63,1473,388],{"class":96},[63,1475,1476],{"class":73},"%w[http https]",[63,1478,399],{"class":96},[63,1480,1481,1484],{"class":65,"line":407},[63,1482,1483],{"class":96},"  validates_numericality_of ",[63,1485,1486],{"class":83},":clicked\n",[63,1488,1489],{"class":65,"line":413},[63,1490,347],{"emptyLinePlaceholder":346},[63,1492,1493,1496,1498,1500,1503,1506,1509,1512,1514,1517],{"class":65,"line":443},[63,1494,1495],{"class":96},"  validates_length_of ",[63,1497,1362],{"class":83},[63,1499,133],{"class":96},[63,1501,1502],{"class":83},"within:",[63,1504,1505],{"class":83}," 3",[63,1507,1508],{"class":96},"..",[63,1510,1511],{"class":83},"255",[63,1513,133],{"class":96},[63,1515,1516],{"class":83},"on:",[63,1518,1519],{"class":83}," :create\n",[63,1521,1522,1524,1526,1528,1530,1533,1535,1537,1539,1541],{"class":65,"line":448},[63,1523,1495],{"class":96},[63,1525,1348],{"class":83},[63,1527,133],{"class":96},[63,1529,1502],{"class":83},[63,1531,1532],{"class":83}," 1",[63,1534,1508],{"class":96},[63,1536,1511],{"class":83},[63,1538,133],{"class":96},[63,1540,1516],{"class":83},[63,1542,1519],{"class":83},[63,1544,1545],{"class":65,"line":454},[63,1546,167],{"class":139},[10,1548,1549],{},"now run",[54,1551,1553],{"className":56,"code":1552,"language":58,"meta":15,"style":15},"$ bundle exec rspec\n",[60,1554,1555],{"__ignoreMap":15},[63,1556,1557,1559,1561,1564],{"class":65,"line":66},[63,1558,70],{"class":69},[63,1560,258],{"class":73},[63,1562,1563],{"class":73}," exec",[63,1565,1566],{"class":73}," rspec\n",[10,1568,1569],{},"And that's it, all green baby!",[297,1571],{},[300,1573,1575],{"id":1574},"controller","Controller",[10,1577,1578],{},"Now that our models are all setup, let’s generate the controllers.",[54,1580,1582],{"className":56,"code":1581,"language":58,"meta":15,"style":15},"$ rails g controller Links\n",[60,1583,1584],{"__ignoreMap":15},[63,1585,1586,1588,1590,1592,1595],{"class":65,"line":66},[63,1587,70],{"class":69},[63,1589,74],{"class":73},[63,1591,1136],{"class":73},[63,1593,1594],{"class":73}," controller",[63,1596,1597],{"class":73}," Links\n",[10,1599,1600,1601,1606],{},"And while we're working with an API application we're going to be writing ",[24,1602,1605],{"href":1603,"rel":1604},"https:\u002F\u002Frelishapp.com\u002Frspec\u002Frspec-rails\u002Fdocs\u002Frequest-specs\u002Frequest-spec",[28],"request specs"," instead.",[10,1608,1609],{},"Request specs work differently to normal controller tests, they're going to simulate http requests to the application rather than triggering the methods in our controller. And that's exactly what we're looking for.",[10,1611,1612],{},"Lets setup our factory",[54,1614,1616],{"className":56,"code":1615,"language":58,"meta":15,"style":15},"$ touch spec\u002Ffactories\u002Flinks.rb\n",[60,1617,1618],{"__ignoreMap":15},[63,1619,1620,1622,1625],{"class":65,"line":66},[63,1621,70],{"class":69},[63,1623,1624],{"class":73}," touch",[63,1626,1627],{"class":73}," spec\u002Ffactories\u002Flinks.rb\n",[10,1629,1630],{},"And paste this in",[54,1632,1634],{"className":118,"code":1633,"language":120,"meta":15,"style":15},"FactoryBot.define do\n  factory :link do\n    url { 'http:\u002F\u002Fwww.google.com' }\n    slug { Faker::Name.unique.first_name }\n  end\nend\n",[60,1635,1636,1648,1658,1669,1692,1696],{"__ignoreMap":15},[63,1637,1638,1641,1643,1646],{"class":65,"line":66},[63,1639,1640],{"class":83},"FactoryBot",[63,1642,115],{"class":96},[63,1644,1645],{"class":69},"define",[63,1647,140],{"class":139},[63,1649,1650,1653,1656],{"class":65,"line":143},[63,1651,1652],{"class":96},"  factory ",[63,1654,1655],{"class":83},":link",[63,1657,140],{"class":139},[63,1659,1660,1663,1666],{"class":65,"line":150},[63,1661,1662],{"class":96},"    url { ",[63,1664,1665],{"class":73},"'http:\u002F\u002Fwww.google.com'",[63,1667,1668],{"class":96}," }\n",[63,1670,1671,1674,1677,1679,1681,1683,1685,1687,1690],{"class":65,"line":164},[63,1672,1673],{"class":96},"    slug { ",[63,1675,1676],{"class":83},"Faker",[63,1678,522],{"class":96},[63,1680,711],{"class":83},[63,1682,115],{"class":96},[63,1684,716],{"class":69},[63,1686,115],{"class":96},[63,1688,1689],{"class":69},"first_name",[63,1691,1668],{"class":96},[63,1693,1694],{"class":65,"line":228},[63,1695,747],{"class":139},[63,1697,1698],{"class":65,"line":241},[63,1699,167],{"class":139},[10,1701,1702],{},"and with that, the controller specs. This is a big one, but hang tight we'll go through it.",[54,1704,1706],{"className":118,"code":1705,"language":120,"meta":15,"style":15},"# frozen_string_literal: true\n\nrequire 'rails_helper'\n\nRSpec.describe 'links API', type: :request do\n\n  # Add links\n  let!(:links) { create_list(:link, 10) }\n  let!(:link) { links.last }\n  let!(:link_id) { link.id }\n\n  # Test suite for GET \u002Flinks\n  describe 'GET \u002Flinks' do\n    # make HTTP get request before each example\n    before { get '\u002Flinks', params: {} }\n    # json method is a custom method defined in support\u002Frequests_spec_helper.rb\n    it 'returns links' do\n      expect(json).not_to be_empty\n      expect(json.size).to eq(10)\n    end\n\n    it 'returns status code 200' do\n      expect(response).to have_http_status(200)\n    end\n  end\n\n  # Test suite for GET \u002Flinks\u002F:slug\n  describe 'GET \u002Fs\u002F:slug' do\n    let(:link_slug) { link.slug }\n\n    before { get \"\u002Fs\u002F#{link_slug}\", headers: headers }\n\n    context 'when the record exists' do\n      it 'returns the link' do\n        expect(response).to redirect_to %r{\\Ahttp:\u002F\u002Fwww.google.com}\n      end\n\n      it 'returns status code 200' do\n        expect(link.reload.clicked).to eq(1)\n        # expect response to be a redirect\n        expect(response).to have_http_status(302)\n      end\n    end\n\n    context 'when the record does not exist' do\n      let(:link_slug) { '12345' }\n\n      it 'returns status code 404' do\n        expect(response).to have_http_status(404)\n      end\n\n      it 'returns a not found message' do\n        expect(response.body).to match(\u002FSorry, link not found.\u002F)\n      end\n    end\n  end\n\n  # Test suite for POST \u002Flinks\n  describe 'POST \u002Flinks' do\n    # valid payload\n    let(:valid_attributes) { { slug: 'test', url: 'www.google.com' } }\n\n    context 'when the request is valid' do\n      before { post '\u002Flinks', params: valid_attributes, headers: headers }\n\n      it 'creates a link' do\n        expect(json['slug']).to eq('test')\n        expect(json['url']).to eq('http:\u002F\u002Fwww.google.com')\n      end\n\n      it 'returns status code 201' do\n        expect(response).to have_http_status(201)\n      end\n    end\n\n    context 'when the slug is taken' do\n      let(:link) { create(:link, slug: 'test') }\n      before { post '\u002Flinks', params: valid_attributes, headers: headers }\n\n      it 'creates a link with new slug' do\n        expect(json['slug'][0..3]).to eq('test')\n        expect(json['url']).to eq('http:\u002F\u002Fwww.google.com')\n      end\n\n      it 'returns status code 200' do\n        expect(response).to have_http_status(201)\n      end\n    end\n\n    context 'when the request is invalid' do\n      let(:invalid_attributes) { { url: nil } }\n      before { post '\u002Flinks', params: invalid_attributes, headers: headers }\n\n      it 'returns status code 422' do\n        expect(response).to have_http_status(422)\n      end\n\n      it 'returns a validation failure message' do\n        expect(json['message']).to eq(['url', \"can't be blank\"])\n      end\n    end\n  end\n\n  # Test suite for PUT \u002Flinks\u002F:id\n  describe 'PUT \u002Flinks\u002F:id' do\n    let!(:valid_attributes) { { url: 'www.yahoo.com' } }\n\n    context 'when the record exists' do\n      before { put \"\u002Flinks\u002F#{link_id}\", params: valid_attributes, headers: headers }\n\n      it 'updates the record' do\n        expect(json['url']).to eq('http:\u002F\u002Fwww.yahoo.com')\n      end\n\n      it 'returns status code 204' do\n        expect(response).to have_http_status(200)\n      end\n    end\n  end\n\n  # Test suite for DELETE \u002Flinks\u002F:id\n  describe 'DELETE \u002Flinks\u002F:id' do\n    before { delete \"\u002Flinks\u002F#{link_id}\", params: {}, headers: headers }\n\n    it 'returns status code 204' do\n      expect(response).to have_http_status(200)\n    end\n  end\nend\n",[60,1707,1708,1713,1717,1723,1727,1747,1751,1756,1782,1798,1815,1819,1824,1834,1839,1855,1860,1870,1884,1909,1913,1917,1926,1945,1949,1953,1957,1962,1971,1988,1992,2013,2017,2027,2037,2060,2065,2069,2077,2105,2110,2127,2131,2135,2139,2148,2164,2168,2177,2194,2198,2202,2211,2235,2239,2243,2247,2251,2256,2265,2270,2299,2303,2312,2330,2334,2343,2367,2388,2392,2396,2405,2422,2426,2430,2435,2445,2471,2488,2493,2503,2535,2556,2561,2566,2575,2592,2597,2602,2607,2617,2636,2654,2659,2669,2687,2692,2697,2707,2736,2741,2746,2751,2756,2762,2772,2791,2796,2805,2829,2834,2844,2866,2871,2876,2886,2903,2908,2913,2918,2923,2929,2939,2962,2967,2976,2993,2998,3003],{"__ignoreMap":15},[63,1709,1710],{"class":65,"line":66},[63,1711,1712],{"class":146},"# frozen_string_literal: true\n",[63,1714,1715],{"class":65,"line":143},[63,1716,347],{"emptyLinePlaceholder":346},[63,1718,1719,1721],{"class":65,"line":150},[63,1720,324],{"class":139},[63,1722,1300],{"class":73},[63,1724,1725],{"class":65,"line":164},[63,1726,347],{"emptyLinePlaceholder":346},[63,1728,1729,1731,1733,1735,1738,1740,1742,1745],{"class":65,"line":228},[63,1730,594],{"class":83},[63,1732,115],{"class":96},[63,1734,1318],{"class":69},[63,1736,1737],{"class":73}," 'links API'",[63,1739,133],{"class":96},[63,1741,1325],{"class":83},[63,1743,1744],{"class":83}," :request",[63,1746,140],{"class":139},[63,1748,1749],{"class":65,"line":241},[63,1750,347],{"emptyLinePlaceholder":346},[63,1752,1753],{"class":65,"line":370},[63,1754,1755],{"class":146},"  # Add links\n",[63,1757,1758,1761,1763,1765,1768,1771,1773,1775,1777,1780],{"class":65,"line":375},[63,1759,1760],{"class":69},"  let!",[63,1762,388],{"class":96},[63,1764,1202],{"class":83},[63,1766,1767],{"class":96},") { ",[63,1769,1770],{"class":69},"create_list",[63,1772,388],{"class":96},[63,1774,1655],{"class":83},[63,1776,133],{"class":96},[63,1778,1779],{"class":83},"10",[63,1781,1351],{"class":96},[63,1783,1784,1786,1788,1790,1793,1796],{"class":65,"line":402},[63,1785,1760],{"class":69},[63,1787,388],{"class":96},[63,1789,1655],{"class":83},[63,1791,1792],{"class":96},") { links.",[63,1794,1795],{"class":69},"last",[63,1797,1668],{"class":96},[63,1799,1800,1802,1804,1807,1810,1813],{"class":65,"line":407},[63,1801,1760],{"class":69},[63,1803,388],{"class":96},[63,1805,1806],{"class":83},":link_id",[63,1808,1809],{"class":96},") { link.",[63,1811,1812],{"class":69},"id",[63,1814,1668],{"class":96},[63,1816,1817],{"class":65,"line":413},[63,1818,347],{"emptyLinePlaceholder":346},[63,1820,1821],{"class":65,"line":443},[63,1822,1823],{"class":146},"  # Test suite for GET \u002Flinks\n",[63,1825,1826,1829,1832],{"class":65,"line":448},[63,1827,1828],{"class":96},"  describe ",[63,1830,1831],{"class":73},"'GET \u002Flinks'",[63,1833,140],{"class":139},[63,1835,1836],{"class":65,"line":454},[63,1837,1838],{"class":146},"    # make HTTP get request before each example\n",[63,1840,1841,1844,1847,1849,1852],{"class":65,"line":499},[63,1842,1843],{"class":96},"    before { get ",[63,1845,1846],{"class":73},"'\u002Flinks'",[63,1848,133],{"class":96},[63,1850,1851],{"class":83},"params:",[63,1853,1854],{"class":96}," {} }\n",[63,1856,1857],{"class":65,"line":504},[63,1858,1859],{"class":146},"    # json method is a custom method defined in support\u002Frequests_spec_helper.rb\n",[63,1861,1862,1865,1868],{"class":65,"line":510},[63,1863,1864],{"class":96},"    it ",[63,1866,1867],{"class":73},"'returns links'",[63,1869,140],{"class":139},[63,1871,1872,1875,1878,1881],{"class":65,"line":516},[63,1873,1874],{"class":69},"      expect",[63,1876,1877],{"class":96},"(json).",[63,1879,1880],{"class":69},"not_to",[63,1882,1883],{"class":96}," be_empty\n",[63,1885,1886,1888,1891,1894,1897,1900,1903,1905,1907],{"class":65,"line":533},[63,1887,1874],{"class":69},[63,1889,1890],{"class":96},"(json.",[63,1892,1893],{"class":69},"size",[63,1895,1896],{"class":96},").",[63,1898,1899],{"class":69},"to",[63,1901,1902],{"class":69}," eq",[63,1904,388],{"class":96},[63,1906,1779],{"class":83},[63,1908,399],{"class":96},[63,1910,1911],{"class":65,"line":550},[63,1912,1100],{"class":139},[63,1914,1915],{"class":65,"line":567},[63,1916,347],{"emptyLinePlaceholder":346},[63,1918,1919,1921,1924],{"class":65,"line":576},[63,1920,1864],{"class":96},[63,1922,1923],{"class":73},"'returns status code 200'",[63,1925,140],{"class":139},[63,1927,1928,1930,1933,1935,1938,1940,1943],{"class":65,"line":581},[63,1929,1874],{"class":69},[63,1931,1932],{"class":96},"(response).",[63,1934,1899],{"class":69},[63,1936,1937],{"class":69}," have_http_status",[63,1939,388],{"class":96},[63,1941,1942],{"class":83},"200",[63,1944,399],{"class":96},[63,1946,1947],{"class":65,"line":586},[63,1948,1100],{"class":139},[63,1950,1951],{"class":65,"line":591},[63,1952,747],{"class":139},[63,1954,1955],{"class":65,"line":608},[63,1956,347],{"emptyLinePlaceholder":346},[63,1958,1959],{"class":65,"line":614},[63,1960,1961],{"class":146},"  # Test suite for GET \u002Flinks\u002F:slug\n",[63,1963,1964,1966,1969],{"class":65,"line":644},[63,1965,1828],{"class":96},[63,1967,1968],{"class":73},"'GET \u002Fs\u002F:slug'",[63,1970,140],{"class":139},[63,1972,1973,1976,1978,1981,1983,1986],{"class":65,"line":649},[63,1974,1975],{"class":69},"    let",[63,1977,388],{"class":96},[63,1979,1980],{"class":83},":link_slug",[63,1982,1809],{"class":96},[63,1984,1985],{"class":69},"slug",[63,1987,1668],{"class":96},[63,1989,1990],{"class":65,"line":662},[63,1991,347],{"emptyLinePlaceholder":346},[63,1993,1994,1996,1999,2002,2005,2007,2010],{"class":65,"line":667},[63,1995,1843],{"class":96},[63,1997,1998],{"class":73},"\"\u002Fs\u002F",[63,2000,2001],{"class":73},"#{link_slug}",[63,2003,2004],{"class":73},"\"",[63,2006,133],{"class":96},[63,2008,2009],{"class":83},"headers:",[63,2011,2012],{"class":96}," headers }\n",[63,2014,2015],{"class":65,"line":685},[63,2016,347],{"emptyLinePlaceholder":346},[63,2018,2019,2022,2025],{"class":65,"line":703},[63,2020,2021],{"class":96},"    context ",[63,2023,2024],{"class":73},"'when the record exists'",[63,2026,140],{"class":139},[63,2028,2029,2032,2035],{"class":65,"line":727},[63,2030,2031],{"class":96},"      it ",[63,2033,2034],{"class":73},"'returns the link'",[63,2036,140],{"class":139},[63,2038,2039,2042,2044,2046,2049,2053,2057],{"class":65,"line":744},[63,2040,2041],{"class":69},"        expect",[63,2043,1932],{"class":96},[63,2045,1899],{"class":69},[63,2047,2048],{"class":96}," redirect_to ",[63,2050,2052],{"class":2051},"sns5M","%r{",[63,2054,2056],{"class":2055},"sRjNt","\\A",[63,2058,2059],{"class":2051},"http:\u002F\u002Fwww.google.com}\n",[63,2061,2062],{"class":65,"line":750},[63,2063,2064],{"class":139},"      end\n",[63,2066,2067],{"class":65,"line":755},[63,2068,347],{"emptyLinePlaceholder":346},[63,2070,2071,2073,2075],{"class":65,"line":771},[63,2072,2031],{"class":96},[63,2074,1923],{"class":73},[63,2076,140],{"class":139},[63,2078,2079,2081,2084,2087,2089,2092,2094,2096,2098,2100,2103],{"class":65,"line":786},[63,2080,2041],{"class":69},[63,2082,2083],{"class":96},"(link.",[63,2085,2086],{"class":69},"reload",[63,2088,115],{"class":96},[63,2090,2091],{"class":69},"clicked",[63,2093,1896],{"class":96},[63,2095,1899],{"class":69},[63,2097,1902],{"class":69},[63,2099,388],{"class":96},[63,2101,2102],{"class":83},"1",[63,2104,399],{"class":96},[63,2106,2107],{"class":65,"line":791},[63,2108,2109],{"class":146},"        # expect response to be a redirect\n",[63,2111,2112,2114,2116,2118,2120,2122,2125],{"class":65,"line":796},[63,2113,2041],{"class":69},[63,2115,1932],{"class":96},[63,2117,1899],{"class":69},[63,2119,1937],{"class":69},[63,2121,388],{"class":96},[63,2123,2124],{"class":83},"302",[63,2126,399],{"class":96},[63,2128,2129],{"class":65,"line":819},[63,2130,2064],{"class":139},[63,2132,2133],{"class":65,"line":833},[63,2134,1100],{"class":139},[63,2136,2137],{"class":65,"line":838},[63,2138,347],{"emptyLinePlaceholder":346},[63,2140,2141,2143,2146],{"class":65,"line":843},[63,2142,2021],{"class":96},[63,2144,2145],{"class":73},"'when the record does not exist'",[63,2147,140],{"class":139},[63,2149,2150,2153,2155,2157,2159,2162],{"class":65,"line":858},[63,2151,2152],{"class":69},"      let",[63,2154,388],{"class":96},[63,2156,1980],{"class":83},[63,2158,1767],{"class":96},[63,2160,2161],{"class":73},"'12345'",[63,2163,1668],{"class":96},[63,2165,2166],{"class":65,"line":868},[63,2167,347],{"emptyLinePlaceholder":346},[63,2169,2170,2172,2175],{"class":65,"line":873},[63,2171,2031],{"class":96},[63,2173,2174],{"class":73},"'returns status code 404'",[63,2176,140],{"class":139},[63,2178,2179,2181,2183,2185,2187,2189,2192],{"class":65,"line":878},[63,2180,2041],{"class":69},[63,2182,1932],{"class":96},[63,2184,1899],{"class":69},[63,2186,1937],{"class":69},[63,2188,388],{"class":96},[63,2190,2191],{"class":83},"404",[63,2193,399],{"class":96},[63,2195,2196],{"class":65,"line":894},[63,2197,2064],{"class":139},[63,2199,2200],{"class":65,"line":904},[63,2201,347],{"emptyLinePlaceholder":346},[63,2203,2204,2206,2209],{"class":65,"line":909},[63,2205,2031],{"class":96},[63,2207,2208],{"class":73},"'returns a not found message'",[63,2210,140],{"class":139},[63,2212,2213,2215,2218,2221,2223,2225,2228,2230,2233],{"class":65,"line":914},[63,2214,2041],{"class":69},[63,2216,2217],{"class":96},"(response.",[63,2219,2220],{"class":69},"body",[63,2222,1896],{"class":96},[63,2224,1899],{"class":69},[63,2226,2227],{"class":69}," match",[63,2229,388],{"class":96},[63,2231,2232],{"class":2051},"\u002FSorry, link not found.\u002F",[63,2234,399],{"class":96},[63,2236,2237],{"class":65,"line":930},[63,2238,2064],{"class":139},[63,2240,2241],{"class":65,"line":939},[63,2242,1100],{"class":139},[63,2244,2245],{"class":65,"line":944},[63,2246,747],{"class":139},[63,2248,2249],{"class":65,"line":949},[63,2250,347],{"emptyLinePlaceholder":346},[63,2252,2253],{"class":65,"line":964},[63,2254,2255],{"class":146},"  # Test suite for POST \u002Flinks\n",[63,2257,2258,2260,2263],{"class":65,"line":973},[63,2259,1828],{"class":96},[63,2261,2262],{"class":73},"'POST \u002Flinks'",[63,2264,140],{"class":139},[63,2266,2267],{"class":65,"line":978},[63,2268,2269],{"class":146},"    # valid payload\n",[63,2271,2272,2274,2276,2279,2282,2285,2288,2290,2293,2296],{"class":65,"line":983},[63,2273,1975],{"class":69},[63,2275,388],{"class":96},[63,2277,2278],{"class":83},":valid_attributes",[63,2280,2281],{"class":96},") { { ",[63,2283,2284],{"class":83},"slug:",[63,2286,2287],{"class":73}," 'test'",[63,2289,133],{"class":96},[63,2291,2292],{"class":83},"url:",[63,2294,2295],{"class":73}," 'www.google.com'",[63,2297,2298],{"class":96}," } }\n",[63,2300,2301],{"class":65,"line":991},[63,2302,347],{"emptyLinePlaceholder":346},[63,2304,2305,2307,2310],{"class":65,"line":996},[63,2306,2021],{"class":96},[63,2308,2309],{"class":73},"'when the request is valid'",[63,2311,140],{"class":139},[63,2313,2314,2317,2319,2321,2323,2326,2328],{"class":65,"line":1002},[63,2315,2316],{"class":96},"      before { post ",[63,2318,1846],{"class":73},[63,2320,133],{"class":96},[63,2322,1851],{"class":83},[63,2324,2325],{"class":96}," valid_attributes, ",[63,2327,2009],{"class":83},[63,2329,2012],{"class":96},[63,2331,2332],{"class":65,"line":1010},[63,2333,347],{"emptyLinePlaceholder":346},[63,2335,2336,2338,2341],{"class":65,"line":1015},[63,2337,2031],{"class":96},[63,2339,2340],{"class":73},"'creates a link'",[63,2342,140],{"class":139},[63,2344,2345,2347,2350,2353,2356,2358,2360,2362,2365],{"class":65,"line":1036},[63,2346,2041],{"class":69},[63,2348,2349],{"class":96},"(json[",[63,2351,2352],{"class":73},"'slug'",[63,2354,2355],{"class":96},"]).",[63,2357,1899],{"class":69},[63,2359,1902],{"class":69},[63,2361,388],{"class":96},[63,2363,2364],{"class":73},"'test'",[63,2366,399],{"class":96},[63,2368,2369,2371,2373,2376,2378,2380,2382,2384,2386],{"class":65,"line":1041},[63,2370,2041],{"class":69},[63,2372,2349],{"class":96},[63,2374,2375],{"class":73},"'url'",[63,2377,2355],{"class":96},[63,2379,1899],{"class":69},[63,2381,1902],{"class":69},[63,2383,388],{"class":96},[63,2385,1665],{"class":73},[63,2387,399],{"class":96},[63,2389,2390],{"class":65,"line":1060},[63,2391,2064],{"class":139},[63,2393,2394],{"class":65,"line":1074},[63,2395,347],{"emptyLinePlaceholder":346},[63,2397,2398,2400,2403],{"class":65,"line":1086},[63,2399,2031],{"class":96},[63,2401,2402],{"class":73},"'returns status code 201'",[63,2404,140],{"class":139},[63,2406,2407,2409,2411,2413,2415,2417,2420],{"class":65,"line":1097},[63,2408,2041],{"class":69},[63,2410,1932],{"class":96},[63,2412,1899],{"class":69},[63,2414,1937],{"class":69},[63,2416,388],{"class":96},[63,2418,2419],{"class":83},"201",[63,2421,399],{"class":96},[63,2423,2424],{"class":65,"line":1103},[63,2425,2064],{"class":139},[63,2427,2428],{"class":65,"line":1108},[63,2429,1100],{"class":139},[63,2431,2433],{"class":65,"line":2432},75,[63,2434,347],{"emptyLinePlaceholder":346},[63,2436,2438,2440,2443],{"class":65,"line":2437},76,[63,2439,2021],{"class":96},[63,2441,2442],{"class":73},"'when the slug is taken'",[63,2444,140],{"class":139},[63,2446,2448,2450,2452,2454,2456,2459,2461,2463,2465,2467,2469],{"class":65,"line":2447},77,[63,2449,2152],{"class":69},[63,2451,388],{"class":96},[63,2453,1655],{"class":83},[63,2455,1767],{"class":96},[63,2457,2458],{"class":69},"create",[63,2460,388],{"class":96},[63,2462,1655],{"class":83},[63,2464,133],{"class":96},[63,2466,2284],{"class":83},[63,2468,2287],{"class":73},[63,2470,1351],{"class":96},[63,2472,2474,2476,2478,2480,2482,2484,2486],{"class":65,"line":2473},78,[63,2475,2316],{"class":96},[63,2477,1846],{"class":73},[63,2479,133],{"class":96},[63,2481,1851],{"class":83},[63,2483,2325],{"class":96},[63,2485,2009],{"class":83},[63,2487,2012],{"class":96},[63,2489,2491],{"class":65,"line":2490},79,[63,2492,347],{"emptyLinePlaceholder":346},[63,2494,2496,2498,2501],{"class":65,"line":2495},80,[63,2497,2031],{"class":96},[63,2499,2500],{"class":73},"'creates a link with new slug'",[63,2502,140],{"class":139},[63,2504,2506,2508,2510,2512,2515,2518,2520,2523,2525,2527,2529,2531,2533],{"class":65,"line":2505},81,[63,2507,2041],{"class":69},[63,2509,2349],{"class":96},[63,2511,2352],{"class":73},[63,2513,2514],{"class":96},"][",[63,2516,2517],{"class":83},"0",[63,2519,1508],{"class":96},[63,2521,2522],{"class":83},"3",[63,2524,2355],{"class":96},[63,2526,1899],{"class":69},[63,2528,1902],{"class":69},[63,2530,388],{"class":96},[63,2532,2364],{"class":73},[63,2534,399],{"class":96},[63,2536,2538,2540,2542,2544,2546,2548,2550,2552,2554],{"class":65,"line":2537},82,[63,2539,2041],{"class":69},[63,2541,2349],{"class":96},[63,2543,2375],{"class":73},[63,2545,2355],{"class":96},[63,2547,1899],{"class":69},[63,2549,1902],{"class":69},[63,2551,388],{"class":96},[63,2553,1665],{"class":73},[63,2555,399],{"class":96},[63,2557,2559],{"class":65,"line":2558},83,[63,2560,2064],{"class":139},[63,2562,2564],{"class":65,"line":2563},84,[63,2565,347],{"emptyLinePlaceholder":346},[63,2567,2569,2571,2573],{"class":65,"line":2568},85,[63,2570,2031],{"class":96},[63,2572,1923],{"class":73},[63,2574,140],{"class":139},[63,2576,2578,2580,2582,2584,2586,2588,2590],{"class":65,"line":2577},86,[63,2579,2041],{"class":69},[63,2581,1932],{"class":96},[63,2583,1899],{"class":69},[63,2585,1937],{"class":69},[63,2587,388],{"class":96},[63,2589,2419],{"class":83},[63,2591,399],{"class":96},[63,2593,2595],{"class":65,"line":2594},87,[63,2596,2064],{"class":139},[63,2598,2600],{"class":65,"line":2599},88,[63,2601,1100],{"class":139},[63,2603,2605],{"class":65,"line":2604},89,[63,2606,347],{"emptyLinePlaceholder":346},[63,2608,2610,2612,2615],{"class":65,"line":2609},90,[63,2611,2021],{"class":96},[63,2613,2614],{"class":73},"'when the request is invalid'",[63,2616,140],{"class":139},[63,2618,2620,2622,2624,2627,2629,2631,2634],{"class":65,"line":2619},91,[63,2621,2152],{"class":69},[63,2623,388],{"class":96},[63,2625,2626],{"class":83},":invalid_attributes",[63,2628,2281],{"class":96},[63,2630,2292],{"class":83},[63,2632,2633],{"class":83}," nil",[63,2635,2298],{"class":96},[63,2637,2639,2641,2643,2645,2647,2650,2652],{"class":65,"line":2638},92,[63,2640,2316],{"class":96},[63,2642,1846],{"class":73},[63,2644,133],{"class":96},[63,2646,1851],{"class":83},[63,2648,2649],{"class":96}," invalid_attributes, ",[63,2651,2009],{"class":83},[63,2653,2012],{"class":96},[63,2655,2657],{"class":65,"line":2656},93,[63,2658,347],{"emptyLinePlaceholder":346},[63,2660,2662,2664,2667],{"class":65,"line":2661},94,[63,2663,2031],{"class":96},[63,2665,2666],{"class":73},"'returns status code 422'",[63,2668,140],{"class":139},[63,2670,2672,2674,2676,2678,2680,2682,2685],{"class":65,"line":2671},95,[63,2673,2041],{"class":69},[63,2675,1932],{"class":96},[63,2677,1899],{"class":69},[63,2679,1937],{"class":69},[63,2681,388],{"class":96},[63,2683,2684],{"class":83},"422",[63,2686,399],{"class":96},[63,2688,2690],{"class":65,"line":2689},96,[63,2691,2064],{"class":139},[63,2693,2695],{"class":65,"line":2694},97,[63,2696,347],{"emptyLinePlaceholder":346},[63,2698,2700,2702,2705],{"class":65,"line":2699},98,[63,2701,2031],{"class":96},[63,2703,2704],{"class":73},"'returns a validation failure message'",[63,2706,140],{"class":139},[63,2708,2710,2712,2714,2717,2719,2721,2723,2726,2728,2730,2733],{"class":65,"line":2709},99,[63,2711,2041],{"class":69},[63,2713,2349],{"class":96},[63,2715,2716],{"class":73},"'message'",[63,2718,2355],{"class":96},[63,2720,1899],{"class":69},[63,2722,1902],{"class":69},[63,2724,2725],{"class":96},"([",[63,2727,2375],{"class":73},[63,2729,133],{"class":96},[63,2731,2732],{"class":73},"\"can't be blank\"",[63,2734,2735],{"class":96},"])\n",[63,2737,2739],{"class":65,"line":2738},100,[63,2740,2064],{"class":139},[63,2742,2744],{"class":65,"line":2743},101,[63,2745,1100],{"class":139},[63,2747,2749],{"class":65,"line":2748},102,[63,2750,747],{"class":139},[63,2752,2754],{"class":65,"line":2753},103,[63,2755,347],{"emptyLinePlaceholder":346},[63,2757,2759],{"class":65,"line":2758},104,[63,2760,2761],{"class":146},"  # Test suite for PUT \u002Flinks\u002F:id\n",[63,2763,2765,2767,2770],{"class":65,"line":2764},105,[63,2766,1828],{"class":96},[63,2768,2769],{"class":73},"'PUT \u002Flinks\u002F:id'",[63,2771,140],{"class":139},[63,2773,2775,2778,2780,2782,2784,2786,2789],{"class":65,"line":2774},106,[63,2776,2777],{"class":69},"    let!",[63,2779,388],{"class":96},[63,2781,2278],{"class":83},[63,2783,2281],{"class":96},[63,2785,2292],{"class":83},[63,2787,2788],{"class":73}," 'www.yahoo.com'",[63,2790,2298],{"class":96},[63,2792,2794],{"class":65,"line":2793},107,[63,2795,347],{"emptyLinePlaceholder":346},[63,2797,2799,2801,2803],{"class":65,"line":2798},108,[63,2800,2021],{"class":96},[63,2802,2024],{"class":73},[63,2804,140],{"class":139},[63,2806,2808,2811,2814,2817,2819,2821,2823,2825,2827],{"class":65,"line":2807},109,[63,2809,2810],{"class":96},"      before { put ",[63,2812,2813],{"class":73},"\"\u002Flinks\u002F",[63,2815,2816],{"class":73},"#{link_id}",[63,2818,2004],{"class":73},[63,2820,133],{"class":96},[63,2822,1851],{"class":83},[63,2824,2325],{"class":96},[63,2826,2009],{"class":83},[63,2828,2012],{"class":96},[63,2830,2832],{"class":65,"line":2831},110,[63,2833,347],{"emptyLinePlaceholder":346},[63,2835,2837,2839,2842],{"class":65,"line":2836},111,[63,2838,2031],{"class":96},[63,2840,2841],{"class":73},"'updates the record'",[63,2843,140],{"class":139},[63,2845,2847,2849,2851,2853,2855,2857,2859,2861,2864],{"class":65,"line":2846},112,[63,2848,2041],{"class":69},[63,2850,2349],{"class":96},[63,2852,2375],{"class":73},[63,2854,2355],{"class":96},[63,2856,1899],{"class":69},[63,2858,1902],{"class":69},[63,2860,388],{"class":96},[63,2862,2863],{"class":73},"'http:\u002F\u002Fwww.yahoo.com'",[63,2865,399],{"class":96},[63,2867,2869],{"class":65,"line":2868},113,[63,2870,2064],{"class":139},[63,2872,2874],{"class":65,"line":2873},114,[63,2875,347],{"emptyLinePlaceholder":346},[63,2877,2879,2881,2884],{"class":65,"line":2878},115,[63,2880,2031],{"class":96},[63,2882,2883],{"class":73},"'returns status code 204'",[63,2885,140],{"class":139},[63,2887,2889,2891,2893,2895,2897,2899,2901],{"class":65,"line":2888},116,[63,2890,2041],{"class":69},[63,2892,1932],{"class":96},[63,2894,1899],{"class":69},[63,2896,1937],{"class":69},[63,2898,388],{"class":96},[63,2900,1942],{"class":83},[63,2902,399],{"class":96},[63,2904,2906],{"class":65,"line":2905},117,[63,2907,2064],{"class":139},[63,2909,2911],{"class":65,"line":2910},118,[63,2912,1100],{"class":139},[63,2914,2916],{"class":65,"line":2915},119,[63,2917,747],{"class":139},[63,2919,2921],{"class":65,"line":2920},120,[63,2922,347],{"emptyLinePlaceholder":346},[63,2924,2926],{"class":65,"line":2925},121,[63,2927,2928],{"class":146},"  # Test suite for DELETE \u002Flinks\u002F:id\n",[63,2930,2932,2934,2937],{"class":65,"line":2931},122,[63,2933,1828],{"class":96},[63,2935,2936],{"class":73},"'DELETE \u002Flinks\u002F:id'",[63,2938,140],{"class":139},[63,2940,2942,2945,2947,2949,2951,2953,2955,2958,2960],{"class":65,"line":2941},123,[63,2943,2944],{"class":96},"    before { delete ",[63,2946,2813],{"class":73},[63,2948,2816],{"class":73},[63,2950,2004],{"class":73},[63,2952,133],{"class":96},[63,2954,1851],{"class":83},[63,2956,2957],{"class":96}," {}, ",[63,2959,2009],{"class":83},[63,2961,2012],{"class":96},[63,2963,2965],{"class":65,"line":2964},124,[63,2966,347],{"emptyLinePlaceholder":346},[63,2968,2970,2972,2974],{"class":65,"line":2969},125,[63,2971,1864],{"class":96},[63,2973,2883],{"class":73},[63,2975,140],{"class":139},[63,2977,2979,2981,2983,2985,2987,2989,2991],{"class":65,"line":2978},126,[63,2980,1874],{"class":69},[63,2982,1932],{"class":96},[63,2984,1899],{"class":69},[63,2986,1937],{"class":69},[63,2988,388],{"class":96},[63,2990,1942],{"class":83},[63,2992,399],{"class":96},[63,2994,2996],{"class":65,"line":2995},127,[63,2997,1100],{"class":139},[63,2999,3001],{"class":65,"line":3000},128,[63,3002,747],{"class":139},[63,3004,3006],{"class":65,"line":3005},129,[63,3007,167],{"class":139},[10,3009,3010,3011,3014,3015,115],{},"We start by populating the database with a list of 10 link records (thanks to factory bot).\nWe also have a custom helper method ",[60,3012,3013],{},"json"," which parses the JSON response to a Ruby Hash which is easier to work with in our tests.\nLet’s define it in ",[60,3016,3017],{},"spec\u002Fsupport\u002Frequest_spec_helper",[10,3019,3020],{},"Add the directory and file:",[54,3022,3024],{"className":56,"code":3023,"language":58,"meta":15,"style":15},"$ mkdir spec\u002Fsupport && touch spec\u002Fsupport\u002Frequest_spec_helper.rb\n",[60,3025,3026],{"__ignoreMap":15},[63,3027,3028,3030,3032,3035,3037,3040],{"class":65,"line":66},[63,3029,70],{"class":69},[63,3031,292],{"class":73},[63,3033,3034],{"class":73}," spec\u002Fsupport",[63,3036,97],{"class":96},[63,3038,3039],{"class":69},"touch",[63,3041,3042],{"class":73}," spec\u002Fsupport\u002Frequest_spec_helper.rb\n",[54,3044,3046],{"className":118,"code":3045,"language":120,"meta":15,"style":15},"# frozen_string_literal: true\n\nmodule RequestSpecHelper\n  # Parse JSON response to ruby hash\n  def json\n    JSON.parse(response.body)\n  end\nend\n\n",[60,3047,3048,3052,3056,3064,3069,3076,3092,3096],{"__ignoreMap":15},[63,3049,3050],{"class":65,"line":66},[63,3051,1712],{"class":146},[63,3053,3054],{"class":65,"line":143},[63,3055,347],{"emptyLinePlaceholder":346},[63,3057,3058,3061],{"class":65,"line":150},[63,3059,3060],{"class":139},"module",[63,3062,3063],{"class":69}," RequestSpecHelper\n",[63,3065,3066],{"class":65,"line":164},[63,3067,3068],{"class":146},"  # Parse JSON response to ruby hash\n",[63,3070,3071,3073],{"class":65,"line":228},[63,3072,1191],{"class":139},[63,3074,3075],{"class":69}," json\n",[63,3077,3078,3081,3083,3086,3088,3090],{"class":65,"line":241},[63,3079,3080],{"class":83},"    JSON",[63,3082,115],{"class":96},[63,3084,3085],{"class":69},"parse",[63,3087,2217],{"class":96},[63,3089,2220],{"class":69},[63,3091,399],{"class":96},[63,3093,3094],{"class":65,"line":370},[63,3095,747],{"class":139},[63,3097,3098],{"class":65,"line":375},[63,3099,167],{"class":139},[10,3101,3102],{},"The support directory is not auto-loaded by default. To enable this, open the rails helper and comment out the support directory auto-loading and then\ninclude it as shared module for all request specs in the RSpec configuration block.",[10,3104,3105],{},"Run the tests and you'll see a sea of red. But that's ok because we're going to start writing code that actually does stuff.",[10,3107,3108],{},[13,3109],{"alt":15,"src":3110},"https:\u002F\u002Fmedia.giphy.com\u002Fmedia\u002Fbg1MQ6IUVoVOM\u002Fsource.gif",[297,3112],{},[300,3114,3116],{"id":3115},"conclusion","Conclusion",[10,3118,3119,3120,3124],{},"So a quick recap, we've created a ruby on rails api, created the models, and of course setup our test suite. In ",[24,3121,3123],{"href":3122},"\u002F2021\u002F01\u002F14\u002Flink-shortener-part-2","part 2"," we'll create the routes, version our api and start creating links!",[3126,3127,3128],"style",{},"html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .sns5M, html code.shiki .sns5M{--shiki-default:#DBEDFF}html pre.shiki code .sRjNt, html code.shiki .sRjNt{--shiki-default:#85E89D;--shiki-default-font-weight:bold}",{"title":15,"searchDepth":143,"depth":143,"links":3130},[],"2021-01-13 23:21","md",{},"\u002F2021\u002F01\u002F13\u002Flink-shortener-part-1",{"title":5,"description":15},"2021\u002F01\u002F13\u002Flink-shortener-part-1","Whether you're a person with a long link, or a down and out Nigerian prince. This is the blog post for you","d5h5-HnerDt9h8flIVIAyaIM-I4_165ObAx7OUCww1c",1791634646002]