Nepabeigtā dienasgrāmata: DELFI. Valda uzskats, ka..

šonakt, 1:30 am, atsauksmēm

DELFI. Valda uzskats, ka cilvēka seksuālo orientāciju iespējams noteikt, izmantojot īpašu teoriju.

Nepabeigtā dienasgrāmata (pajautaa): Poll #19689 Open to: ..

vakar, 11:04 pm, 14 atsauksmes / atsauksmēm

Poll #19689
Open to: All, results viewable to: All

Cikos jūs parasti ejat gulēt?

View Answers

20:00
1 (0.9%)

20:30
0 (0.0%)

21:00
1 (0.9%)

21:30
0 (0.0%)

22:00
2 (1.8%)

22:30
4 (3.7%)

23:00
5 (4.6%)

23:30
14 (12.8%)

24:00
19 (17.4%)

00:30
10 (9.2%)

01:00
19 (17.4%)

01:30
6 (5.5%)

02:00
13 (11.9%)

02:30
5 (4.6%)

03:00
10 (9.2%)

ублюдок (racoon)

ублюдок: Par межкультурние отнашения

vakar, 10:18 pm, 7 atsauksmes / atsauksmēm

Iespējams šo ziņu palaidāt garām, bet pagājušonedēļ Baku priekšpilsētas, Surahānas mērs/izpilddirektors (viņiem tas viss viens) parakstīja sadraudzības līgumu ar Rīgas Latgales priekšpilsētu, nu ķip Maskačko po Ķengarags. Lai nu kā viņiem tā draudzība, mēs "āfterofišal" pamatīgi padraudzējāmies Jūrmalā. Ne tikai par labiekārtošanu, pat par Karabahu, sabiedriskajām attiecībām, armēņiem, krieviem, latviešiem un naftas cenām izdraudzējāmies, līdz bija pat nakts un bu bū.

No tā visa dzimst "tiešais zvans", ko šodien izmantoju, un pirms to anouncēt, gribētu Jūsu, cilvēk-speciālistu viedokli dzirdēt. Jūrmalai ar Baku satelītpilsētām nekāda līguma nav, nekā nav, tukša kopa, bet cik pareizi vai nē, ir uzrunāt ar ideju un pie uzvaras ieviest tradīciju, ka mēs ar Azeriem apmaināmies ar "labākajiem audzēkņiem". Tas būtu piemēram visu piecu vidusskolu labākās 11 klases liecības par domes naudu (~100 LVL x liecību skaits ~10) sūtīt uz Baku citu kultūru apgūšanai. Baku puse viņus + 2 pedagogus uzņem, sarīko tusu ar vienaudžiem, diskusija par jebko, kultūras apmaiņa, muzeji, ekskursijas utt un tas viss par AZ naudu. No mums tik cik biļetes. Es uzskatu, ka tas ir nereāli kruta, 11. klasniekam redzēt citas kultūras, dabūt jaunus draugus utt., bet es neesmu izglītības un kultūras speciālists, lai spriestu, vai mums to vajag un vai par šādu piedāvājumu mani nelinčos. Your opīņjen?

BTW. Izglītības iestādēs un vidusskolniekos šo ziņu dabūšu iekšā četrās stundās, ja skaitam no rītdienas rīta.
sursurs (sursurs)

sursurs: šodien nodarbojos ar izmirkšanu līdz ādai

vakar, 10:08 pm, 1 atsauksme / atsauksmēm

3x.
visas trīs tiešām līdz ādai.
Vajadzēja man pēc skolas uz Šmerli (jo kkas ritenim minoties bija sācis nejēgā dīvaini krakšķēt. Arvis saka, ka vainīga esot brīvrumba - kurai vnk kkas gļuko. Varot vēl nomočīt visu vasaru, bet tikpat labi šī varot izbeigties jebkurā brīdī. Un ar izbeigšanos apzīmējot stāvokli, kad pabraukt fiziski nav iespējams.
Nobazarēju ar bračku pa mobu bīdot dzelas par brīvrumbas iegādi (viņš savu veco taisījās pārdot). Kad pamalē jau sāka zibeņot un ducināt, tad attapos, ka jājož uz darbu. Ar 10min izrādās nepietika. Jau krietni pirms teikas dabūju pirmās lieļās lāses, bet kamēr biju tikusi līdz Biķernieku ielai, jau bija absolūti vienalga, vai pati braucu peļķēs un piesmeļu botes vēl vairāk, vai arī to dara blakus esošie autiņi un es dabūju sev virsū lieku ūdens spaini..
Darbā brīnumianā kārtā bikses pat izžuva (laikam, tāpēc ka nevilku nost), zeķes izgriezu, bet tās tika knapi apsusēja. Labi, ka no rīta džemperis bija tikai piesvīdis, tā nu to izžāvēju ar domu vilkt vakarā.

Vaarā tkla nočammājos un izlīdu laukā, lai pēc 1 minūtes sāktos tāds gāziens, kurš mani 20mm posmā izmērcēja līdz pēdējai vīlītei. Ielīdu pieturā apdomāt dzīvi. Sapratu, ka anyway viss būs slapjš, tāpēc varu arī aizbraukt uz to Olimpiju. Nu neko - saslapu vēl vairāk, pēc 5min gan kļuva silti (labi, ka temperatūra turējās kkādi padsmit grādi, pie zemākas es būtu sākusi drebuļoties).
Olimpijā beigu beigās izdomāju, ka ar tām slapjajām drēbēm es nejūtos spējīga izlšit vēlreiz laukā lietū, lai brauktu vēl 8-9km uz māju, tāpēc Rimčikā nopirku kkādu sporta jaku pa 5.99 (kura gan laiž cauri ūdeni), tomēr (kā izrādījās) ne pārāk stipri, plus ir silta un vispār baigi ērtā :)
Nu tad es mājās braucot samirku trešo reizi. Plus pa Krasteni mani apšķieda nenosakāms skaits automašīnu, tai skaitā vienss buss pacēla tādu daudzumu ūdens un sagāza man tieši ģīmī, ka man aš elpu aizsita. Bet nu neko - ja es braucu pa KRasta ielas brauktuvi tādā laikā, es aptuveni rēķinos ar sekām..
tēmas: velo
jauna un kaislīga (nrk)

jauna un kaislīga: Aptauja par apģērbu ..

vakar, 8:11 pm, atsauksmēm

lavendera (lavendera)

lavendera: Ja no bulciņām un cepumiem..

vakar, 4:26 pm, 2 atsauksmes / atsauksmēm

Ja no bulciņām un cepumiem cilvēkam būtu tikpat acumirklīga reakcija kā no alkohola, tad pateikt uz visiem laikiem "bulciņas mani neinteresē" būtu vieglāk par vieglu. Bet šīs, nelietes, piezogas kā partizāni un ierīko uz tava vēdera un pakaļas nocietinājumus ar klusu mērķi "mēs tagad šeit sēdēsim un prom neiesim". Un tu pat nepamani tās diversantes, iekams spogulis nesāk uz tevi bļaut.

Iedomājieties citu versiju: tu apēd bulciņu un - hop! - bulciņa materializējas zem ādas uz vēdera. Vēēeē, nēēēē, nekādas bulciņas, dodiet man spinātus un brokoļus! Pat nekādu gribasspēku vairs nevajadzētu, vai ne?

- (n_komentari): Briselē dievs nav tuvāk?

vakar, 4:10 pm, 1 atsauksme / atsauksmēm

Briselē dievs nav tuvāk?
lavendera (lavendera)

lavendera: Tas ir tā normāli, ka..

vakar, 3:55 pm, 1 atsauksme / atsauksmēm

Tas ir tā normāli, ka tieši pēc apkopes sāk čakarēties ātrumpārslēgs, uz dažiem ātrumiem krakšķēt zobrati un ķēde pēkšņi izrādās izstaipījusies? Līdz tam tikai mazliet čīkstēja pedāļi minoties, nomainīja monobloku...

SYN: A List Apart: The Full Feed: The Design of Code: Organizing JavaScript

vakar, 12:00 pm, (bez komentāriem..)

Great design is a product of care and attention applied to areas that matter, resulting in a useful, understandable, and hopefully beautiful user interface. But don’t be fooled into thinking that design is left only for designers.

There is a lot of design in code, and I don’t mean code that builds the user interface—I mean the design of code.

Well-designed code is much easier to maintain, optimize, and extend, making for more efficient developers. That means more focus and energy can be spent on building great things, which makes everyone happy—users, developers, and stakeholders.

There are three high-level, language-agnostic aspects to code design that are particularly important.

  1. System architecture—The basic layout of the codebase. Rules that govern how various components, such as models, views, and controllers, interact with each other.
  2. Maintainability—How well can the code be improved and extended?
  3. Reusability—How reusable are the application’s components? How easily can each implementation of a component be customized?

In looser languages, specifically JavaScript, it takes a bit of discipline to write well-designed code. The JavaScript environment is so forgiving that it’s easy to throw bits and pieces everywhere and still have things work. Establishing system architecture early (and sticking to it!) provides constraints to your codebase, ensuring consistency throughout.

One approach I’m fond of consists of a tried-and-true software design pattern, the module pattern, whose extensible structure lends itself to a solid system architecture and a maintainable codebase. I like building modules within a jQuery plugin, which makes for beautiful reusability, provides robust options, and exposes a well-crafted API.

Below, I’ll walk through how to craft your code into well-organized components that can be reused in projects to come.

The module pattern

There are a lot of design patterns out there, and equally as many resources on them. Addy Osmani wrote an amazing (free!) book on design patterns in JavaScript, which I highly recommend to developers of all levels.

The module pattern is a simple structural foundation that can help keep your code clean and organized. A “module” is just a standard object literal containing methods and properties, and that simplicity is the best thing about this pattern: even someone unfamiliar with traditional software design patterns would be able to look at the code and instantly understand how it works.

In applications that use this pattern, each component gets its own distinct module. For example, to build autocomplete functionality, you’d create a module for the textfield and a module for the results list. These two modules would work together, but the textfield code wouldn’t touch the results list code, and vice versa.

That decoupling of components is why the module pattern is great for building solid system architecture. Relationships within the application are well-defined; anything related to the textfield is managed by the textfield module, not strewn throughout the codebase—resulting in clear code.

Another benefit of module-based organization is that it is inherently maintainable. Modules can be improved and optimized independently without affecting any other part of the application.

I used the module pattern for the basic structure of jPanelMenu, the jQuery plugin I built for off-canvas menu systems. I’ll use that as an example to illustrate the process of building a module.

Building a module

To begin, I define three methods and a property that are used to manage the interactions of the menu system.


var jpm = {
animated: true,
openMenu: function( ) {
…
this.setMenuStyle( );
},
closeMenu: function( ) {
…
this.setMenuStyle( );
},
setMenuStyle: function( ) { … }
};

The idea is to break down code into the smallest, most reusable bits possible. I could have written just one toggleMenu( ) method, but creating distinct openMenu( ) and closeMenu( ) methods provides more control and reusability within the module.

Notice that calls to module methods and properties from within the module itself (such as the calls to setMenuStyle( )) are prefixed with the this keyword—that’s how modules access their own members.

That’s the basic structure of a module. You can continue to add methods and properties as needed, but it doesn’t get any more complex than that. After the structural foundations are in place, the reusability layer—options and an exposed API—can be built on top.

jQuery plugins

The third aspect of well-designed code is probably the most crucial: reusability. This section comes with a caveat. While there are obviously ways to build and implement reusable components in raw JavaScript (we’re about 90 percent of the way there with our module above), I prefer to build jQuery plugins for more complex things, for a few reasons.

Most importantly, it’s a form of unobtrusive communication. If you used jQuery to build a component, you should make that obvious to those implementing it. Building the component as a jQuery plugin is a great way to say that jQuery is required.

In addition, the implementation code will be consistent with the rest of the jQuery-based project code. That’s good for aesthetic reasons, but it also means (to an extent) that developers can predict how to interact with the plugin without too much research. Just one more way to build a better developer interface.

Before you begin building a jQuery plugin, ensure that the plugin does not conflict with other JavaScript libraries using the $ notation. That’s a lot simpler than it sounds—just wrap your plugin code like so:


(function($) {
// jQuery plugin code here
})(jQuery);

Next, we set up our plugin and drop our previously built module code inside. A plugin is just a method defined on the jQuery ($) object.


(function($) {
$.jPanelMenu = function( ) {
var jpm = {
animated: true,
openMenu: function( ) {
…
this.setMenuStyle( );
},
closeMenu: function( ) {
…
this.setMenuStyle( );
},
setMenuStyle: function( ) { … }
};
};
})(jQuery);

All it takes to use the plugin is a call to the function you just created.


var jpm = $.jPanelMenu( );

Options

Options are essential to any truly reusable plugin because they allow for customizations to each implementation. Every project brings with it a slew of design styles, interaction types, and content structures. Customizable options help ensure that you can adapt the plugin to fit within those project constraints.

It’s best practice to provide good default values for your options. The easiest way to do that is to use jQuery’s $.extend( ) method, which accepts (at least) two arguments.

As the first argument of $.extend( ), define an object with all available options and their default values. As the second argument, pass through the passed-in options. This will merge the two objects, overriding the defaults with any passed-in options.


(function($) {
$.jPanelMenu = function(options) {
var jpm = {
options: $.extend({
'animated': true,
'duration': 500,
'direction': 'left'
}, options),
openMenu: function( ) {
…
this.setMenuStyle( );
},
closeMenu: function( ) {
…
this.setMenuStyle( );
},
setMenuStyle: function( ) { … }
};
};
})(jQuery);

Beyond providing good defaults, options become almost self-documenting—someone can look at the code and see all of the available options immediately.

Expose as many options as is feasible. The customization will help in future implementations, and flexibility never hurts.

API

Options are terrific ways to customize how a plugin works. An API, on the other hand, enables extensions to the plugin’s functionality by exposing methods and properties for the implementation code to take advantage of.

While it’s great to expose as much as possible through an API, the outside world shouldn’t have access to all internal methods and properties. Ideally, you should expose only the elements that will be used.

In our example, the exposed API should include calls to open and close the menu, but nothing else. The internal setMenuStyle( ) method runs when the menu opens and closes, but the public doesn’t need access to it.

To expose an API, return an object with any desired methods and properties at the end of the plugin code. You can even map returned methods and properties to those within the module code—this is where the beautiful organization of the module pattern really shines.


(function($) {
$.jPanelMenu = function(options) {
var jpm = {
options: $.extend({
'animated': true,
'duration': 500,
'direction': 'left'
}, options),
openMenu: function( ) {
…
this.setMenuStyle( );
},
closeMenu: function( ) {
…
this.setMenuStyle( );
},
setMenuStyle: function( ) { … }
};

return {
open: jpm.openMenu,
close: jpm.closeMenu,
someComplexMethod: function( ) { … }
};
};
})(jQuery);

API methods and properties will be available through the object returned from the plugin initialization.


var jpm = $.jPanelMenu({
duration: 1000,
…
});
jpm.open( );

Polishing developer interfaces

With just a few simple constructs and guidelines, we’ve built ourselves a reusable, extensible plugin that will help make our lives easier. Like any part of what we do, experiment with this structure to see if it works for you, your team, and your workflow.

Whenever I find myself building something with a potential for reuse, I break it out into a module-based jQuery plugin. The best part about this approach is that it forces you to use—and test—the code you write. By using something as you build it, you’ll quickly identify strengths, discover shortcomings, and plan changes.

This process leads to battle-tested code ready for open-source contributions, or to be sold and distributed. I’ve released my (mostly) polished plugins as open-source projects on GitHub.

Even if you aren’t building something to be released in the wild, it’s still important to think about the design of your code. Your future self will thank you.

SYN: A List Apart: The Full Feed: Writing Testable JavaScript

vakar, 12:00 pm, (bez komentāriem..)

We’ve all been there: that bit of JavaScript functionality that started out as just a handful of lines grows to a dozen, then two dozen, then more. Along the way, a function picks up a few more arguments; a conditional picks up a few more conditions. And then one day, the bug report comes in: something’s broken, and it’s up to us to untangle the mess.

As we ask our client-side code to take on more and more responsibilities—indeed, whole applications are living largely in the browser these days—two things are becoming clear. One, we can’t just point and click our way through testing that things are working as we expect; automated tests are key to having confidence in our code. Two, we’re probably going to have to change how we write our code in order to make it possible to write tests.

Really, we need to change how we code? Yes—because even if we know that automated tests are a good thing, most of us are probably only able to write integration tests right now. Integration tests are valuable because they focus on how the pieces of an application work together, but what they don’t do is tell us whether individual units of functionality are behaving as expected.

That’s where unit testing comes in. And we’ll have a very hard time writing unit tests until we start writing testable JavaScript.

Unit vs. integration: what’s the difference?

Writing integration tests is usually fairly straightforward: we simply write code that describes how a user interacts with our app, and what the user should expect to see as she does. Selenium is a popular tool for automating browsers. Capybara for Ruby makes it easy to talk to Selenium, and there are plenty of tools for other languages, too.

Here’s an integration test for a portion of a search app:

def test_search
fill_in('q', :with => 'cat')
find('.btn').click
assert( find('#results li').has_content?('cat'), 'Search results are shown' )
assert( page.has_no_selector?('#results li.no-results'), 'No results is not shown' )
end

Whereas an integration test is interested in a user’s interaction with an app, a unit test is narrowly focused on a small piece of code:

When I call a function with a certain input, do I receive the expected output?

Apps that are written in a traditional procedural style can be very difficult to unit test—and difficult to maintain, debug, and extend, too. But if we write our code with our future unit testing needs in mind, we will not only find that writing the tests becomes more straightforward than we might have expected, but also that we’ll simply write better code, too.

To see what I’m talking about, let’s take a look at a simple search app:

Srchr

When a user enters a search term, the app sends an XHR to the server for the corresponding data. When the server responds with the data, formatted as JSON, the app takes that data and displays it on the page, using client-side templating. A user can click on a search result to indicate that he “likes” it; when this happens, the name of the person he liked is added to the “Liked” list on the right-hand side.

A “traditional” JavaScript implementation of this app might look like this:

var tmplCache = {};

function loadTemplate (name) {
if (!tmplCache[name]) {
tmplCache[name] = $.get('/templates/' + name);
}
return tmplCache[name];
}

$(function () {

var resultsList = $('#results');
var liked = $('#liked');
var pending = false;

$('#searchForm').on('submit', function (e) {
e.preventDefault();

if (pending) { return; }

var form = $(this);
var query = $.trim( form.find('input[name="q"]').val() );

if (!query) { return; }

pending = true;

$.ajax('/data/search.json', {
data : { q: query },
dataType : 'json',
success : function (data) {
loadTemplate('people-detailed.tmpl').then(function (t) {
var tmpl = _.template(t);
resultsList.html( tmpl({ people : data.results }) );
pending = false;
});
}
});

$('<li>', {
'class' : 'pending',
html : 'Searching &hellip;'
}).appendTo( resultsList.empty() );
});

resultsList.on('click', '.like', function (e) {
e.preventDefault();
var name = $(this).closest('li').find('h2').text();
liked.find('.no-results').remove();
$('<li>', { text: name }).appendTo(liked);
});

});

My friend Adam Sontag calls this Choose Your Own Adventure code—on any given line, we might be dealing with presentation, or data, or user interaction, or application state. Who knows! It’s easy enough to write integration tests for this kind of code, but it’s hard to test individual units of functionality.

What makes it hard? Four things:

  • A general lack of structure; almost everything happens in a $(document).ready() callback, and then in anonymous functions that can’t be tested because they aren’t exposed.
  • Complex functions; if a function is more than 10 lines, like the submit handler, it’s highly likely that it’s doing too much.
  • Hidden or shared state; for example, since pending is in a closure, there’s no way to test whether the pending state is set correctly.
  • Tight coupling; for example, a $.ajax success handler shouldn’t need direct access to the DOM.

Organizing our code

The first step toward solving this is to take a less tangled approach to our code, breaking it up into a few different areas of responsibility:

  • Presentation and interaction
  • Data management and persistence
  • Overall application state
  • Setup and glue code to make the pieces work together

In the “traditional” implementation shown above, these four categories are intermingled—on one line we’re dealing with presentation, and two lines later we might be communicating with the server.

Code Lines

While we can absolutely write integration tests for this code—and we should!—writing unit tests for it is pretty difficult. In our functional tests, we can make assertions such as “when a user searches for something, she should see the appropriate results,” but we can’t get much more specific. If something goes wrong, we’ll have to track down exactly where it went wrong, and our functional tests won’t help much with that.

If we rethink how we write our code, though, we can write unit tests that will give us better insight into where things went wrong, and also help us end up with code that’s easier to reuse, maintain, and extend.

Our new code will follow a few guiding principles:

  • Represent each distinct piece of behavior as a separate object that falls into one of the four areas of responsibility and doesn’t need to know about other objects. This will help us avoid creating tangled code.
  • Support configurability, rather than hard-coding things. This will prevent us from replicating our entire HTML environment in order to write our tests.
  • Keep our objects’ methods simple and brief. This will help us keep our tests simple and our code easy to read.
  • Use constructor functions to create instances of objects. This will make it possible to create “clean” copies of each piece of code for the sake of testing.

To start with, we need to figure out how we’ll break our application into different pieces. We’ll have three pieces dedicated to presentation and interaction: the Search Form, the Search Results, and the Likes Box.

Application Views

We’ll also have a piece dedicated to fetching data from the server and a piece dedicated to gluing everything together.

Let’s start by looking at one of the simplest pieces of our application: the Likes Box. In the original version of the app, this code was responsible for updating the Likes Box:

var liked = $('#liked');

var resultsList = $('#results');



// ...



resultsList.on('click', '.like', function (e) {
e.preventDefault();

var name = $(this).closest('li').find('h2').text();

liked.find( '.no-results' ).remove();

$('<li>', { text: name }).appendTo(liked);

});

The Search Results piece is completely intertwined with the Likes Box piece and needs to know a lot about its markup. A much better and more testable approach would be to create a Likes Box object that’s responsible for manipulating the DOM related to the Likes Box:

var Likes = function (el) {
this.el = $(el);
return this;
};

Likes.prototype.add = function (name) {
this.el.find('.no-results').remove();
$('<li>', { text: name }).appendTo(this.el);
};

This code provides a constructor function that creates a new instance of a Likes Box. The instance that’s created has an .add() method, which we can use to add new results. We can write a couple of tests to prove that it works:

var ul;

setup(function(){
ul = $('<ul><li class="no-results"></li></ul>');
});

test('constructor', function () {
var l = new Likes(ul);
assert(l);
});

test('adding a name', function () {
var l = new Likes(ul);
l.add('Brendan Eich');

assert.equal(ul.find('li').length, 1);
assert.equal(ul.find('li').first().html(), 'Brendan Eich');
assert.equal(ul.find('li.no-results').length, 0);
});

Not so hard, is it? Here we’re using Mocha as the test framework, and Chai as the assertion library. Mocha provides the test and setup functions; Chai provides assert. There are plenty of other test frameworks and assertion libraries to choose from, but for the sake of an introduction, I find these two work well. You should find the one that works best for you and your project—aside from Mocha, QUnit is popular, and Intern is a new framework that shows a lot of promise.

Our test code starts out by creating an element that we’ll use as the container for our Likes Box. Then, it runs two tests: one is a sanity check to make sure we can make a Likes Box; the other is a test to ensure that our .add() method has the desired effect. With these tests in place, we can safely refactor the code for our Likes Box, and be confident that we’ll know if we break anything.

Our new application code can now look like this:

var liked = new Likes('#liked');
var resultsList = $('#results');



// ...



resultsList.on('click', '.like', function (e) {
e.preventDefault();

var name = $(this).closest('li').find('h2').text();

liked.add(name);
});

The Search Results piece is more complex than the Likes Box, but let’s take a stab at refactoring that, too. Just as we created an .add() method on the Likes Box, we also want to create methods for interacting with the Search Results. We’ll want a way to add new results, as well as a way to “broadcast” to the rest of the app when things happen within the Search Results—for example, when someone likes a result.

var SearchResults = function (el) {
this.el = $(el);
this.el.on( 'click', '.btn.like', _.bind(this._handleClick, this) );
};

SearchResults.prototype.setResults = function (results) {
var templateRequest = $.get('people-detailed.tmpl');
templateRequest.then( _.bind(this._populate, this, results) );
};

SearchResults.prototype._handleClick = function (evt) {
var name = $(evt.target).closest('li.result').attr('data-name');
$(document).trigger('like', [ name ]);
};

SearchResults.prototype._populate = function (results, tmpl) {
var html = _.template(tmpl, { people: results });
this.el.html(html);
};

Now, our old app code for managing the interaction between Search Results and the Likes Box could look like this:

var liked = new Likes('#liked');
var resultsList = new SearchResults('#results');



// ...



$(document).on('like', function (evt, name) {
liked.add(name);
})

It’s much simpler and less entangled, because we’re using the document as a global message bus, and passing messages through it so individual components don’t need to know about each other. (Note that in a real app, we’d use something like Backbone or the RSVP library to manage events. We’re just triggering on document to keep things simple here.) We’re also hiding all the dirty work—such as finding the name of the person who was liked—inside the Search Results object, rather than having it muddy up our application code. The best part: we can now write tests to prove that our Search Results object works as we expect:

var ul;
var data = [ /* fake data here */ ];

setup(function () {
ul = $('<ul><li class="no-results"></li></ul>');
});

test('constructor', function () {
var sr = new SearchResults(ul);
assert(sr);
});

test('display received results', function () {
var sr = new SearchResults(ul);
sr.setResults(data);

assert.equal(ul.find('.no-results').length, 0);
assert.equal(ul.find('li.result').length, data.length);
assert.equal(
ul.find('li.result').first().attr('data-name'),
data[0].name
);
});

test('announce likes', function() {
var sr = new SearchResults(ul);
var flag;
var spy = function () {
flag = [].slice.call(arguments);
};

sr.setResults(data);
$(document).on('like', spy);

ul.find('li').first().find('.like.btn').click();

assert(flag, 'event handler called');
assert.equal(flag[1], data[0].name, 'event handler receives data' );
});

The interaction with the server is another interesting piece to consider. The original code included a direct $.ajax() request, and the callback interacted directly with the DOM:

$.ajax('/data/search.json', {
data : { q: query },
dataType : 'json',
success : function( data ) {
loadTemplate('people-detailed.tmpl').then(function(t) {
var tmpl = _.template( t );
resultsList.html( tmpl({ people : data.results }) );
pending = false;
});
}
});

Again, this is difficult to write a unit test for, because so many different things are happening in just a few lines of code. We can restructure the data portion of our application as an object of its own:

var SearchData = function () { };

SearchData.prototype.fetch = function (query) {
var dfd;

if (!query) {
dfd = $.Deferred();
dfd.resolve([]);
return dfd.promise();
}

return $.ajax( '/data/search.json', {
data : { q: query },
dataType : 'json'
}).pipe(function( resp ) {
return resp.results;
});
};

Now, we can change our code for getting the results onto the page:

var resultsList = new SearchResults('#results');

var searchData = new SearchData();

// ...

searchData.fetch(query).then(resultsList.setResults);

Again, we’ve dramatically simplified our application code, and isolated the complexity within the Search Data object, rather than having it live in our main application code. We’ve also made our search interface testable, though there are a couple caveats to bear in mind when testing code that interacts with the server.

The first is that we don’t want to actually interact with the server—to do so would be to reenter the world of integration tests, and because we’re responsible developers, we already have tests that ensure the server does the right thing, right? Instead, we want to “mock” the interaction with the server, which we can do using the Sinon library. The second caveat is that we should also test non-ideal paths, such as an empty query.

test('constructor', function () {
var sd = new SearchData();
assert(sd);
});

suite('fetch', function () {
var xhr, requests;

setup(function () {
requests = [];
xhr = sinon.useFakeXMLHttpRequest();
xhr.onCreate = function (req) {
requests.push(req);
};
});

teardown(function () {
xhr.restore();
});

test('fetches from correct URL', function () {
var sd = new SearchData();
sd.fetch('cat');

assert.equal(requests[0].url, '/data/search.json?q=cat');
});

test('returns a promise', function () {
var sd = new SearchData();
var req = sd.fetch('cat');

assert.isFunction(req.then);
});

test('no request if no query', function () {
var sd = new SearchData();
var req = sd.fetch();
assert.equal(requests.length, 0);
});

test('return a promise even if no query', function () {
var sd = new SearchData();
var req = sd.fetch();

assert.isFunction( req.then );
});

test('no query promise resolves with empty array', function () {
var sd = new SearchData();
var req = sd.fetch();
var spy = sinon.spy();

req.then(spy);

assert.deepEqual(spy.args[0][0], []);
});

test('returns contents of results property of the response', function () {
var sd = new SearchData();
var req = sd.fetch('cat');
var spy = sinon.spy();

requests[0].respond(
200, { 'Content-type': 'text/json' },
JSON.stringify({ results: [ 1, 2, 3 ] })
);

req.then(spy);

assert.deepEqual(spy.args[0][0], [ 1, 2, 3 ]);
});
});

For the sake of brevity, I’ve left out the refactoring of the Search Form, and also simplified some of the other refactorings and tests, but you can see a finished version of the app here if you’re interested.

When we’re done rewriting our application using testable JavaScript patterns, we end up with something much cleaner than what we started with:

$(function() {
var pending = false;

var searchForm = new SearchForm('#searchForm');
var searchResults = new SearchResults('#results');
var likes = new Likes('#liked');
var searchData = new SearchData();

$(document).on('search', function (event, query) {
if (pending) { return; }

pending = true;

searchData.fetch(query).then(function (results) {
searchResults.setResults(results);
pending = false;
});

searchResults.pending();
});

$(document).on('like', function (evt, name) {
likes.add(name);
});
});

Even more important than our much cleaner application code, though, is the fact that we end up with a codebase that is thoroughly tested. That means we can safely refactor it and add to it without the fear of breaking things. We can even write new tests as we find new issues, and then write the code that makes those tests pass.

Testing makes life easier in the long run

It’s easy to look at all of this and say, “Wait, you want me to write more code to do the same job?”

The thing is, there are a few inescapable facts of life about Making Things On The Internet. You will spend time designing an approach to a problem. You will test your solution, whether by clicking around in a browser, writing automated tests, or—shudder—letting your users do your testing for you in production. You will make changes to your code, and other people will use your code. Finally: there will be bugs, no matter how many tests you write.

The thing about testing is that while it might require a bit more time at the outset, it really does save time in the long run. You’ll be patting yourself on the back the first time a test you wrote catches a bug before it finds its way into production. You’ll be grateful, too, when you have a system in place that can prove that your bug fix really does fix a bug that slips through.

Additional resources

This article just scratches the surface of JavaScript testing, but if you’d like to learn more, check out:

  • My presentation from the 2012 Full Frontal conference in Brighton, UK.
  • Grunt, a tool that helps automate the testing process and lots of other things.
  • Test-Driven JavaScript Development by Christian Johansen, the creator of the Sinon library. It is a dense but informative examination of the practice of testing JavaScript.
watt (watt)

watt: interesanti kā nopērkot..

vakar, 2:09 pm, 15 atsauksmes / atsauksmēm

interesanti kā nopērkot kādu tiešām labu mantu, gribas uzreiz nopirkt to pašu vēl vienu - lai gan skaidri zināms ka nav pēc otras vajadzības, bet nu tik laba manta!
puukas kuukas (piene_ne)

puukas kuukas (pajautaa): gribu picu

vakar, 2:34 pm, 15 atsauksmes / atsauksmēm

iesakiet labāko picu piegādātāju, lūdzu
izelpa (elpa)

izelpa (pajautaa): Vai kāds cibiņš vai..

vakar, 2:01 pm, 5 atsauksmes / atsauksmēm

Vai kāds cibiņš vai cibiņam draugs pēdējo pāris gadu laikā ir devies road-tripā uz/pa Ukrainu?
Saprotu, ka ceļi drausmīgi, bet cenšos aptaujāt visus iespējamos draudziņus par pieredzi. Interesētu ieteikumi, atstāsti par grūtībām, ar kādām nācās saskarties utt (bedres, policija, valoda, tirgi, uzmācīgi cilvēki, laipni cilvēki). Visa praktiskā informācija būs noderīga. Caur Baltkrieviju nedomājam braukt, tā ka turienes ieteikumi īsti laikam nav nepieciešami.

:)

Ja baigi gari un/vai negribas šeit, var rakstīt e-pastu uz miersmiers inboxā.

Nepabeigtā dienasgrāmata: Pēc Daft Punk ieteikuma..

vakar, 1:14 pm, 2 atsauksmes / atsauksmēm

Pēc Daft Punk ieteikuma tagad klausos agrīno Giorgio Moroder, lol

Nepabeigtā dienasgrāmata: divas ar puisi nedēļas

vakar, 1:03 pm, 3 atsauksmes / atsauksmēm

divas ar puisi nedēļas
muzungu (muzungu)

muzungu (pajautaa): Iesakiet labu vietu..

vakar, 10:09 am, 8 atsauksmes / atsauksmēm

Iesakiet labu vietu Vecrīgā, kur pavakariņot bez lielas drūzmas, skaļuma un tūristu bariem, par vakariņām divām personām iekļaujoties teiksim 20 Ls. Ja vēl ir laba alus un vīna izvēle, tad nu vispār smuki būtu. Noteikti negribas pabus. Iespēja pasēdēt ārā, ja ir labs laiks, būtu baigais plus.
osinsh (osinsh)

osinsh (pajautaa): ēstuves

vakar, 11:07 am, 8 atsauksmes / atsauksmēm

Aye!

Kādam nav zināmas labs ēstūzis Vīlandes ielas rajonā? Skola, old-school ēdnīca, kur par saprātīgām cenām var apēst kartupeļu biezputru ar kotletēm.
markizs (markizs)

markizs (pajautaa): Pašvaldību vēlēšanas

vakar, 10:57 am, 3 atsauksmes / atsauksmēm

Ir iespējams nobalot pašvaldību vēlēšanās no ārzemēm? Vai arī citā datumā?
nenopietns cilvēks (pzrk)

nenopietns cilvēks: bilde

vakar, 9:58 am, 10 atsauksmes / atsauksmēm

Nesen palaistais studentu satelīts EstCube atsūtījis pirmās fočenes no kosmosa (un uzreiz tiek uz Postimees vāka):
http://www.postimees.ee/1242204/estcube-1-foto-maast-on-ule-ootuste-kvaliteetne

Interesanti, ka kameras izstrādāšana esot bijis kāda studenta bakalaura darbs. Mans bakalaura darbs ne tuvu nelido kosmosā un vispār nemāk uzņemt fotogrāfijas.
ausos: Queen - Don't Stop Me Now - open.spotify.com/track/5eK3EGtSzTz9eEcPFwE37E

Nepabeigtā dienasgrāmata: APOLLO. Laikraksts norāda -..

vakar, 9:55 am, 2 atsauksmes / atsauksmēm

APOLLO. Laikraksts norāda - nav izslēgts, ka topošie absolventi varētu paši sev izdomāt diagnozes un pārliecināt par tām arī ārstus – ka, piemēram, uztraukumā reibst galva, trūkst elpas un nevar padomāt. Organiska trauksme, viegli kognitīvi traucējumi, garastāvokļa vai personības traucējumi, neprecizēta reakcija uz smagu stresu un citi - ārsti atzīst, ka šādas kaites nav viennozīmīgi pierādāmas.
None of the Above (artis)

None of the Above: A commercial CA protects you..

vakar, 12:49 am, atsauksmēm

A commercial CA protects you from anyone whose money it refuses to take. Matt Blaze

http://iang.org/ssl/pki_considered_harmful.html
es pats personīgs (maigs)

es pats personīgs: kamēr es ar E. pa Londonu ar..

vakar, 12:12 am, atsauksmēm

kamēr es ar E. pa Londonu ar fiksīšiem braukājām (kādi stilīgi jaunieši, bļe, es ne Latvijā, ne Holandijā ar fiksīti nebraukāju, taisni zirgam jāsmejās) viņš naktī parkā pēc pirmās chardonnay pudeles izmeta pērli:
"Ja gribās dabūt kaut ko tādu, kas tev nekad nav bijis, tad esi gatavs darīt (un dari!!) to, ko nekad neesi darījis"
nenopietns cilvēks (pzrk)

nenopietns cilvēks: sērfo no dīvāna

aizvakar, 11:32 pm, 10 atsauksmes / atsauksmēm

Ja vēl neesi dzirdējis par Geoguessr, tad ir pēdējais laiks uzzināt un pamēģināt. Viss ir ļoti vienkārši — tev atver Google Street View (bez kartes), un tu "staigā" pa apkārtni un mēģini uzminēt, kurā pasaules vietā esi izmests. Reizēm tevi izmet nekurienes vidū, reizēm Kanādā Austrālijā Krievijā, bet reizēm tajā nekurienes vidū paveras elpu aizraujoši skati un Street View esamība ļauj tās brīnumainās vietas izpētīt vēl (mazliet) vairāk.

Kur es šodien šādi "paviesojos":
Japāņu cietoksnis: http://goo.gl/maps/Lb7xC
Sardīnijas piekraste: http://goo.gl/maps/QFUKM

Nepabeigtā dienasgrāmata: APOLLO. Katrai pasaules..

aizvakar, 11:03 pm, 1 atsauksme / atsauksmēm

APOLLO. Katrai pasaules valstij ir savas sabiedrības veselības prioritātes. ASV tā ir aptaukošanās epidēmija, Brazīlijai– narkomānija jauniešu vidū, Taizemei– notekūdeņu sanitārija, Japānai– psihosomātiskas bailes no zemestrīcēm un nukleāras katastrofas, Latvijai– bērnu un jauniešu alkoholisms un smēķēšana.

Nepabeigtā dienasgrāmata: sievišķi smagi noziegumi

aizvakar, 10:55 pm, atsauksmēm

sievišķi smagi noziegumi
es pats personīgs (maigs)

es pats personīgs: divas lietas. 1. manai cibai..

aizvakar, 9:47 pm, 3 atsauksmes / atsauksmēm

divas lietas.
1. manai cibai drīz būs 10 gadu (jēzus soldenais!!!)
2. mans jaunākais mīlākais izteiciens ir yolo-yolo. lietošu blā-blā vietā, pieliekot vēl klāt "jā"
tad tiem holandiešiem teikšu
"jaaaaa, jaaa... yolo-yolo"
es pats personīgs (maigs)

es pats personīgs (pajautaa): Sveiki cibieši, Kāds ir..

aizvakar, 9:27 pm, 1 atsauksme / atsauksmēm

Sveiki cibieši,

Kāds ir patstāvīgi kārtojis vīzu uz Apvienotajiem Arābu Emirātiem? Ne caur tūrisma aģentūru?
Paldies!
tēmas: vīza
Pātaru Ansis (kuminjsh)

Pātaru Ansis: Pagrabainais puksts

aizvakar, 9:56 pm, 6 atsauksmes / atsauksmēm

Iespējams, ka iepriekšējā dzīvē esmu bijusi rūķis, jo man neiedomājami patīk tuneļi, metro un velvēti pagrabi.

... tālāk ... )
Pātaru Ansis (kuminjsh)

Pātaru Ansis: Pilis un baznīcas (kulturēlais puksts)

aizvakar, 9:40 pm, 4 atsauksmes / atsauksmēm

Pātaru Ansis (kuminjsh)

Pātaru Ansis: Šampanieša avēnija (puksts veltīts visiem Cibas snobiem)

aizvakar, 9:26 pm, 4 atsauksmes / atsauksmēm

Jā, tā nu tā iela patiešām saucas!;)


... tālāk ... )
Pātaru Ansis (kuminjsh)

Pātaru Ansis: Un vēl tas, kas bija pirms nedēļas (botāniskais puksts)

aizvakar, 9:21 pm, 8 atsauksmes / atsauksmēm

Vakar sataisīju pukstu par manu Europas braucienu - ar kādām 30 bildēm, kaut ko drusku ne tā izdarījos, uuun - tadāmmm, viss pazuda. Sapratu, ka vakar: ņe suģba, tāpēc atliku uz šodienu. Bet nu lai nesataisītu brīnumus šodien, būs vairāki bilžaini puksti;)
Pirmais - botāniskais: Jums ir kāda nojausma, kādiem kokiem (jā, kokiem!) pieder šie ziedi?
... tālāk ... )

SYN: A List Apart: The Full Feed: This week's sponsor: Igloo Software

aizvakar, 5:00 pm, (bez komentāriem..)

If SharePoint and Facebook had a baby, it would look a lot like Igloo (without the recessive balding or oversharing tendencies). Hosted, managed and with a major release every 90 days, Igloo is a whitelabel content platform built for today’s needs. We also have Sandwich Video.

Floristiņš (iztulko): kā latviski skan spinal..

aizvakar, 8:16 pm, 3 atsauksmes / atsauksmēm

kā latviski skan spinal disc herniation?

Nepabeigtā dienasgrāmata: prieka mājas lapa

aizvakar, 6:28 pm, 1 atsauksme / atsauksmēm

prieka mājas lapa
MaKo (mako)

MaKo: Negaiss

aizvakar, 6:26 pm, 4 atsauksmes / atsauksmēm

Negaiss staigā apkārt un nenāk klāt. Gribas spirgtumu un lai lietus nomazgā putekļus.