{"id":3839,"date":"2015-04-26T15:54:23","date_gmt":"2015-04-26T19:54:23","guid":{"rendered":"http:\/\/webninjataylor.com\/library\/?p=3839"},"modified":"2015-05-05T18:11:12","modified_gmt":"2015-05-05T22:11:12","slug":"ruby-on-rails-suggested-project-flow","status":"publish","type":"post","link":"https:\/\/webninjataylor.com\/library\/ruby-on-rails-suggested-project-flow\/","title":{"rendered":"Ruby on Rails: Suggested Project Flow"},"content":{"rendered":"<ol>\n<li>\n<h4>Sketch Use Cases, Page Flow, &amp; Basic Data<\/h4>\n<ul>\n<li>Identify basic <strong>use cases,\u00a0<\/strong>sketch <strong>page flow<\/strong> on paper (or simple drawing app), and identify <strong>basic data<\/strong> (e.g. product which will have a name, description, image, and price)\n<ul>\n<li>Ambiguity is ok at this stage since Ruby on Rails is great for agile, iterative development<\/li>\n<\/ul>\n<\/li>\n<li>Pick a part of the app to start developing which\u00a0makes the most sense\u00a0as a starting point (e.g. the web interface for maintaining\u00a0product information)\n<ul>\n<li>Develop in small iterations which involve multiple steps<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<h4>Create a New App<\/h4>\n<ul>\n<li>run <span class=\"code\">rails new <strong>appname<\/strong><\/span><\/li>\n<li>Open in Sublime Text 2 and add the following to the .gitignore\n<pre># Ignore Sublime project and workspace files\r\n*.sublime-*<\/pre>\n<\/li>\n<li>Create empty repo on GitHub<\/li>\n<li><span class=\"code\">cd<\/span> into project and run the following\n<pre>git init\r\ngit remote add origin git@github.com:<strong>username<\/strong>\/<strong>appname<\/strong>.git\r\ngit add .\r\ngit commit\r\ngit push origin master<\/pre>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<h4>Create the\u00a0Scaffold<\/h4>\n<ul>\n<li>The scaffold is\u00a0the model, views, controller, and migration for a table<\/li>\n<li>In Rails, a model is automatically mapped to a database table whose name is the plural form of the model\u2019s class<\/li>\n<li><span class=\"code\">rails generate scaffold <strong>Product title:string description:text image_url:string price:decimal<\/strong><\/span>\n<ul>\n<li>You can reverse a generation with <span class=\"code\">rails d scaffold Product<\/span><\/li>\n<\/ul>\n<\/li>\n<li>Run\u00a0<span class=\"code\">rake db:migrate<\/span> to get Rails to <strong>apply\u00a0the migration<\/strong>\u00a0to the development database\n<ul>\n<li>A migration represents a change we want to make to the data, expressed in a source file in database-independent terms<\/li>\n<li>If you edit the migration (a.k.a. modify the schema)(e.g. 20150426201937_create_products.rb), run\u00a0<span class=\"code\">rake db:migrate<\/span>\u00a0again<\/li>\n<\/ul>\n<\/li>\n<li>Note: At this point you can start the server with <span class=\"code\">bin\/rails server<\/span> and navigate to the page created with the controller name used in the scaffold generation (e.g. from step two it was Product, so now you can see it at\u00a0http:\/\/localhost:3000\/products)<\/li>\n<li>Database fun&#8230;\n<ul>\n<li><span class=\"code\">rake db:rollback<\/span> &#8230; to rollback the migration<\/li>\n<li><span class=\"code\">rake db:migrate<\/span> &#8230; to redo the migration<\/li>\n<li><span class=\"code\">rake db:seed<\/span> &#8230; to reseed the database<\/li>\n<\/ul>\n<\/li>\n<li>Run <strong><span class=\"code\">rake test<\/span><\/strong> to run the tests<\/li>\n<\/ul>\n<\/li>\n<li>\n<h4>Populate\u00a0Test Data<\/h4>\n<ul>\n<li>Modify seeds.rb with test data\n<ul>\n<li>seeds.rb is used for populating a table by running the <span class=\"code\">create!()<\/span> method on\u00a0the model with the same name\n<ul>\n<li>In general, methods that end in <span class=\"code\">!<\/span> (Ruby&#8217;s\u00a0&#8220;dangerous methods&#8221;) indicate that the method will modify the object&#8217;s state<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li>You can use\u00a0<span class=\"code\">%{\u2026}<\/span> in place of <span class=\"code\">&#8220;&#8230;&#8221;<\/span> for long strings<\/li>\n<li>Run\u00a0<strong><span class=\"code\">rake db:seed<\/span><\/strong> to <strong>populate the development database<\/strong>\n<ul>\n<li>If your seeds.rb starts with (for example)\u00a0<span class=\"code\"><strong>Product<\/strong>.delete_all<\/span>\u00a0ANY and ALL data you may have in that table\/class will be replaced<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<h4>Edit Styles<\/h4>\n<ul>\n<li>app\/views\/layouts\/application.html.erb is the main layout file<\/li>\n<li>Adding\u00a0<span class=\"code\">class='&lt;%= controller.controller_name %&gt;&#8217;<\/span> to the body tag of the main layout file will dynamically set a CSS class for each controller<\/li>\n<li>Some useful helper methods for views&#8230;\n<ul>\n<li>Alternate class names in a table with\u00a0<span class=\"code\">class=&#8221;&lt;%= <strong>cycle<\/strong>(&#8216;list_line_odd&#8217;, &#8216;list_line_even&#8217;) %&gt;&#8221;<\/span><\/li>\n<li>Remove HTML markup from\u00a0descriptions and truncate them to n characters &#8230;\n<ul>\n<li><span class=\"code\">&lt;%= <strong>truncate<\/strong>(<strong>strip_tags<\/strong>(product.description), length: 80) %&gt;<\/span><\/li>\n<\/ul>\n<\/li>\n<li>Set a browser alert to confirm data deletion &#8230;\u00a0<span class=\"code\">&lt;%= link_to &#8216;Destroy&#8217;, product, method: :delete,\u00a0<strong>data: { confirm: &#8216;Are you sure?&#8217; }<\/strong> %&gt;<\/span>\n<ul>\n<li>The <span class=\"code\">method: :delete<\/span>\u00a0parameter is used to indicate that an HTTP DELETE method should be used for this hyperlink, routing the request to the proper action in the controller<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<h4>Validation and Unit Testing<\/h4>\n<ul>\n<li>Add <strong>validations<\/strong> to the model (the gatekeeper to the database) and test data to the test controller\n<ul>\n<li>e.g. validate that certain fields are not empty &#8230;\u00a0<span class=\"code\">validates :title, :description, :image_url, presence: true<\/span><\/li>\n<li>e.g. validate a price of at least one penny &#8230;\u00a0<span class=\"code\">validates :price, numericality: {greater_than_or_equal_to: 0.01}<\/span><\/li>\n<li>e.g. validate uniqueness &#8230;\u00a0<span class=\"code\">validates :title, uniqueness: true<\/span><\/li>\n<li>e.g. validate against a regex &#8230;\n<pre>validates :image_url, allow_blank: true, format: {\r\n  with: %r{\\.(gif|jpg|png)\\Z}i,\r\n  message: 'must be a URL for GIF, JPG or PNG image.'\r\n}<\/pre>\n<\/li>\n<li>Running <span class=\"code\">rake test<\/span> now will throw errors, so you need to add test data to the test controller (e.g.\u00a0\/test\/controllers\/products_controller_test.rb)\n<ul>\n<li><span class=\"code\">rake test<\/span> runs against the dummy data in the controller test<\/li>\n<li>Add test data to the <span class=\"code\">setup<\/span> method with references to it in the create and update tests<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li><strong>Unit tests<\/strong> are stored in test\/models\n<ul>\n<li>Use assertions for pass\/fail (via the\u00a0<span class=\"code\">assert<\/span>\u00a0method) to tell the framework what we expect to be true<\/li>\n<li>We can use the model\u2019s <span class=\"code\">errors()<\/span> and <span class=\"code\">invalid?()<\/span> methods to see whether it validates, and we can use the <span class=\"code\">any?()<\/span> method of the error list to see whether there is an error associated with a particular attribute<\/li>\n<li>Run unit testing with\u00a0<span class=\"code\">rake test:models<\/span> &#8230; runs tests from the test model against the validations in the &#8220;real&#8221; model<\/li>\n<li>Unit test with dummy data in the test model&#8230;\n<pre>test \"product price must be positive\" do\r\n  product = Product.new(title: \"My Book Title\", description: \"yyy\", image_url: \"zzz.jpg\")\r\n  product.price = -1\r\n  assert product.invalid?\r\n  assert_equal [\"must be greater than or equal to 0.01\"], product.errors[:price]\r\n  product.price = 0\r\n  assert product.invalid?\r\n  assert_equal [\"must be greater than or equal to 0.01\"], product.errors[:price]\r\n  product.price = 1\r\n  assert product.valid?\r\nend<\/pre>\n<\/li>\n<\/ul>\n<\/li>\n<li>In the world of testing, a <strong>fixture<\/strong> is an environment in which you can run a test\n<ul>\n<li>You specify fixture data in files in the test\/fixtures directory in YAML data object files<\/li>\n<li>Each fixture file contains the data for a single model<\/li>\n<li>Indentation and order matters<\/li>\n<li>Specify the fixture in the test model<\/li>\n<li>Association-based stories are key to remembering large worlds of fixtures with ease &#8230; e.g.\u00a0<span class=\"code\">fred<\/span> is paying for his <span class=\"code\">christmas_order<\/span> with his <span class=\"code\">invalid_credit_card<\/span> first, then paying with his <span class=\"code\">valid_credit_card<\/span>, and finally choosing to ship it all off to <span class=\"code\">aunt_mary<\/span><\/li>\n<li>Fixtures put data into the test database (not development)\n<ul>\n<li>Each test method gets a freshly initialized table in the test database, loaded from the fixtures<\/li>\n<li>Automatically done by the <span class=\"code\">rake test<\/span> command but can be done separately by running <span class=\"code\">rake db:test:prepare<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<h4>Views and Layouts<\/h4>\n<ul>\n<li>Create additional controllers as needed (e.g.\u00a0<span class=\"code\">rails generate controller <strong>Store index<\/strong><\/span> &#8230; or &#8230; <span class=\"code\">rails generate controller <strong>Say hello goodbye<\/strong><\/span>)<\/li>\n<li>You can change the root of your site in routes.rb (e.g.\u00a0<span class=\"code\">root &#8216;store#index&#8217;, as: &#8216;store&#8217;<\/span> &#8230; the as: &#8216;store&#8217;\u00a0tells Rails to create a store_path accessor method)<\/li>\n<li>Reference data in the controllers &#8230;\n<pre>def index\r\n  @products = Product.order(:title)\r\nend<\/pre>\n<p>&#8230; and output in the views &#8230;<\/p>\n<pre>&lt;% @products.each do |product| %&gt;\r\n  &lt;%= product.title %&gt;\r\n&lt;% end %&gt;<\/pre>\n<\/li>\n<li>Spit out an image in a view &#8230; e.g.\u00a0<span class=\"code\">&lt;%= image_tag(product.image_url) %&gt;<\/span><\/li>\n<li>Use\u00a0the <span class=\"code\">require_tree<\/span> directive in the SCSS to\u00a0automatically include all stylesheets available in this directory and in any subdirectory<\/li>\n<li>There&#8217;s a\u00a0<span class=\"code\">number_to_currency()<\/span> helper method (e.g. turn 36.0 into $36.00)<\/li>\n<\/ul>\n<\/li>\n<li>\n<h4>Functional Tests<\/h4>\n<ul>\n<li>Create HTML\/CSS element tests in controller &#8230; for example&#8230;\n<pre>test \"should get index\" do\r\n  get :index\r\n  assert_response :success\r\n  assert_select '#columns #side a', minimum: 4\r\n  assert_select '#main .entry', 3\r\n  assert_select 'h3', 'Programming Ruby 1.9'\r\n  assert_select '.price', \/\\$[,\\d]+\\.\\d\\d\/\r\nend<\/pre>\n<\/li>\n<li>Both validation and functional tests will test the behavior of controllers\u00a0only; they will not retroactively affect any objects that already exist in the database or in fixtures<\/li>\n<li>run just functional tests with\u00a0<span class=\"code\">rake test:controllers<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<h2>Resources<\/h2>\n<ul>\n<li>Agile Web Development with Rails 4 (course book)<\/li>\n<li><a href=\"http:\/\/www.korenlc.com\/rails-generate-model-vs-resourse-vs-scaffold\/\" target=\"_blank\">Rails Generate Differences<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Sketch Use Cases, Page Flow, &amp; Basic Data Identify basic use cases,\u00a0sketch page flow on paper (or simple drawing app), and identify basic data (e.g. product which will have a name, description, image, and price) Ambiguity is ok at this stage since Ruby on Rails is great for agile, iterative development Pick a part of [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[144],"tags":[152],"class_list":["post-3839","post","type-post","status-publish","format-standard","hentry","category-web-shots","tag-ruby"],"_links":{"self":[{"href":"https:\/\/webninjataylor.com\/library\/wp-json\/wp\/v2\/posts\/3839","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/webninjataylor.com\/library\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/webninjataylor.com\/library\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/webninjataylor.com\/library\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/webninjataylor.com\/library\/wp-json\/wp\/v2\/comments?post=3839"}],"version-history":[{"count":66,"href":"https:\/\/webninjataylor.com\/library\/wp-json\/wp\/v2\/posts\/3839\/revisions"}],"predecessor-version":[{"id":3950,"href":"https:\/\/webninjataylor.com\/library\/wp-json\/wp\/v2\/posts\/3839\/revisions\/3950"}],"wp:attachment":[{"href":"https:\/\/webninjataylor.com\/library\/wp-json\/wp\/v2\/media?parent=3839"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/webninjataylor.com\/library\/wp-json\/wp\/v2\/categories?post=3839"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/webninjataylor.com\/library\/wp-json\/wp\/v2\/tags?post=3839"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}