Rails, AngularJS (1.5.8), and Webpack — Part 4

Okay. We left off last time actually seeing Angular working with webpack. Things were looking good – in a very basic “Hello World” kind of way. Today, we’re going to create a controller and a component to make an http request to hit up the backend for some data. First, let’s create some data.

Seeding the Database

Heading back to Rails land briefly to create some users and meals. In app/db/seeds.db let’s make a couple of arrays and then use a looping method to create our Users and Meals at the same time.

# app/db/seeds.rb

users = ["Luke", "Han", "Leia", "Obi", "Chewbacca", "C3P0"]
# Our users array from a galaxy from far far away

meals = ["Tattooine Tacos", "Chewie's Chowder", "Princess Pizza", "Darth's Death Stack", "Brain du Jar Jar", "R2's Nuts and Bolts"]
# Our meals array with some interesting looking delicacies

i = 0

6.times do
  UserMeal.create(user_id: User.create(name: users[i]).id, meal_id: 
  Meal.create(title: meals[i]).id)
  i += 1
end

Here we are using our two arrays and a loop to create not only a new User and Meal each go round, but the association between each one too. Now we can run ‘rails db:seed’ in our terminal and our database should be seeded. If we go into our rails console (‘rails c’ in the terminal), and type ‘User.first.meals’, we should see something like this:

=> #<ActiveRecord::Associations::CollectionProxy [#]>

Cool! Our seeding experiment worked and our associations seem to be solid. Let’s take it one step further and get the actual name of our first user’s meal – ‘User.first.meals.first.title’

=> “Tattooine Tacos”

Sweet! We are finished with the backend for now. Let’s get this front end party going!

Creating a Controller

First we’ll create our controller and use Angular’s built in $http service to get data from the backend. We have to use ngInject to inject the $http service into the controller. Then we’ll use $onInit to set up the call to the api. In app/javascript/packs, we’ll create a new file called ‘home.controller.js’. Then we’ll add our controller and export it, so it can be imported wherever we want it.

// app/javascript/packs/home.controller.js

class HomeController {
  constructor($http) {
    'ngInject';
    this.$http = $http;
  }

  $onInit = () => {
    this.$http({
      method: 'GET',
      url: '/users.json'
    })
    .then(res => {
      console.log(res.data)
      this.users = res.data
    })
    .catch(err => {
      console.log(err)
    })
  }
}

export default HomeController;

$onInit is a lifecycle hook that initializes on start up. So our $http call will happen when the HomeController is called upon. The $http call takes one argument, a configuration object consisting of a ‘GET’ method and a url, to make the HTTP request and returns a promise. For a successful promise, we use ‘then’. For an unsuccessful promise, we use ‘catch’. I believe the old standard was ‘success’ and ‘error’ to handle promises.

Now we need to output be able to output our data into a template. Stand alone templates are not used with modules. Instead, we’ll use a simple component.

Creating a Component

Let’s create a new file called ‘home.component.js’ in the same folder as our other Angular files. Then we have to import the HomeController and create the component. Components are very simple, as we’ll see. They basically point to a variable, which we’ll then export. Our component will take three properties: controller, controllerAs, and template. Components could take more properties, but we won’t need anything more for our needs. Our users will be coming to us in an array, so we’ll have to loop over this array to get each user. Angular comes with a very handy ng-repeat, which can be inserted right into the html.

// app/javascript/home.component.js

import HomeController from './home.controller';

const HomeComponent = {
  controller: HomeController,
  controllerAs: '$ctrl',
  template: `<div ng-repeat="user in $ctrl.users">
              <h2>{{user}}</h2>
            </div>`
}

export default HomeComponent;

This should give us what we need. Now we have to import our component to our main app module and we should see something in our browser.

Importing Component into Main App Module

Let’s head over to our meals module and see what we can do. In app/javascript/packs/application.js, we’ll import the home component under our angular and uirouter imports. Then we’ll add our component to the meals module so we can use it in the router.

//app/javascript/application.js

import angular from 'angular';
import uirouter from 'angular-ui-router';
import HomeComponent from './home.component';  // New


const meals = angular.module('meals', [uirouter])
  .component('homeComponent', HomeComponent) // New
  .config(($stateProvider) => {
    $stateProvider
      .state('home', {
        url: '/',
        component: 'homeComponent' // New

      })
  });

export default meals;

I added a ‘New’ comment next to the lines that were added. Notice that we still had to add the component to the module. Then we used the stringified name and added it to our ‘home’ route as a component. Let’s run the servers and see if we have anything.

Remember, we need to run two servers – one for the front end and one for the back. In one tab in your terminal run ‘./bin/webpack-dev-server’ and in another tab, run ‘rails s’. Refresh the browser, click home and….

{"id":1,"name":"Luke","created_at":"2017-10-11T22:21:37.642Z","updated_at":"2017-10-11T22:21:37.642Z","url":"http://localhost:3000/users/1.json"}

{"id":2,"name":"Han","created_at":"2017-10-11T22:21:37.679Z","updated_at":"2017-10-11T22:21:37.679Z","url":"http://localhost:3000/users/2.json"}

{"id":3,"name":"Leia","created_at":"2017-10-11T22:21:37.685Z","updated_at":"2017-10-11T22:21:37.685Z","url":"http://localhost:3000/users/3.json"}

{"id":4,"name":"Obi","created_at":"2017-10-11T22:21:37.692Z","updated_at":"2017-10-11T22:21:37.692Z","url":"http://localhost:3000/users/4.json"}

{"id":5,"name":"Chewbacca","created_at":"2017-10-11T22:21:37.698Z","updated_at":"2017-10-11T22:21:37.698Z","url":"http://localhost:3000/users/5.json"}

{"id":6,"name":"C3P0","created_at":"2017-10-11T22:21:37.705Z","updated_at":"2017-10-11T22:21:37.705Z","url":"http://localhost:3000/users/6.json"}

Nice! We got some JSON! We’re on the right track. Now all we have to do is get the name out of each object. To do that, we’ll have to modify the template in our component ever so slightly.

// app/javascript/home.component.js

import HomeController from './home.controller';

const HomeComponent = {
  controller: HomeController,
  controllerAs: '$ctrl',
  template: `<div ng-repeat="user in $ctrl.users">
              <h2>{{user.name}}</h2>
            </div>`
}

export default HomeComponent;

If you can’t see what was added, no worries. In the

tag we were getting the whole user, which is why we got all that extra JSON. The only thing that was added was a name attribute to the user – {{user.name}}. These things update live, so if we look back to the browser, we should see our Star Wars heroes.

An Aside

This actually took me quite a while to get running. I’m learning as I go and had some trouble understanding how to use $http in the controller. Specifically, how to inject it. Everywhere I looked, people were saying the same thing: outside of the HomeController class, inject the $http service as such – HomeController.$inject = [“$http”];

This did not work. I finally came across a someone’s code with ‘ngInject’ loaded in the constructor. I tried that, and everything worked!

We’ve gone through a lot today and I’m going to leave off here. Next time we’ll separate the $http call into a service, as it should be and see our Chewbacca’s favorite meal. Until then…Cheers:)

Advertisements

Removing whitespace from a string in Ruby vs. JavaScript

I’m back from vacation. After visiting my family in Long Island, we ventured up to the Catskills for a few nights, which culminated with my younger sister getting married. It was a very cool wedding! The next day, my wife, nephew, and I drove up to Montreal to see her folks. Montreal is one of my favorite cities (in the summer) – it has very European feel. As great as it is to get away, it’s always hard to get back into the groove after a trip. I’m just getting my sea legs back under me so this post is going to be short and sweet.

Have you ever needed to get rid of leading and trailing whitespace from a string? It’s a good rule of thumb to do just that to any input you’re receiving on a form. Luckily, it’s quite easy in most languages. Let’s take a look at how we’ll approach this in Ruby first.

str = "  How's it going?   "
=> "  How's it going?   "
# Let's get rid of that whitespace
str.strip()  
=> "How's it going?"
str 
=> "  How's it going?   "
# We didn't permanently change the string itself
# Most methods are non-destructive, but Ruby let's us be destructive if we 
# really want to. We just got to bang it (pun intended)
str.strip!() 
"How's it going?"
str 
=> "How's it going?"

Pretty simple! Let’s take a look at how to do this in JavaScript.

let str = "  How's it going?   ";
str.trim(); 
'How/'s it going?'
str  
'   How\'s it going?   '
// There's no permanent destructive way to change strings in JavaScript - they 
// are immutable. So we just have to reassign our variable like so...
str = str.trim();
'How\'s it going?'
str  
'How\'s it going?'

I mentioned that strings in JavaScript are immutable. This means the string cannot be changed in memory. So when we call a method like trim or slice on a string, the return value is a new string. Like the example above, we have to reassign ‘str’ to the return value of the method call. In Ruby however, strings are mutable. Let’s take a look.

str = "  Trim me  "
str.object_id 
=> 70365544319940 
str.strip!()
=> "Trim me"
str.object_id
=> 70365544319940 
# Even though we changed the string, the id is still the same
# We can also change characters in place, which cannot be done in JS
str[7] = '!'
=> '!'
str
=> "Trim me!"
# Pretty cool!
str.object_id
=> 70365544319940 
# No change

That’s it for this week. Always remember how cool Ruby can be. It makes life easier in many ways. JavaScript however, can do much more. It is not only an Object Oriented language, but a multi-paradigm language! From Wikipedia:

As a multi-paradigm language, JavaScript supports event-driven, functional, and imperative (including object-oriented and prototype-based) programming styles. It has an API for working with text, arrays, dates, regular expressions, and basic manipulation of the DOM, but does not include any I/O, such as networking, storage, or graphics facilities, relying for these upon the host environment in which it is embedded.

Thanks Wikipedia! Until next time! Cheers:)

Cool Shortcuts in JS

After learning Ruby, getting stuff done in Javascript can seem much harder. Many methods in Ruby, such as sort, are just a dot notation away. However, in JS you have to write out the whole function. Check it…

# Ruby
numbers = [2, 5, 6, 11, 1, 23, 3]
numbers.sort!
# numbers = [1, 2, 3, 5, 6, 11, 23] Not bad at all!
# That was so easy. Let's try it with names....

names = ['Martha', 'Tony', 'Sarah','Bob', 'Clara']
names.sort!
# names = ["Bob", "Clara", "Martha", "Sarah", "Tony"]
# Woah! It's the same!

// JS
let numbers = [2, 5, 6, 11, 1, 23, 3]
numbers = numbers.sort((a, b) => a - b)
// Well that's not so bad
// Let's try it with names

let names = ['Martha', 'Tony', 'Sarah','Bob', 'Clara']
names = names.sort((a, b) => {
  if (a < b) {
    return -1;
  } else if (a > b) {
    return 1;
  } else {
    return 0;
  }
});
// Yuck. Quite a bit more verbose

Well, unfortunately there are just some things in JavaScript that are the way they are. Like my Scottish father-in-law says, “You cannot fart against thunder”. Some things you just have to accept. Sometimes, you (or people that make the thunder) can change it. ES6 has brought some cool changes that have made programming in JS much more fun. I’ve been messing around with some cool shortcuts that you can use to give your fingers a bit of a break. Some of these were already available pre-ES6.

First let’s take a look at flattening an array of arrays.

# Just for fun I'll include the Ruby magic

arrays = [1, [2, 3], [4, [5, 6]], 7]
arrays.flatten!
# [1, 2, 3, 4, 5, 6, 7] - Too easy

// Pre ES6
var arrays = [1, [2, 3], [4, [5, 6]], 7];
var flattenedArrays = [].concat.apply([], arrays);
// [1, 2, 3, 4, 5, 6, 7]
// Not horrible, but the spread operator makes it even better...

const arrays = [1, [2, 3], [4, [5, 6]], 7];
const flattenedArrays = [].concat(...arrays)
// Not a huge difference, but - Less typing = Happier coder 

Ok that wasn’t a huge deal. Here are some examples using operands. Thanks to these wonderful humans for bringing them to my attention.

var number = 1,
  string = "1",

// How would you normally change number to a string? Personally, I'd use the  .toString() method

number.toString()
// "1" 
// But this is more fun...
number + ""  // "1"
// I like

// How bout changing that string to an integer?
// A little parseInt() shall we?

parseInt(string) // 1
// The following is much more succinct

+string // 1
// Shiiiiit. That's cool.

How bout finding the max/min from an array. Well the Math prototype does not accept an array as it’s arguments.

// Pre-JS6
const numbers = [8, 7, 55, 4];
Math.max.apply(null, numbers) // 55
Math.min.apply(null, numbers) // 4
// JS6
Math.max(...numbers)  // 55
Math.min(...numbers)  // 4
// Very nice!

# Just for S & G's - Ruby
numbers.max  # 55 - beautiful

The spread operator makes this possible by spreading the values in the array out into arguments of the math functions. Not quite the magical abstractness of Ruby, but cool nonetheless! There are of course libraries like lodash that fill in some of the cracks, but there are always new things to discover about a language. The never ending mountain is waiting to be climbed. She is forever shifting and you can never reach the top. You just have to learn to love climbing. And remember…you cannot fart against thunder (in a very broad Scottish accent). That man has some stories.

My First Time

 

Creating a CLI Gem. Nope, not that kind of blog. Sorry.

Ok.  That was fun, exciting and scary.  Not scary in a ‘OMG I’m going to die!’ way, but more of a ‘I don’t know WTF I’m doing…wait YES I DO…no I don’t!’ kind of thing.  I’m new to this whole coding thing and every new step in the learning process is a brand new experience. Being humble to the process and trusting that things will start clicking is a must. And they do start clicking. I should get the old clichéd adage, ‘Patience is a virtue’, tattooed on my fingers, so as I watch them type away, I can…well you get the point.  I’m getting faster at typing too, although I’ll probably need  a new ‘delete’ key soon.

Anywho, let’s get down to business. So this was the first gem I created. It wasn’t as painful as I thought it would be. Quite easy in fact, as most of the hard work has been done by the brave souls that came before me – learning Ruby from a manual, trudging their desktops through the snow barefoot. You know the story. Thank Google for those guys and girls. Things are much easier now. A bundle here. A gem there. And bam. Your very own gem. All you have to do is fill out your files with some fancy classes, scrape a website (which I find disturbingly fun), test it (many times in my case), and voila – an honest to Google CLI that works.

7NWB2A8I0R

It talks!

I’ll back up a bit and talk about the whole process or #!/usr/bin/env, if you will (a little Ruby humor…hehehe).  I had I few ideas about what I wanted to do my project on. My final decision came down to the scraping part of the project. Oh yeah, so I should tell you a little bit about the project. We were tasked with scraping a website for some info, with a second level of getting more detailed info about each option. I thought about the top 20 Vegan (yeah I’m one of those) restaurants in Los Angeles, but scraping Yelp looked like a no no. There’s a site called LAist.com that has top 20 things to do this weekend, but that link is always changing and my scraping mastery is not what it hopefully will be. So what else is there. Well, I’m in Los Angeles. What do people do here? Buy stuff? Eh. Botox injections? Eh. Come on!

Ok. People like the sun and we have lots of waaaater. Surfing!!! I’m not a surfer (maybe some day) but I thought it would be a fun project. A little Bing-ing around (HA!) and I came across site called surfline.com. Basic site with detailed forecasts of beaches around the world. Cool. Looked easy enough. Not so fast grasshopper. The scraping part wasn’t as easy as I thought it’d be. All the details I wanted were all in nested divs with no clear style elements. Quite the challenge. I learned quite a bit. I’m sure there are faster ways, but I did it (cue Frank Sinatra) MY WAY! Yay for me! The real test would be if it worked the next day. And it did. Pretty cool! Look ma! No hands! Here’s a look at my Scraper class:

Screen Shot 2016-04-11 at 7.27.42 PM

 

With the hard stuff out the way, the rest was fairly easy. Although it was almost finished by this point because I set up my classes before I worked on the scraper. Just connecting the dots from there. The run file is in the bin, which has this line: SurfReport::CLI.new.call. This takes us to our call method in the CLI class.

From there we are calling four methods:

  1. Our ‘make_days‘ method, which takes our array of hashes created in the Scraper class and passes that array to the Report class, which instantiates each day object with their corresponding instance variables. Those are all saved into the class method, all, which leads us to…
  2. Our ‘list_surf_reports‘ method, which iterates over our Report.all method and spits out a list for three days (today, tomorrow and the next), with a brief forecast.
  3. Our ‘menu‘ method comes next, which is the mock meat and potatoes of the program. Here we ask the user which day they want to check out. We get(s) that input and throw her into a plain old if/else statement. You get the idea.
  4. Last but not least, our ‘later‘ method. Instead of typing the industry standard, ‘exit’, we type ‘later’, cuz we’re way cool surfers.

And we’re like outta there man! In the future when my coding skills match my thinking, I’ll expand the scrape method to include searches for more cities and beaches. Check out my repo here if you have a minute. There’s a README file to get you started and surfing. Also a video of me explaining how to use the program. Thanks for reading about my first gem, and for that matter, my first blog post eva!  Now get off my WAAAAAVE!!!!