#WDI Week 7 Notes
##Monday
###MTA Demos
###Timers & Underscore
rails new matrix -T
gem 'underscore-rails' in Gemfile
bundle
//= require underscore in application.js
rails generate controller Pages index
start server
view page sources to check that underscore.js is included
Matrix js and comments:
var numbers = [];
// set to null so that i can call clear timeout and stop the timer running
$(document).ready(function() {
var addBox = function(n){
var $box = $('<div/>').addClass('box');
$box.text(n);
$box.prependTo('#boxes');
}
// the difference between .text and .html is that html will allow you to include
// html tags like <strong></strong>, text wont. in this case we know that the text will
// always just be a number, so no need to make it hml
var showNumbers = function(){
$('#boxes').empty();
// _(numbers).each(function(n){
// addBox(n);
// });
_(numbers).each(addBox); // (this line does the same as the above three)
// when somebody calls show numbers pass the numbers into underscore
// call the .each method on it
};
var addNumber = function(){
var n = $('#number').val();
console.log(n);
// at this point, the n is a string
n = parseInt(n);
numbers.push(n);
addBox(n);
$('#number').val('').focus();
// sets the value to nothing and focuses on the input
};
$('#add_number').on('click', addNumber);
// $('#add_number').click(addNumber); this is the same code as above
var squareNumbers = function(){
console.log("squareing numbers");
numbers = _(numbers).map(function(n){
return n * n;
});
// _.map(numbers), function(num){return num * num;});
// _(numbers).map(function(num){return num * num;});
// the above two lines do the same thing
showNumbers();
};
$('#square').on('click', squareNumbers);
var customFunction = function(){
numbers = _(numbers).map(calc);
showNumbers();
};
var calc = function(i){
var equation = $('#number').val();
return eval(equation);
};
$('#function').on('click', customFunction);
var timer = null;
$('#start').on('click', function(){
clearInterval(timer);
// this will ensure that any existing timer is stopped;
timer = setInterval(addRandom, 500);
// this line is starting the timer, saying every interval i want
// you to run the addRandom function
// and it will do this every 500 miliseconds
});
$('#stop').on('click', function(){
clearInterval(timer);
});
var addRandom = function(){
var n = _.random(10000);
// generates a random number between 0 and 10,000
console.log(n);
numbers.push(n);
addBox(n);
};
});
Matrix html:
<h1>Welcome to the Matrix</h1>
<div>
<%= text_field_tag 'number', nil, :autofocus => true %>
<%= button_tag 'Add number', :id => 'add_number' %>
<%= button_tag 'Square', :id => 'square' %>
<%= button_tag 'Custom function', :id => 'function' %>
<%= button_tag 'Start', :id => 'start' %>
<%= button_tag 'Stop', :id => 'stop' %>
</div>
<div id="boxes"></div>
###Ajax Intro
curl http://code.jquery.com/jquery-1.11.2.js > public/js/jquery.js
AJAX - asynchronous javascript and xml
We did this in the console:
var request = new XMLHttpRequest()
request
XMLHttpRequest {statusText: "", status: 0, responseURL: "", response: "", responseType: ""…}
request.readyState
0
request.open('GET', '/bros')
undefined
request.readyState
1
request.send()
request.responseText
"Zeppo"
To get another Marx brother:
var request = new XMLHttpRequest()
undefined
request.open('GET', '/bros')
undefined
request.send()
undefined
VM450:2 XHR finished loading: GET "http://localhost:4567/bros".
request.responseText
"Harpo"
-
you can re-use the same request, you don't need to make a new one each time
-
making a new request is like opening a new tab
-
using the same request is like re-entering the url in the same tab
Another example:
In main.rb:
get '/lotto' do
Random.rand(1..40).to_s
end
In the console:
var lottoRequest = new XMLHttpRequest()
undefined
lottoRequest.open('GET', '/lotto')
undefined
lottoRequest.readyState
1
lottoRequest.send()
undefined
VM632:2 XHR finished loading: GET "http://localhost:4567/lotto".
lottoRequest.responseText
"34"
**Another console example: **
var request = new XMLHttpRequest()
undefined
request.onreadystatechange = function(){
console.log(this.responseText);
console.log(this.readyState);
}
function (){
console.log(this.responseText);
console.log(this.readyState);
}
request.readyState
0
request.open('GET', '/slow')
VM1162:3
VM1162:4 1
undefined
request.send()
undefined
VM1162:3
VM1162:4 2
VM1162:3 haha
VM1162:4 3
VM1316:2 XHR finished loading: GET "http://localhost:4567/slow".
VM1162:3 haha
VM1162:4 4
In the ajax.js file:
var request = new XMLHttpRequest();
request.onreadystatechange = function(){
console.log("ready state has changed");
console.log(this.readyState);
console.log(this.responseText);
if (this.readyState === 4) {
$('h3').text(this.responseText);
console.log("Done");
}
};
request.open('GET', '/slow');
request.send(); // asynchronous
console.log("ajax request is probably still running");
###Flickr
https://github.com/amysimmons/wdi8_homework/tree/master/amy/mta_jquery
##Tuesday
###Warmup
https://gist.github.com/wofockham/53232592840ff0b712cd
###Flickr Demos
https://github.com/amysimmons/wdi8_homework/tree/master/amy/flickr_app
###Flickr Review
###More Ajax
###JQuery: Return Flight
###Ajax Movies
$.ajax('http://www.omdbapi.com/?t=jaws&y=&plot=short&r=json')
Object {readyState: 1, getResponseHeader: function, getAllResponseHeaders: function, setRequestHeader: function, overrideMimeType: function…}
jquery-87424c3c19e96d4fb033c10ebe21ec40.js?body=1:9660 XHR finished loading: GET "http://www.omdbapi.com/?t=jaws&y=&plot=short&r=json".
$.ajax('http://www.abc.net.au/news')
Object {readyState: 1, getResponseHeader: function, getAllResponseHeaders: function, setRequestHeader: function, overrideMimeType: function…}
localhost/:1 XMLHttpRequest cannot load http://www.abc.net.au/news. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access.
$.ajax('http://www.omdbapi.com/?t=jaws&y=&plot=short&r=json').done(function(result){
console.log(result);
});
Object {readyState: 1, getResponseHeader: function, getAllResponseHeaders: function, setRequestHeader: function, overrideMimeType: function…}abort: function ( statusText ) {always: function () {complete: function () {done: function () {error: function () {fail: function () {getAllResponseHeaders: function () {getResponseHeader: function ( key ) {overrideMimeType: function ( type ) {pipe: function ( /* fnDone, fnFail, fnProgress */ ) {progress: function () {promise: function ( obj ) {readyState: 4responseText: "{"Title":"Jaws","Year":"1975","Rated":"PG","Released":"20 Jun 1975","Runtime":"124 min","Genre":"Drama, Thriller","Director":"Steven Spielberg","Writer":"Peter Benchley (screenplay), Carl Gottlieb (screenplay), Peter Benchley (based on the novel by)","Actors":"Roy Scheider, Robert Shaw, Richard Dreyfuss, Lorraine Gary","Plot":"When a gigantic great white shark begins to menace the small island community of Amity, a police chief, a marine scientist and grizzled fisherman set out to stop it.","Language":"English","Country":"USA","Awards":"Won 3 Oscars. Another 10 wins & 14 nominations.","Poster":"http://ia.media-imdb.com/images/M/MV5BNDcxODkyMjY4MF5BMl5BanBnXkFtZTgwOTk5NTc5MDE@._V1_SX300.jpg","Metascore":"79","imdbRating":"8.1","imdbVotes":"332,608","imdbID":"tt0073195","Type":"movie","Response":"True"}"setRequestHeader: function ( name, value ) {state: function () {status: 200statusCode: function ( map ) {statusText: "OK"success: function () {then: function ( /* fnDone, fnFail, fnProgress */ ) {__proto__: Object
jquery-87424c3c19e96d4fb033c10ebe21ec40.js?body=1:9660 XHR finished loading: GET "http://www.omdbapi.com/?t=jaws&y=&plot=short&r=json".jquery-87424c3c19e96d4fb033c10ebe21ec40.js?body=1:9660 jQuery.ajaxTransport.sendjquery-87424c3c19e96d4fb033c10ebe21ec40.js?body=1:9211 jQuery.extend.ajaxVM418:2 (anonymous function)VM229:777 InjectedScript._evaluateOnVM229:710 InjectedScript._evaluateAndWrapVM229:626 InjectedScript.evaluate
VM418:3 {"Title":"Jaws","Year":"1975","Rated":"PG","Released":"20 Jun 1975","Runtime":"124 min","Genre":"Drama, Thriller","Director":"Steven Spielberg","Writer":"Peter Benchley (screenplay), Carl Gottlieb (screenplay), Peter Benchley (based on the novel by)","Actors":"Roy Scheider, Robert Shaw, Richard Dreyfuss, Lorraine Gary","Plot":"When a gigantic great white shark begins to menace the small island community of Amity, a police chief, a marine scientist and grizzled fisherman set out to stop it.","Language":"English","Country":"USA","Awards":"Won 3 Oscars. Another 10 wins & 14 nominations.","Poster":"http://ia.media-imdb.com/images/M/MV5BNDcxODkyMjY4MF5BMl5BanBnXkFtZTgwOTk5NTc5MDE@._V1_SX300.jpg","Metascore":"79","imdbRating":"8.1","imdbVotes":"332,608","imdbID":"tt0073195","Type":"movie","Response":"True"}
##Wednesday
###Warm up
https://gist.github.com/anonymous-wolf/9bf29bd5980a35b3f9ec
https://gist.github.com/anonymous-wolf/0288b4aea24fd07ca833
###Movie demos
https://github.com/amysimmons/wdi8_homework/tree/master/amy/movies_jquery
###Ajax review
###TODO App
Have a task
A description of the task
A checkbox that will determine whether it was completed or not
They can check or uncheck the box
Save button
rails new todo_app -T
rails generate migration create_tasks task:text description:text completed:boolean
rake db:migrate
touch app/models/task.rb
annotate
###Homework
####Working with JavaScript in Rails - http://edgeguides.rubyonrails.org/working_with_javascript_in_rails.html
Ajax introduction:
- JavaScript can make requests to the server, and parse the response.
- It also has the ability to update information on the page.
- Combining these two powers, a JavaScript writer can make a web page that can update just parts of itself, without needing to get the full page data from the server.
- This is a powerful technique that we call Ajax.
$.ajax({
url: "/test"
}).done(function(html) {
return $("#results").append(html);
});
The above code that makes an Ajax request using the jQuery library, appending the result to the div with an id of results.
Unobtrusive JavaScript:
Built-in helpers:
Methods written in Ruby to assist you in generating HTML.
eg form_for
<%= form_for(@article, remote: true) do |f| %>
...
<% end %>
The above generates the following html:
<form accept-charset="UTF-8" action="/articles" class="new_article" data-remote="true" id="new_article" method="post">
...
</form>
The data-remote="true" means the form will be submitted by Ajax rather than by the browser's normal submit mechanism.
To bind to the ajax:success event upon a successful submission:
$(document).ready(function() {
return $("#new_article").on("ajax:success", function(e, data, status, xhr) {
return $("#new_article").append(xhr.responseText);
}).on("ajax:error", function(e, xhr, status, error) {
return $("#new_article").append("<p>ERROR</p>");
});
});
form_tag differs slightly
<%= form_tag('/articles', remote: true) do %>
...
<% end %>
results in the following html:
<form accept-charset="UTF-8" action="/articles" data-remote="true" method="post">
...
</form>
link_to generates links and also has a remote option:
<%= link_to "an article", @article, remote: true %>
generates the following html:
<a href="/articles/1" data-remote="true">an article</a>
Say we have a list of articles that can be deleted with one click:
<%= link_to "Delete article", @article, remote: true, method: :delete %>
button_to
<%= button_to "An article", @article, remote: true %>
generates the following html:
<form action="/articles/1" class="button_to" data-remote="true" method="post">
<div><input type="submit" value="An article"></div>
</form>
Server-side concerns:
Ajax requests should usually return JSON, rather than HTML.
In this form example:
<%= form_for(@user, remote: true) do |f| %>
<%= f.label :name %><br>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
Because the form's remote option is set to true, the request will be posted to the UsersController as an Ajax request, looking for JavaScript.
The create action of your controller would then look like this:
# app/controllers/users_controller.rb
# ......
def create
@user = User.new(params[:user])
respond_to do |format|
if @user.save
format.html { redirect_to @user, notice: 'User was successfully created.' }
format.js {}
format.json { render json: @user, status: :created, location: @user }
else
format.html { render action: "new" }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
Turbolinks:
Turbolinks will make an Ajax request for the page, parse the response, and replace the entire of the page with the of the response.
It will then use PushState to change the URL to the correct one, preserving refresh semantics and giving you pretty URLs.
Additional reading:
http://www.alfajango.com/blog/rails-3-remote-links-and-forms/
http://railscasts.com/episodes/205-unobtrusive-javascript
http://railscasts.com/episodes/390-turbolinks
- An anonymous function allows a developer to create a function that has no name.
- Anonymous functions can be used to store a bit of functionality in a variable and pass that piece of functionality around.
For example, an anonymous function in Ruby and JS:
[1, 2, 3].map do |number_to_double|
number_to_double * 2
end
_.map([1, 2, 3], function(numberToDouble) {
return numberToDouble * 2
})
- When a closure is created it can reference any of the variables available in its local environment.
JS lexical scoping example:
function lexicalScopeExample(multiplier) {
return _.map([1, 2, 3], function(number) {
var newNumberToDouble = number * multiplier
return newNumberToDouble * 2
});
}
In our JavaScript example we have two lexical scopes:
- The lexicalScopeExample function has the parameter multiplier
- The block we pass to the map method can access the multiplier, number and newNumberToDouble variables
-
The block’s lexical scope includes the variables which belong to the lexical scope it is created in.
-
The inverse is not true.
-
Variables defined in the block’s lexical scope are not then available to the function’s lexical scope.
Closures:
-
Closures allows us to define units of functionality while maintaining references to all the variables that exist in the lexical scope they are defined.
-
These units of functionality can then be executed in a different lexical scope.
function scopeTwo(callback) {
callback();
}
function scopeOne() {
var closureAddend = 3;
scopeTwo(function() {
closureAddend = closureAddend + 5;
});
console.log("The variable in our main lexical scope after the method is
executed " + closureAddend);
}
javascript > scopeOne()
The variable in our main lexical scope after the method is executed equals 8
In our JavaScript example we have three lexical scopes:
-
The scopeOne function includes the closureAddend variable
-
The function (closure) we pass when we call the scopeTwo function includes the closureAddend variables
- In the browser console we call our scopeOne function
- Within this function we create the closureAddend variable and assign it the value of 3
- Within the lexical scope of our scopeOne function we define a function and pass it to the scopeTwo function
- Inside the new lexical scope of the scopeTwo function we execute our closure that we called callback
- Inside of our closure we add 5 to the closureAddend variable we defined within the lexical scope of the scopeOne function
- We then return to our scopeOne lexical scope and log our closureAddend variable, which has now been incremented by 5
Our closure gets executed in a completely different lexical scope than it was created in, but still has access to the variables in the original scope.
We can't access closureAddend in scopeTwo's lexical scope:
function scopeTwo(callback) {
callback();
console.log("We just modified this variable in the block above " + closureAddend);
}
function scopeOne() {
var closureAddend = 3;
scopeTwo(function() {
closureAddend = closureAddend + 5;
});
console.log("The variable in our main lexical scope after the method
is executed " + closureAddend);
}
javascript > scopeOne()
ReferenceError: closureAddend is not defined
Source:
https://gist.github.com/wofockham/9fb80a265a60c865b308
https://robots.thoughtbot.com/back-to-basics-anonymous-functions-and-closures
####Everything you need to know about Scope
What is Scope?:
Scope refers to the current context of your code.
Ask yourself, are we inside Scope A or Scope B?
Global:
Before you write a line of JavaScript, you're in what we call the Global Scope.
jQuery('.myClass'); is accessing jQuery in global scope.
We can refer to this access as the namespace.
The namespace is sometimes an interchangable word for scope, but usually the refers to the highest level scope.
In this case, jQuery is in the global scope, and is also our namespace.
The jQuery namespace is defined in the global scope, which acts as a namespace for the jQuery library as everything inside it becomes a descendent of that namespace.
Local:
Any scope defined past the global scope.
// Scope A: Global scope out here
var myFunction = function () {
// Scope B: Local scope in here
};
Any locally scoped items are not visible in the global scope - unless exposed!
New function = New Scope:
// Scope A
var myFunction = function () {
// Scope B
var myOtherFunction = function () {
// Scope C
};
};
Lexical scope:
Whenever you see a function within another function, the inner function has access to the scope in the outer function, this is called Lexical Scope or Closure.
// Scope A
var myFunction = function () {
// Scope B
var name = 'Todd'; // defined in Scope B
var myOtherFunction = function () {
// Scope C: `name` is accessible here!
};
};
Any variables/objects/functions defined in it's parent scope, are available in the scope chain
var name = 'Todd';
var scope1 = function () {
// name is available here
var scope2 = function () {
// name is available here too
var scope3 = function () {
// name is also available here!
};
};
};
But lexical scope does not work backwards.
// name = undefined
var scope1 = function () {
// name = undefined
var scope2 = function () {
// name = undefined
var scope3 = function () {
var name = 'Todd'; // locally scoped
};
};
};
Scope chain:
When resolving a variable, JavaScript starts at the innermost scope and searches outwards until it finds the variable/object/function it was looking for.
Closures:
Inside our scope, we can return things so that they're available in the parent scope:
var sayHello = function (name) {
var text = 'Hello, ' + name;
return function () {
console.log(text);
};
};
In the above example, our scope inside sayHello is inaccessible to the public scope.
Calling the function alone will do nothing as it returns a function:
sayHello('Todd'); // nothing happens, no errors, just silence...
The function returns a function, which means it needs assignment, and then calling:
var helloTodd = sayHello('Todd');
helloTodd(); // will call the closure and log 'Hello, Todd'
Scope and this:
By default this refers to the outer most global object, the window.
We can easily show how invoking functions in different ways binds the this value differently:
var myFunction = function () {
console.log(this); // this = global, [object Window]
};
myFunction();
var myObject = {};
myObject.myMethod = function () {
console.log(this); // this = Object { myObject }
};
var nav = document.querySelector('.nav'); // <nav class="nav">
var toggleNav = function () {
console.log(this); // this = <nav> element
};
nav.addEventListener('click', toggleNav, false);
But the this value can cause problems when inside the same function, the scope can be changed and the this value can default back to the window.
For example:
var nav = document.querySelector('.nav'); // <nav class="nav">
var toggleNav = function () {
console.log(this); // <nav> element
setTimeout(function () {
console.log(this); // [object Window]
}, 1000);
};
nav.addEventListener('click', toggleNav, false);
In the above example we've created new scope which is not invoked from our event handler, so it defaults to the window Object as expected.
You can use that access the proper this value:
var nav = document.querySelector('.nav'); // <nav class="nav">
var toggleNav = function () {
var that = this;
console.log(that); // <nav> element
setTimeout(function () {
console.log(that); // <nav> element
}, 1000);
};
nav.addEventListener('click', toggleNav, false);
The above allows you to use the proper this value and resolve problems with newly created scope.
.call() and .apply():
The .call() and .apply() methods allow you to pass in a scope to a function, which binds the correct this value.
Here, we set this to be the value of the li:
var links = document.querySelectorAll('nav li');
for (var i = 0; i < links.length; i++) {
(function () {
console.log(this);
}).call(links[i]);
}
The difference between .call() and .apply():
We can use either .call() or .apply() to change the scope, but any further arguments are where the two differ...
-
.call(scope, arg1, arg2, arg3) takes individual arguments, comma separated
-
.apply(scope, [arg1, arg2]) takes an Array of arguments
Using .call() or .apply() actually invokes your function
.bind():
Using .bind() does not invoke a function, it merely binds the values before the function is invoked.
nav.addEventListener('click', toggleNav.bind(scope, arg1, arg2), false);
The function isn't invoked, and the scope can be changed if needed, but arguments are sat waiting to be passed in.
Source:
https://gist.github.com/wofockham/9fb80a265a60c865b308
http://toddmotto.com/everything-you-wanted-to-know-about-javascript-scope/
##Thursday
###Warmup
https://gist.github.com/anonymous-wolf/36571a4de68d042c7131
https://gist.github.com/anonymous-wolf/383eba988f641a6abc90
class DNA
def initialize(strand1)
@strand1 = strand1
end
def hamming_distance(strand2)
@strand2 = strand2
strand1_length = @strand1.chars.length
strand2_length = @strand2.chars.length
min_length = [strand1_length, strand2_length].min
count = 0
index = - 1
while index < min_length - 1 do
index = index += 1
a = @strand1[index]
b = @strand2[index]
if a != b
count += 1
end
end
p count
end
end
The zip method:
http://apidock.com/ruby/Array/zip
Allows for easy comparison betweeen two arrays!
###Callbacks
Understanding the difference between the following callbacks:
$(document).ready(fn)
- this says i want you to run when the document is ready, you know that at some point the document will be ready and your code will always run, but you don't know when, some time in the future i want this to happen
setTimeout(fn, 1000)
- after 1000 ms i want this function to ran
.fadeOut(fn)
- after the fadeout, i want this fn to run, the default is 400ms, so after 400ms i want this fn to run
.on('click', fn)
- you can't guarantee that a user ever actually clicks this function, so it may never run
.ajax('someurl').done(fn)
-
this ajax fn should finish eventually, but you dont know how long its going to take, also something can go wrong wit this url
-
whats special about the ajax one is that it can access data that isn't currently in your page or in your dom
-
it can reach out to any url, and either get some data from it or put some data to it
-
eg a chat system, the magic thing about ajax is that it can go and get data from the server, pull it into your javascript environment, and then you can do something with it
-
most of the time it will be pulling in something that you couldn't have previously predicted
-
the information it can get or send to the server is anything, it's up to you
-
ajax needs the server to cooperate as well, it's not just javascript
Other than that the above callbacks are all similar, it's always some function that gets called at some point in the future.
###Handlebars
###UJS
Unobtrusive javascript:
if there is data remote true field set on the form
:remote => true
instead of it running my ajax code
it makes a silent request in the background
###Codeschool JavaScript path - https://www.codeschool.com/paths/javascript
##Friday
###Git talk
the app is created by one person in the team
/config/database.yml - add this in the .gitignore file
in config folder, make a new database.yml.example file
host: localhost
username: inceptor
rake db:create
push the existing repo to the command line
by doing a git remote
and then a git push
git rm config/database.yml (if the yml file gets pushed, and doesnt get removed)
the other people in the group clone the project
always git pull before you git push
so that you have the latest changes on your computer
git log wil show you what changes have been made
branch scenario:
git checkout -b users
generate a migration
generate a controller
git add
git commit -m
git status, on branch users
git merge master into users
then can push my users branch into master