Repository navigation
Expand file tree
/
Copy pathatom.xml
More file actions
executable file
·584 lines (433 loc) · 64.7 KB
/
Copy pathatom.xml
File metadata and controls
executable file
·584 lines (433 loc) · 64.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[Zach Lendon's Blog]]></title>
<link href="http://zachlendon.github.com/atom.xml" rel="self"/>
<link href="http://zachlendon.github.com/"/>
<updated>2014-02-12T01:22:23-06:00</updated>
<id>http://zachlendon.github.com/</id>
<author>
<name><![CDATA[Zach Lendon]]></name>
<email><![CDATA[zach.lendon@gmail.com]]></email>
</author>
<generator uri="http://octopress.org/">Octopress</generator>
<entry>
<title type="html"><![CDATA[Starbucks' Big Missed Opportunity]]></title>
<link href="http://zachlendon.github.com/blog/2014/02/12/starbucks-big-missed-opportunity/"/>
<updated>2014-02-12T00:10:00-06:00</updated>
<id>http://zachlendon.github.com/blog/2014/02/12/starbucks-big-missed-opportunity</id>
<content type="html"><![CDATA[<p>Starbucks stores have traditionally been both an individual workhaven and a meeting place for professionals. However, with limited seating, uncomfortable chairs, an often loud environment, and limited outlets, Starbucks is not in the business of providing their customers a coworking location. However, they should be.</p>
<p>Coworking is a trend that is growing in strength and revenue for companies such as <a href="http://www.regus.com">Regus</a>. Professionals are <a href="http://www.fastcompany.com/3004788/future-coworking-and-why-it-will-give-your-business-huge-edge#4">not only reporting improved performance, creativity and self-confidence in these workspaces, they are reporting that they are feeling healthier in them as well</a>. Coworking spaces provide not only desk areas for individuals to work at, but often meeting spaces, copier/printer services, and more. <a href="http://www.mycafeinc.com">Cafe Inc</a>, a Minneapolis-based startup, joins a growing trend nationally of “proworking” locations - coworking locations that provide additional amenities such as a lounge, cafe, and coffee shop area alongside traditional coworking amenities (<a href="https://www.facebook.com/mycafeinc/photos_stream?ref=br_tf">photos</a>).</p>
<p>Most Starbucks stores as currently constructed do not lend themselves to coworking or proworking from their customers. However, Starbucks has shown an interest in branching into the “cafe” space as they continue to expand their food offerings. Starbucks also already offers limited lounge areas in many of their locations. Having stores that have traditional seating areas as they exist now, but also premium spaces where Starbucks could upsell professional coworking services, especially on a day pass basis, can meet a market need that is currently going largely unfulfilled. Many current Starbucks locations could not support this model, requiring either new locations to be built with this in mind, or retrofitting existing locations (one’s that are not standalone where accompanying space could be acquired). Starbucks has shown a willingness to tweak their brand and innovate of late, and this is an opportunity for them to become even more ingrained in their customers’ daily lives. Workers who show a willingness, at the spur of the moment (or even planned in advance) to pay to have their business meeting in private, or use a secured wifi in a more private, comfortable setting for their own work, also would be likely to buy more beverages and food from the Starbucks store adjacent to their short-term professional work area. Shouldn’t Starbucks be in the business of meeting their customers’ needs in a way that also maximizes Starbucks’ potential profit? If they don’t, I believe other businesses will step into their place and not only cement a missed opportunity for Starbucks, but slowly start to erode at portions of their core business.</p>
<p>Professionals already are going into Starbucks to conduct business and to work - and Starbucks wants that, to a point. Starbucks should consider embracing what their customers are already doing and better execute at profiting off of it by providing proworking services in the not too distant future.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Grails Event Push real-world notes]]></title>
<link href="http://zachlendon.github.com/blog/2013/07/22/grails-event-push-real-world-notes/"/>
<updated>2013-07-22T14:32:00-05:00</updated>
<id>http://zachlendon.github.com/blog/2013/07/22/grails-event-push-real-world-notes</id>
<content type="html"><![CDATA[<p>Today’s excellent talk by Colin Harrington at <a href="gr8conf.us">GR8ConfUS</a> on ASync and in particular the Events Push plugin has me wanting to note some thoughts I’ve developed through using the plugin at my current client.</p>
<p>First off, I use the following BuildConfig.groovy definition, so that I can exclude the resources plugin version used by the plugin:</p>
<pre><code> compile("org.grails.plugins:events-push:1.0.M7") {
excludes 'resources', 'org.atmosphere:atmosphere-runtime'
}
</code></pre>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[A Sencha Dirty Store Records Quirk]]></title>
<link href="http://zachlendon.github.com/blog/2013/07/01/a-sencha-dirty-store-records-quirk/"/>
<updated>2013-07-01T10:07:00-05:00</updated>
<id>http://zachlendon.github.com/blog/2013/07/01/a-sencha-dirty-store-records-quirk</id>
<content type="html"><![CDATA[<p>Sencha provides some pretty slick out of the box grid capabilities, one of which, editing, is demonstrated in the ExtJS 4 example <a href="http://dev.sencha.com/deploy/ext-4.0.1/examples/grid/cell-editing.html">shown here</a>.</p>
<p>One example of how Sencha does a great job making the easy hard and the difficult easy (just an opinion I’ve fashioned over the past several months) is the handling of editing empty cell values. For one of the apps I work on, I read XML (yes, awesome I know) from a web service data using <a href="http://groovy.codehaus.org/modules/http-builder/">Groovy’s HTTPBuilder</a>, which under the covers uses <a href="http://groovy.codehaus.org/api/groovy/util/XmlSlurper.html">XmlSlurper</a> to parse said data. When the data is empty, it is by default parsed and represented as “”. When you edit a field in the Sencha grid however, it gets changed to be represented as null. This therefore marks a record as modified in the Sencha store, when it really hasn’t been. Probably the proper approach to deal with this is to write logic in the Sencha <a href="http://docs.sencha.com/extjs/4.1.3/#!/api/Ext.data.Store-event-update">store update event</a> to check modified records for this “”/null shenanigans and not allow the update on the modified record to occur. Once you start making modifications like this for several store objects you’ll probably find it best just to get your OOP on and create your own Store object that extends Sencha’s with goodies like this.</p>
<p>Ultimately Sencha should in my opinion not mark these records as modified for you. But it is just another example how Sencha is great at doing heavy lifting but leaves lots of little pieces around for you to deal with - many which you probably shouldn’t have to.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Updated ExtJs4 Mock Ajax Library for Jasmine]]></title>
<link href="http://zachlendon.github.com/blog/2013/04/02/updated-extjs4-mock-ajax-library-for-jasmine/"/>
<updated>2013-04-02T14:17:00-05:00</updated>
<id>http://zachlendon.github.com/blog/2013/04/02/updated-extjs4-mock-ajax-library-for-jasmine</id>
<content type="html"><![CDATA[<p><a href="https://twitter.com/kenspirit">@kenspirit</a> does a nice job in this series (<a href="http://www.thinkingincrowd.me/blog/2012/08/13/extjs-jasmine-unit-test-part-1-philosophy-and-test-for-store/">part 1</a> and <a href="http://www.thinkingincrowd.me/blog/2012/08/30/extjs-jasmine-unit-test-part-2-ajax-behavior-2/">part 2</a>) of blog posts talking about some of the pain points (Stores, Ajax handling) of unit testing ExtJS applications. While <a href="https://twitter.com/kenspirit">@kenspirit</a> provides a nice adaptation of <a href="https://github.com/pivotal/jasmine-ajax">jasmine-ajax</a> for ExtJs (as jasmine-ajax only supports JQuery and Prototype currently), that adaptation does not work with ExtJS4. The following <a href="https://gist.github.com/zachlendon/5295365">updated version should work - at least for basic Ajax mocking</a>. Let me know if you have issues and I can (futher) update and improve upon this latest adaptation.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[ExtJS4: Fun (not really) with styling checkboxes in grids]]></title>
<link href="http://zachlendon.github.com/blog/2013/03/27/extjs4-fun-not-really-with-styling-checkboxes-in-grids/"/>
<updated>2013-03-27T23:57:00-05:00</updated>
<id>http://zachlendon.github.com/blog/2013/03/27/extjs4-fun-not-really-with-styling-checkboxes-in-grids</id>
<content type="html"><![CDATA[<p><a href="http://www.sencha.com/blog/optimizing-ext-js-4-1-based-applications">‘boxready’</a> is a good event to listen to to change checkbox CSS styles programmatically when using <a href="http://docs.sencha.com/ext-js/4-1/#!/api/Ext.selection.CheckboxModel">CheckboxModels</a> in an extJS4 panel where the ‘grid’ is defined as an ‘item’ in a panel. When you are defining an actual ‘grid’ component though (i.e., when you are doing more than simply tying an extJS ‘store’ to a standard grid component via an item) and want to change checkboxes within a grid listener, you’ll want to use the ‘viewready’ event instead - with a ‘defer’ to boot. I admit that it seems that there should be another event that one could use and not have to do a ‘defer’ timing hack, but so far my attempts at trying other events have proven fruitless (I’d love to be advised differently). All of these events in question are shown in <a href="http://docs.sencha.com/ext-js/4-1/source/AbstractView.html#Ext-view-AbstractView">AbstractView’s source</a>, and when using extJS4+ it’s worthwhile to understand all that goes on in this class, especially event-wise.</p>
<p>The reason one needs to do this styling logic within these events is that the style changes must be applied after everything in your component is visible and any styles are calculated and applied to elements within that component. While you would think you could do these style settings when you instantiate the CheckboxModel and have them honored, and there are random illusions to this working online - you will find that it will fail you under certain scenarios. As an example, ‘headerConfig’ in CheckboxModel has a headerWidth property. Howevever, if you look at <a href="https://code.google.com/p/extjs4/source/browse/trunk/ext4/extjs/src/selection/CheckboxModel.js?r=3">CheckboxModel’s source</a>, setting the config does not appear to actually change it’s value (and in practice this is what I’ve seen). In certain grid scenarios, depending especially on the ‘flex’ property of other columns, you may (ok - will) find ExtJS4+ re-sizing your checkbox columns to excessive sizes - usually on the big end, but potentially too small based on your requirements.</p>
<p>While it is possible and in practice probably better ‘code quality’ to actually get the grid or panel component (using Ext.getCmp() - by id) and then access the CheckboxModel using a ComponentQuery selector - and then change it’s width, another approach is to do it in a more JQuery-like fashion (in Sencha syntax of course). I quite honestly find this to be a bit easier to do, and when working with Sencha, sometimes easy is really welcomed. This is also satisfactorily safe to do in my opinion if you don’t have lots of elements on a page such that the querying performance of these operations will impact the usability of your app. With a pretty complex app I’ve not seen the below queries suffer performance-wise:</p>
<div><script src='https://gist.github.com/5260802.js?file='></script>
<noscript><pre><code></code></pre></noscript></div>
<p>The above code would change the checkboxes in the first row of a grid - the first item finding the styled header checkbox (so you can select all rows) and the second loop finding all rows and grabbing the first element from that row. Yes, you don’t need the ‘each’ necessarily in the first scenario, but it doesn’t hurt anything either.</p>
<p>While extJS4 and Sencha Touch continue to prove to be very powerful frameworks, understanding their nuances and pain points continues to be an interesting journey to be embarking upon.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Thoughts on Testem vs Testacular (Karma)]]></title>
<link href="http://zachlendon.github.com/blog/2013/03/26/quick-thoughts-on-testem-vs-testacular-karma/"/>
<updated>2013-03-26T00:25:00-05:00</updated>
<id>http://zachlendon.github.com/blog/2013/03/26/quick-thoughts-on-testem-vs-testacular-karma</id>
<content type="html"><![CDATA[<p>Both <a href="http://karma-runner.github.com/0.8/index.html">Testacular</a> - recently renamed ‘Karma’ and <a href="https://github.com/airportyh/testem">Testem</a> are great test runners for improving your javascript unit testing workflow. While Karma was developed as part of AngularJS, it is certainly useful as a test runner for javascript unit tests regardless of frameworks and/or libraries leveraged. Key features that both Testem and Karma have include:</p>
<ul>
<li>Support for running/driving multiple browsers simultaneously, including headless browsers (i.e., PhantomJS)</li>
<li>Support for the main testing libraries out there - QUnit, Jasmine, Mocha, etc.</li>
<li>Both will watch the source/test files (that you selectively configure) for changes and automatically re-run tests</li>
<li>Both provide support for local and CI use</li>
<li>Both are terminal focused</li>
</ul>
<p>For me I have found that I have a slight preference for Testem, for a few reasons. One is the Text User Interface:</p>
<p><img src="http://zachlendon.github.com/images/green.png"></p>
<p>which allows you to better visually see the test results by browsers, whereas Testacular provides this as straight output text lines:</p>
<p><img src="http://zachlendon.github.com/images/output.png"></p>
<p>Secondly, the ability to also run tests from a browser is a nice alternative when the terminal is not providing you as much flexibility as you want in certain testing scenarios and development workflows. Configuring this option alongside testem is much more doable than alongside Karma. The minimal configuration that is required is described in the ‘Node Travelers’ Toolchain blogpost (in the <a href="http://blog.nodetraveller.com/Javascript/My-javascript-testing-toolchain.html">icing on the cake section</a>). As is the case in the terminal, the main benefit over a normal Jasmine/browser TDD workflow is that with this testem integration we get watching of changes and automatic reload of the browser on changes. As a small aside, I’ve always found the <a href="https://github.com/esbie/jasmine-bootstrap">Jasmine Bootstrap Reporter</a> to be a great reporter for browser Jasmine reports and significantly better than the default Jasmine Html reporters.</p>
<p><img src="http://zachlendon.github.com/images/jasmineHtml.png"></p>
<p> Do note that when using testem with Jasmine that you may have to slightly modify your Jasmine javascript source to ensure that the #testem hashtag is honored when choosing to run a suite of tests or individual tests from the browser.</p>
<p>On the Karma side, I must admit that Karma does seem to have nice debugging integration into Webstorm IDE, an IDE that I’ve tried but never really used extensively. I also know Sublime Text 2 has <a href="http://sokolovstas.github.com/SublimeWebInspector/">similar javascript debugging support available of late</a>, so it’s possible that Karma’s debugging support is stronger than Testem and that leveraging it on an ongoing basis would prove its value. You’ll note in the Karma documentation “video” that support for “dumping” object state, console logging to the terminal, etc. provides a pretty strong workflow from Karma for those who want their javascript development TDD workflow view to look like a Sublime Text editor on one side of the screen and a terminal window on the other. This editor/terminal workflow is philosophically inline with what Testem is aiming for as well though, so I’m not sure you’re really losing this if your workflow fits one IDE/coding/execution paradigm vs. any other.</p>
<p>Ultimately both Karma and Testem will work for your javascript unit testing workflows and they are better than the alternative - no javascript unit tests and/or no javascript unit test runner. I have found evidence that helps confirm my impression that Testem seems to have been around longer as a project (and thus be a bit more mature), have a bit better documentation, and be more widely used. I’d also be remiss not to say that I have also found it mildly annoying that Google Search Result links to “Testacular” Google Groups posts are “dead ends”, as the group has been renamed (to Karma). When there is ultimately such a small difference between a set of frameworks and/or libraries, it is to me these little things that can add up and - for now - would make me lean towards and recommend Testem. That being said, hopefully we’ll continue to see innovation from both of these tools and therefore hopefully the story on their usefulness and viability is only in the early stages.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Embrace your Javascript Overlords]]></title>
<link href="http://zachlendon.github.com/blog/2013/01/15/give-in-to-javascript/"/>
<updated>2013-01-15T22:53:00-06:00</updated>
<id>http://zachlendon.github.com/blog/2013/01/15/give-in-to-javascript</id>
<content type="html"><![CDATA[<p>My first job out of college was working on OfficeMax.com (for OfficeMax) in 1999, where I primarily wrote a combination of client-side code and server-side javascript, run on Netscape Enterprise Server. For several years thereafter, I attempted to run far away from this ‘javascript’ world, as javascript at the time seemed to be a mess to deal with (this is well pre-JQuery, let alone all the other javascript libraries/frameworks of today), and languages such as Java were where it was happening. I can remember going to Java One for a few years in the early 2000’s and the buzz there was very WWDC-like. It seems hard to believe in this day and age I’m sure. But it’s indicative of the tech industry being both cyclical and the fact that today’s hot technologies are tomorrow’s not quite so cool (but still widely used) technologies.</p>
<p>In the past few years I’ve strived to leverage the Java and object-oriented knowledge I gained from several years prior with other dynamic JVM languages and frameworks, as well as pried my way onto native and mobile web initiatives/projects. Aside from native mobile application work, I’ve found that working with javascript has been best at providing me with an ever-increasing amount of development enjoyment. The innovation in the space is often mind-boggling, and many of the solutions I run across are amongst some of the most elegant libraries and frameworks around today.</p>
<p>That being said, integrating client-side javascript libraries and frameworks with non-javascript-friendly (more on that in a moment) back-ends produces a set of challenges. There’s state synchronization, rather manual synchronizing of changes, wiring together script packages/packaging, CSS compilers, code minifiers, client-side MV+ frameworks, templating engines, client-side history, ORM, database, etc. And that’s just for starters. While this certainly can be managed by seasoned developers, after doing the work of adding all these pieces, wiring them together, testing them and maintaining them, one at <em>some point</em> has to ask themselves: “is this the best way to be doing this?”</p>
<p>I’ve long since asked the questioned and told myself “no” many times. That being said, few web projects are greenfield and rarely - or basically <em>never</em> - are decisions that drive technologies used at companies politics-free. Certainly though I’ve reached a level of exasperation with it. Frameworks such as <a href="http://derbyjs.com/">Derby</a>, <a href="https://github.com/socketstream/socketstream">Socketstream</a> and <a href="http://meteor.com/">Meteor</a> are either built upon or provide out-of-the-box (or optional yet rather easy) integration with popular libraries such as Node.js, Express, Socket.IO, Browserify and MongoDB. And many more. One of the challenges I see in the midwest as a developer is that there has been <em>so</em> much investment made my organizations and developers in the Java stack, and to a lesser extent Rails, that moving to these other stacks is an enormous challenge. There’s misperceptions out there I’m sure that provide excuses for resistance: performance issues, documentation issues, SEO issues, maturity, etc. As I similarly alluded to earlier, these are the same stories that get thrown out in the early part of any adoption cycle for impending technology trends. Some of them have validity to a degree, but they are widely overblown. From my vantage point, being that I’m a strong believer in the “realtime” web replacing the “dynamic” web we see today, platforms such as Node.js - or Vert.x - are our web application platforms of - at the very least - the not too distant future. And what language works on all these platforms, and all of the frameworks I mentioned above? Javascript. That’s why I say embrace it. That’s why I pushed in some talks I gave last year to “learn it” - to understand it - and most importantly, to know how to use it properly.</p>
<p>I’m hopeful in 2013 that I can - at the very least - help push the conversation at local companies and with local developers in my area forward on the types of technology stacks I’ve mentioned above. I have ideas for talks, blog posts and demo apps (not chat apps…) ready to be explored, to excite others, to help show the possibilities and dispel the myths. In short, I’m looking forward to helping others embrace our javascript overlords. If the interest and ideas are out there, I would certainly be very interested in joining forces with other local developers in this fight as well. We can either whine about the state of affairs at clients and companies (a trap I personally at times fall into), or we can actively work to show why there is a better way.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Using Tincr with Grails for Live Client-Side Reloading]]></title>
<link href="http://zachlendon.github.com/blog/2012/11/16/using-tincr-with-grails-for-live-reloading/"/>
<updated>2012-11-16T23:32:00-06:00</updated>
<id>http://zachlendon.github.com/blog/2012/11/16/using-tincr-with-grails-for-live-reloading</id>
<content type="html"><![CDATA[<p>There’s various solutions out there for seeing client-side changes quickly in a browser. One such solution, <a href="http://livereload.com/">livereload.com</a>, was mentioned by Ted Naleid in <a href="https://twitter.com/tednaleid/status/269105419274813440">this tweet</a>. While I don’t have much experience with livereload, I’m not completely convinced I’m doing it wrong (as he suggests somewhat tongue-in-cheek) either (though it wouldn’t be the first time I’ve been wrong). I have been using another solution, and I wanted to share with you how to start using it with your own Grails application, if you so choose. The solution I have been leveraging for live-reload-“like” functionality is the Chrome extension <a href="http://tin.cr/">Tincr</a>. This post attempts to give you a quick guide to leveraging Tincr with your Grails 2.x application and talk briefly about how it helps you iterate your client-side development efforts quicker.</p>
<p>Once you install the Chrome extension, it will show up as a tab in your Chrome Developer Tools view. <img src="http://s8.postimage.org/5eas13ujn/Screen_Shot_2012_11_16_at_11_38_02_PM.png" alt="your Chrome Developer tools, like so:" /></p>
<p>You’ll notice that I choose the Configuration File Option in Tincr:</p>
<p><img src="http://s13.postimage.org/oop700vtv/Screen_Shot_2012_11_16_at_11_41_23_PM.jpg?noCache=1353130755" alt="'Configuration File option'" />.</p>
<p>This allows me to customize the mapping, ideally through regular expressions, between the project’s resource files and where they are located in my project. I do this mapping because I have had issues with it working simply with an http web server, though others might have better success with one of the other pre-configured options.</p>
<p>Nevertheless, here’s an example tincr.json file, which you need to put right under the web-app folder of your project.</p>
<div><script src='https://gist.github.com/4093607.js?file='></script>
<noscript><pre><code>{
"toFile" : [
{"from": "/js/(.+\\.js)",
"to": "/js/$1"},
{"from": "/css/(.+\\.css)",
"to": "/css/$1"}
]
}</code></pre></noscript></div>
<p>As you can see, this basic JSON simply maps js and css resources under the web-app folder, which I set as the Tincr ROOT folder in Chrome, to the project’s js/ and cs/ folders. It works recursively for those directories as well. You can of course get more fancy depending on how your project’s resources are defined.</p>
<p>One ‘gotcha’ to watch out for is resource bundling. To get this to work (at least without major pains), I turn resource bundling off in the Grails 2.x apps I use Tincr in by adding:</p>
<p>grails.resources.debug=true</p>
<p>in the development environment config section of my project’s Config.groovy file. While this adds additional parameters to my resource files, it breaks them out of Grails’ default bundling strategy and more easily allows Tincr to do its magic.</p>
<p>As for that magic, Tincr allows me to make changes to javascript or CSS files in the browser (in Chrome Developer Tools), use Cmd+S (save shortcut, this being the Mac version of a save shortcut), and have the changes be saved back to the file system (and viewable instantly in my IDE). On the reverse side, as you can see in the <a href="http://tin.cr/docs.html">Tincr documentation</a>, you can define a ‘fromFile’ JSON attribute, which will allow you to save a file in your favorite IDE and have Chrome bring in the changes <em>without</em> reloading the page in the browser. Luckily, in this simple configuration example, Tincr is smart enough to reverse-engineer the ‘fromFile’ mapping, so defining it is redundant, and I have therefore not done so.</p>
<p>So hopefully this provides you an impetus and a guide to start to “tinkering” (you knew it was coming…) with <a href="http://tin.cr/">Tincr</a> in your Grails application!</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Four End User Advantages of HTML5 Apps for Mobile Devices]]></title>
<link href="http://zachlendon.github.com/blog/2012/11/14/end-user-advantages-of-html5-apps-for-mobile-devices/"/>
<updated>2012-11-14T22:45:00-06:00</updated>
<id>http://zachlendon.github.com/blog/2012/11/14/end-user-advantages-of-html5-apps-for-mobile-devices</id>
<content type="html"><![CDATA[<p>Rarely a week goes by where there is not another article about “HTML5 Mobile Webapps vs. Native Apps.” Like criticizing Apple, these articles are great at generating traffic (and money?) for the hosting website but often settle little and rarely provide much value for either audience. That being said, seemingly everyone has an opinion on these topics, so don’t expect the articles to end anytime soon. Before I take a stand on one side of the aisle - in order to rebutt points in a specific article I’ll mention shortly, I should preface this post by saying that I hate the ‘vs.’ argument of mobile apps - I believe they both have their time and place, and for enterprise customers I often think “both” is the correct answer. With that out of the way - the latest in the line of these ‘vs.’-style articles that was brought to my attention today was Jeffrey Sambells post: <a href="http://jeffreysambells.com/2012/11/14/on-building-html5-apps-for-mobile-devices">“On Building HTML5 Apps for Mobile Devices”</a>. While discussing the article point by point is <em>very tempting</em> - such as the incorrect summarization of Facebook’s current stance on HTML5 (they still get much more non-native mobile traffic than native, and HTML5 is still very much in play at Facebook) - the point in the article I want to address is:</p>
<ul>
<li>Where’s the end user advantages (for mobile web)?</li>
</ul>
<p>Well here they are - a list of 4 of the top “end user advantages” for mobile web applications:</p>
<ol>
<li>Mobile browsers crash less frequently than your native app. Users get pretty annoyed when apps crash.</li>
<li>Not everyone wants to download an app. ~30% of mobile users have <em>never</em> downloaded <em>any</em> app. If they don’t want to download your app, but want to use your product on their mobile device, having a mobile web app <em>is</em> your other option.</li>
<li>Some native applications will not work on your device - or don’t exist for your device. If you are using an older iOS version, or certain Android devices/OS’s (for example) - or are part of the <a href="https://twitter.com/search?q=%23wearethe3percent">#wearethe3percent Window Phone</a> crowd or 1 of the 30 people still using Blackberry devices, then mobile web apps are often your only option to reach these users.</li>
<li>Some use cases are better suited for mobile web applications. This Mashable article <a href="http://mashable.com/2012/06/06/mobile-site-mobile-app-infographic/">under the Content Usage section</a> does a decent job of summarizing such use cases. Additionally, people who are travelling, especially in slower bandwith areas, will often be able to more quickly access the information this type of information via a mobile web app.</li>
</ol>
<p>There are definitely points in Jeremy’s article that I very much agree with - including the mythical fallacy: “I can just generate a native app from my mobile webapp using product X and it’ll be great!” In the end, mobile and native aren’t going away anytime soon, and there are very strong arguments behind, and reasons for, leveraging each approach as part of an overall mobile strategy.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Log4Javascript - Quick Intro and a LocalStorage Custom Appender]]></title>
<link href="http://zachlendon.github.com/blog/2012/11/12/log4javascript-local-storage-custom-appender/"/>
<updated>2012-11-12T20:52:00-06:00</updated>
<id>http://zachlendon.github.com/blog/2012/11/12/log4javascript-local-storage-custom-appender</id>
<content type="html"><![CDATA[<p>With the continuing shift to the client-side for more and more processing in today’s web applications, be they mobile-specific or not, effective logging of the running state of the client-side part of your application is critical. There are a bevy of different solutions to your client-side logging needs, and I present this customization of <a href="http://log4javascript.org/">Log4Javascript</a> as not an endorsement of <a href="http://log4javascript.org/">Log4Javascript</a> as any sort of holy grail - but it is inevitably a demonstration that it is a viable, customizable solution that you should consider if you are working on a project that has needs in this area.</p>
<p><a href="http://log4javascript.org/">Log4Javascript</a> comes with a collection of appenders and a grouping of logging levels that gives you the type of logging you’ve probably grown accustomed to in your server-side development efforts. Hop over to this JSFiddle and look at a sample Hello World type example that would output a log message to your <a href="http://jsfiddle.net/QpK4t/10/">Browser Console</a></p>
<p>Beyond having appenders that write to the console, out of the box, <a href="http://log4javascript.org/">Log4Javascript</a> includes appenders that write to popups, alert, and submit ajax requests to the server. Using any combination of them are pretty simple operations - they include different options, and can be combined to provide a flexible yet powerful logging strategy.</p>
<p>The proposed LocalStorageAppender I reference adds to this toolkit by providing the ability to store log messages in a browser’s LocalStorage, if available. This can be an effective way to store messages for later use, if needed. For example, if you get an error later in the running of your application, wouldn’t it be nice to upload a set of log messages that happened before the error on the client side, along with the actual error? And if you didn’t have an error, not to post anything?</p>
<p>For the proposed LocalStorageAppender, I leverage <a href="https://github.com/marcuswestin/store.js">Store.js</a>, a “stupid simple” micro javascript framework for interacting with LocalStorage. As with the introductory example earlier, let’s first look at the core, working code that logs to LocalStorage in JSFiddle. To see it working, check out in your browser development tools (Firebug/Chrome Developer Tools/etc) your LocalStorage resource pane to see the “Hello World” log message <a href="http://jsfiddle.net/QpK4t/12/">jsfiddle.jshell.net logging “Hello World” under a timestamped key</a></p>
<p>Let’s break down the ‘running example’ code a bit here.</p>
<div><script src='https://gist.github.com/4063813.js?file='></script>
<noscript><pre><code> LocalStorageAppender.prototype = new log4javascript.Appender();
LocalStorageAppender.prototype.layout = new log4javascript.NullLayout();
LocalStorageAppender.prototype.threshold = log4javascript.Level.DEBUG;</code></pre></noscript></div>
<p>Here we set up our appender object with some pretty self-explanatory functions that all log4javascript appenders need to implement.</p>
<p>The nuts and bolts of our appender is in the append method, so let’s look at that</p>
<div><script src='https://gist.github.com/4063826.js?file='></script>
<noscript><pre><code>LocalStorageAppender.prototype.append = function(loggingEvent) {
var appender = this;
var getFormattedMessage = function() {
var layout = appender.getLayout();
var formattedMessage = layout.format(loggingEvent);
if (layout.ignoresThrowable() && loggingEvent.exception) {
formattedMessage += loggingEvent.getThrowableStrRep();
}
return formattedMessage;
};
if (store.enabled) {
var formattedMessage = getFormattedMessage();
store.set(JSON.stringify(new Date().getTime()), formattedMessage);
if (loggingEvent.level == Level.FATAL) {
var allStoreMessages = store.getAll();
var logMessageStr = loggingEvent.messages[0] + " - previous javascript log messages:";
for(var prop in allStoreMessages) {
if(allStoreMessages.hasOwnProperty(prop)) {
var storeMessage = allStoreMessages[prop];
if (storeMessage instanceof Array) {
logMessageStr += storeMessage[0] + "\n";
} else {
logMessageStr += storeMessage + "\n";
}
}
}
$.ajax({
type: 'POST',
url: '/logging',
data: 'level=' + loggingEvent.level + '&message=' + logMessageStr,
async: true,
success: function(r){
store.clear();
}
});
}
}
};</code></pre></noscript></div>
<p>The getFormattedMessage function is the same as the one used for the BrowserConsoleAppender - nothing particularly special for our use case. You’ll see that I then use Store.js to see if the browser supports LocalStorage, and if it does I store the message in LocalStorage with a timestamped key. You could certainly be more fancy with how you determine what your ‘keys’ are but this assures them to be unique (obviously important) and provides a simple, (hopefully) understandable example.</p>
<p>You’ll then see an example use case where one could, if the loggingEvent has a level that meets a certain threshhold (you could also choose to log events that exceed a certain threshhold), one can get all messages, and format them into a string, separated by newlines, with the last log message presented in the beginning of the string, and a “log stack” of messages for all the messages scooped up from LocalStroage. Those messages are then posted to the server, and upon our success handler the LocalStorage store is cleared.</p>
<p>Additional items to think about when using this type of logging appender includes cleaning up the LocalStorage data you have added when it is no longer useful - for example, upon entry/exit of portions of your application.</p>
<p>I think using LocalStorage is a nice approach for local through production environment client-side logging as it provides a way to consistenly log the client portion of your application and selectively report back to the server when you have issues. It especially hits a nice sweet spot for mobile web applications where client-side code execution/handling can unexpectedly vary across OS’s and their various browsers.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Kickstarter and the fine line of investment]]></title>
<link href="http://zachlendon.github.com/blog/2012/08/31/kickstarter-and-the-fine-line-of-investment/"/>
<updated>2012-08-31T21:14:00-05:00</updated>
<id>http://zachlendon.github.com/blog/2012/08/31/kickstarter-and-the-fine-line-of-investment</id>
<content type="html"><![CDATA[<p>Kickstarter’s <a href="http://www.kickstarter.com/terms-of-use">Terms of Use</a> states that it is a platform “where Project Creators run campaigns to fund creative projects by offering rewards to raise money from Backers.” In many ways this has similarities to investing in the stock market, where one puts up money with the hope of getting a reward - more money. When you do get more money in the market, you pay taxes on it. When you get a reward on Kickstarter, the Terms of Use makes no reference of any such tax liability. In the Kickstarter case, how much an individual might pay in the unlikely event that doing so would make them a good citizen is incredibly unclear, as the “value” of the product often cannot be easily determined.</p>
<p>Kickstarter is also like the stock market in that you could wake up one morning and realize you’ve lost all of your investment. In the stock market, at least you would be able to deduct a capital loss on your taxes, while with a Kickstarter project you are left to kick yourself for backing the project, and to fight for something from the project creator(s). I believe it is fair not to tax the rewards on Kickstarter if you don’t get to also deduct the losses.</p>
<p>That being said, there was a quote that made the rounds recently on Twitter stating the Kickstarter was the QVC for hipsters. I’d link to it to give out proper credit but I’m sure I’d be breaking some sort of Twitter usage policy. The Kickstarter platform is certainly becoming a more popular and more efficient way to launch innovative products. Success on Kickstarter provides a company great momentum from which they can grow their business. Project creators can explore and refine several product strategies at a low cost through Kickstarter projects. In many ways, the 5% fee that Kickstarter charges is a huge bargain for project creators who run their projects the right way.</p>
<p>If you are someone like me who craves the latest, cool technology - a hipster if you will - then Kickstarter can become a alluring ‘shopping’ destination. The Kickstarter terms state that it exists for funding ‘creative projects’, but it is becoming more and more of a place to buy products. Every product is “creative” I guess, right? If it becomes a place ‘shoppers’ shop for products, you can be sure ‘producers’ will find ways to put more products on it. In life everything chases money. Kickstarter reviews projects before allowing them online, but it doesn’t seem to be doing alot to limit products from being sold under what often seems like the guise of “investment”. I say this because I can assure you that many people don’t look at this like an investment - they are looking at what they are “pledging” as a purchase of a product. Kickstarter can word it however they want, but that’s how many “pledgers” look at it.</p>
<p>As more and more ‘business’ gets conducted on Kickstarter, I believe it will come under regulatory scrutiny. There has not been a public marketplace to date that I’m aware of that hasn’t in time fallen under regulatory scrutiny. You could say that selling new products on Amazon and eBay is a pretty wide open marketplace, but I would retort that those marketplaces are indeed under scrutiny. As I said, money chases money, and when money and products change hands in a very public fashion and aren’t taxed or regulated, that doesn’t last forever.</p>
<p>I’ve backed two Kickstarter projects personally <a href="http://www.kickstarter.com/projects/smartthings/smartthings-make-your-world-smarter/">SmartThings</a> and <a href="http://www.kickstarter.com/projects/supr/slim-the-thinnest-wallet-ever">SlimWallet</a> - both great looking projects - and if those projects go well I’m certainly likely to press my luck and invest in more projects that I find appealing. Like any market, blow ups and the regulators will come a calling. I’m going to enjoy the marketplace before outside forces try and ruin it. And I’m going to try and “invest” carefully - and so should you. Often Wall Street is considered a “legal casino” and be assured that Kickstarter is also a casino - an online, hipster casino. As in all casinos, the house has the advantage, so in time you will lose. Place your bets carefully.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Improving Mobile Capabilities of Geb Pages and Spock Specifications]]></title>
<link href="http://zachlendon.github.com/blog/2012/05/15/improving-mobile-capabilities-of-geb-pages-and-spock-specifications/"/>
<updated>2012-05-15T22:54:00-05:00</updated>
<id>http://zachlendon.github.com/blog/2012/05/15/improving-mobile-capabilities-of-geb-pages-and-spock-specifications</id>
<content type="html"><![CDATA[<p>I spoke this evening at <a href="ojug.org">OJUG</a> about automated testing of mobile web applications on mobile devices with <a href="http://www.gebish.org/">Geb</a> and <a href="http://code.google.com/p/spock/">Spock</a>. The source code that I demoed during the talk is on <a href="https://github.com/zachlendon/flashcards-grails/">GitHub</a>.</p>
<p>With the original forked application, different Grails layouts were used for the mobile version (which leverages <a href="jquerymobile.com/">JQuery Mobile</a>) of the app vs. the non-mobile version. As can be found in many similar web applications, the mobile version acts as a slimmed down version of the desktop application, with limited functionality and different urls to access certain pages. This creates challenges when writing functional tests for the application - even with great frameworks such as Geb and Spock. While one way to address these challenges is to build the application differently so as to not encounter them, in reality that may not be in the developer’s complete control. And even when it is, it likely is not a viable solution for removing all “challenges” that will come about when trying to create functional tests around a mobile web application. It’s just not an area that is refined to that level yet. For my forked ‘flashcards’ app, I addressed some of the challenges, but left many in place. With that as a baseline, I did, as part of my talk, give a few examples of ways to address these challenges using Geb and Spock that I’d like to share.</p>
<p>One challenge I referenced above is with the URL that you ‘drive’ to using <a href="http://seleniumhq.org/docs/03_webdriver.html">WebDriver</a> is typically defined statically as a <a href="http://www.gebish.org/manual/current/api/geb-core/geb/Page.html#url">url property in your Geb Page Object</a>. But what if you want to use the same page object but have a different url for your mobile version? And what if you want to define a different <a href="http://www.gebish.org/manual/current/api/geb-core/geb/Page.html#at">at</a> condition when you have arrived at the page for mobile? Define mobileUrl and mobileAt properties of course - which I do in this base page class.</p>
<div><script src='https://gist.github.com/2707275.js?file='></script>
<noscript><pre><code>
class GebRemotePage extends Page {
/**
* Returns the constant part of the url to this page.
* <p>
* This implementation returns the static url property of the class.
*/
@Override
String getPageUrl() {
//if we are using a remote web driver then we have to use the ip address of the local instance.
//To keep the individual page objects "cleaner", we try to deduce when we need the full url
//and put it in only in those scenarios. The downside is we assume port. Perhaps/ideally
//there's a way to ask grails for just this part and we can make that dynamic
if (getDriver() instanceof RemoteWebDriver) {
RemoteWebDriver remoteWebDriver = getDriver()
DesiredCapabilities capabilities = remoteWebDriver.getCapabilities()
def contextPathToUse = this.class.url
def hostToUse = "localhost"
if (["android", "iPhone", "iPad"].contains(capabilities.browserName)) {
hostToUse = InetAddress.getLocalHost().getHostAddress()
if (this.class.hasProperty("mobileUrl")) {
contextPathToUse = this.class.mobileUrl
}
}
return "http://" + hostToUse + ":" + "8080" + contextPathToUse
}
this.class.url
}
@Override
boolean verifyAt() {
if (this.class.hasProperty("mobileAt")) {
def verifier = this.class.mobileAt?.clone()
if (verifier) {
verifier.delegate = this
verifier.resolveStrategy = Closure.DELEGATE_FIRST
verifier()
} else {
true
}
super.verifyAt()
}
}
}
</code></pre></noscript></div>
<p>You’ll notice that this page also builds the entire url to use and determines whether we need to use an ip address or can just use localhost. This allows Geb to communicate with my mobile browser in both emulator and on-device scenarios. I find this to be easier than port forwarding scenarios, which is the other known strategy for finding mobile browser instances that are not located on the same ip address as the running application.</p>
<p>Another issue that I have run into that I’ve addressed through a base Spock ‘Spec’ class is wanting to know when I’m testing via a mobile device. I find this useful as it allows me the option of having ‘switch’ logic in my Geb code, if I so choose, to do something different if I’m running on a mobile device vs. if I’m not - similar to <a href="http://grails.org/plugin/spring-mobile">withMobileDevice from the Spring Mobile Grails Plugin</a>.</p>
<p>An example base specification for this might look like:</p>
<div><script src='https://gist.github.com/2707341.js?file='></script>
<noscript><pre><code>class MobileSpec extends GebReportingSpec {
@Shared
def isMobile = false
def setupSpec() {
if (getBrowser().driver instanceof RemoteWebDriver) {
RemoteWebDriver remoteWebDriver = getBrowser().driver
DesiredCapabilities capabilities = remoteWebDriver.getCapabilities()
if (["android", "iPhone", "iPad"].contains(capabilities.browserName)) {
isMobile = true
}
}
}
}</code></pre></noscript></div>
<p>With this ‘isMobile’ @Shared property, I can now execute logic specific to mobile or non-mobile devices in specifications that extend this MobileSpec class. You’ll see in my <a href="https://github.com/zachlendon/flashcards-grails/blob/master/test/functional/com/kgrodzicki/flashcard/grails/LessonsSpec.groovy">LessonsSpec</a> class that I do switch ‘setup’ logic for my tests where I need to bootstrap some data. With the desktop application, I can do this by uploading a file. On a mobile web application - especially iOS - good luck uploading a file on a mobile web application. With my ‘isMobile’ switch, I can choose to manually ‘seed’ an in-memory H2 database with data when on a mobile device, vs. actually uploading the data through the browser when on the desktop device. While this creates some undesirable complexity in the code, it does allow for re-usability of the Spec class between mobile and desktop versions. In the end, it’s the developer’s choice and the one chosen in this particular test isn’t intended to be correct for this use case or any other - it’s more intended to demonstrate capabilities.</p>
<p>In the end, hopefully this sample application and the pulled out examples above will help drive discussion about what we can do to make testing functionality of both mobile and web applications easier and more pain free. While the tools, like Geb and Spock, that we have at our disposal currently can be used pretty cleverly, trying to test mobile and web versions of applications functionally side-by-side can be challenging. As the source code in the sample application alludes to, the styling applied by JQuery Mobile, Bootstrap, JQuery UI and other frameworks makes defining selectors to get at DOM content challenging, and makes creating pages and specifications that can be used for both versions a somewhat foolhardy endeavor (though my sample forked application tries). Couple this with running these tests on mobile emulators and mobile devices (instead of simply spoofing user-agent strings to act like mobile device), and developers and test builders are created with an array of challenges. Limiting these challenges going forward will allow us to help the clients we work for create better quality, tested desktop and mobile web applications in the years ahead.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Backbone.js: from Hashbangs to HTML5 PushState]]></title>
<link href="http://zachlendon.github.com/blog/2012/02/21/backbone-dot-js-from-hashbangs-to-pushstate/"/>
<updated>2012-02-21T22:45:00-06:00</updated>
<id>http://zachlendon.github.com/blog/2012/02/21/backbone-dot-js-from-hashbangs-to-pushstate</id>
<content type="html"><![CDATA[<p>All kinds of <a href="http://isolani.co.uk/blog/javascript/BreakingTheWebWithHashBangs">talk lately</a> about hash-bang URLs (http://url#fragment) and how they will lead to the end of civilization (seemingly) this coming December. I was one of the guilty parties, implementing hash-bangs in a heavy AJAX mobile application I’ve been working on, even though nearly (if not) all of the browsers the users would have would support <a href="http://dev.w3.org/html5/spec-author-view/history.html#dom-history-pushstate">HTML5 PushState</a>. I did this being aware of the burdgeoning holy war, and had always intended to convert. It actually fits more cleanly into the conventions of the Grails back-end for the application - i.e., http://url/fragment - and it’s HTML5, the future - plus all the other reasons those articles allude to (however opinionated or valid they might actually be). Nevertheless, my two-staged approach was more a matter of already knowing how to do the hash-bang approach and needing to understand better the pushState approach prior to implementing it.</p>
<p>Much of the mobile webapp in question is built upon <a href="http://documentcloud.github.com/backbone/">Backbone.js</a> (which is AWESOME) - and I find it can often take a bit of investigative work (blog/documentation/source code reading + experimentation) to figure out exactly how to do something ‘well’. It’s really pretty simple to switch between a ‘hashbang’ approach and a pushState approach with backbone.js - but it didn’t always seem that way. First some setup then some key takeaways:</p>
<p>Typically at some point in your backbone.js MVC workflow you are updating the state of your models and/or collections on the front-end through communicating with the server. As a result, backbone.js will fire a corresponding add, remove or reset event, which you as the event-informed developer will hook into and bind your own JS function(s) against. If you wish to update the browser url - such that it is bookmarkable and works as a state in your browser’s history - you’ll typically do this here.</p>
<p>So there’s your background. Now let’s look at the two scaled down versions of common backbone.js use cases and discuss the subtle different of approach - first hashbang:</p>
<div><script src='https://gist.github.com/1881657.js?file='></script>
<noscript><pre><code>//document.ready() js function contains backbone.js instantiating code which:
$.mobile.pushStateEnabled = false;
new App.Routers.TodosRouter();
Backbone.history = Backbone.history || new Backbone.History({});
Backbone.history.start();
//Backbone Router
App.Routers.TodosRouter = Backbone.Router.extend({
routes: {
":toDoId": "loadTodo"
},
initialize: function() {
this.todos = new App.Collections.Todos();
this.todoView = new App.Views.TodoView({collection: this.todos});
this.todos.fetch();
},
loadTodo: function(toDoId) {
//hear you have your id and your todos collection
//you would use underscore and/or backbone collection functionality to get
//the model object (represented by model below) you want
...
this.todoView.setModelAndRender(model);
}
// The View
App.Views.TodoView = Backbone.View.extend({
el:'.content',
events: {
},
initialize:function () {
this.collection.bind('reset', this.navigateToTodo, this);
this.collection.bind('add', this.navigateToTodo, this);
},
navigateToTodo () {
Backbone.history.navigate('#' + this.model.toJSON().id);
return this;
},
setModelAndRender: function(model) {
this.model = model;
this.render();
return this;
},
render:function () {
}
}
</code></pre></noscript></div>
<p>then HTML5 pushState:</p>
<div><script src='https://gist.github.com/1881650.js?file='></script>
<noscript><pre><code>//document.ready() js function contains backbone.js instantiating code which:
$.mobile.pushStateEnabled = false;
new App.Routers.TodosRouter();
Backbone.history = Backbone.history || new Backbone.History({});
Backbone.history.start({pushState:true});
//Backbone Router
App.Routers.TodosRouter = Backbone.Router.extend({
routes: {
"/contextPath/:toDoId": "loadTodo"
},
initialize: function() {
this.todos = new App.Collections.Todos();
this.todoView = new App.Views.TodoView({collection: this.todos});
this.todos.fetch();
},
loadTodo: function(toDoId) {
//hear you have your id and your todos collection
//you would use underscore and/or backbone collection functionality to get
//the model object (represented by model below) you want
...
this.todoView.setModelAndRender(model);
}
// The View
App.Views.TodoView = Backbone.View.extend({
el:'.content',
events: {
},
initialize:function () {
this.collection.bind('reset', this.navigateToTodo, this);
this.collection.bind('add', this.navigateToTodo, this);
},
navigateToTodo () {
Backbone.history.navigate('/todos/' + this.model.toJSON().id, {trigger: true});
return this;
},
setModelAndRender: function(model) {
this.model = model;
this.render();
return this;
},
render:function () {
}
}
</code></pre></noscript></div>
<p>They look pretty similar. Note that with the pushState version we have a $.mobile.pushStateEnabled = false;. That’s so if our project has JQuery Mobile that it plays nicely. There’s the whole {pushState:true} option declaration when starting the backbone history, but that’s pretty evident/expected. Notice with the router that the pushState version has the /contextPath before the :toDoId while the hashbang version just has the :toDoId. Backbone’s history functionality has the concept of a ‘root’ url - where presumably one could set the contextPath and all url’s in your backbone implementation would be based off of that, but I found in practice that my url’s at best weren’t consistent on when they did or didn’t use that root definition. So I’d almost recommend seeing if a root url works for you (especially if you have no contextPath) but having this as your fallback. I almost found it clearer to define the full context path where needed - as opposed to having a root path used in Backbone history - and ensuring it would work there - and then leveraging a context path elsewhere - vs. just being explicit across the few areas where url’s exist in one’s backbone app. Nevertheless, I could imagine this ‘base root context path’ being configured as a global variable (or in general be configurable) for some apps where the contextPath could change based on environment (helping bring consistency to your url syntax across your backbone.js layer), but for my use case I haven’t crossed that bridge yet.</p>
<p>Back on topic, note the:</p>
<p>Backbone.history.navigate(‘#’ + this.model.toJSON().id);</p>
<p>vs.</p>
<p>Backbone.history.navigate(‘/todos/’ + this.model.toJSON().id, {trigger: true});</p>
<p>This in general seems pretty sensible, but the trigger scenario is significant here (and is noted in the backbone docs) - so that my loadTodo router method gets called. With the hashbang approach, the hashchange event was just being picked up and the router method was getting called by backbone. I believe there’s something I could be doing where the router function in question could automatically be called in both scenarios - but it’s good to know that if the router method isn’t getting called that this option is the cure. It’s much more elegant at least than the manual calls to the router method that you see in some online examples.</p>
<p>Other than that, you’re really set. Regardless of implementation, the “requirement” is that the url’s you “save” should be bookmarkable - i.e., not broken at least and should presumably show the same state of the app that the user sees when the url is initially presented. That makes them also convenient to use as the identifiers in your history, as in the end you are manipulating the browser’s ‘back’ history state, should your user use the browser’s back button in your javascript heavy application. And even though in practice backbone.js is most ideal in SPA (single page architecture) applications, even in those applications, being able to return to previous view states is often a requirement. One trick in restoring these ‘states’ is often the models you need to re-render those states are in the backbone collections you already have locally, so you may just be able to grab the right models (based on one or more identifiers in the url) and re-render the view. There’s also the option that you’ve chosen to cache an HTML5 page in the DOM and can simply transition to that page in your UI upon navigating to particular points in your Backbone.js history.</p>
<p>So while this code hopefully helps provide some examples for the UI prospective, don’t forget the back-end piece of ensuring that the url’s you are saving can be returned to and provide valid results.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Up and Blogging with Octopress, Mao and GitHub Pages]]></title>
<link href="http://zachlendon.github.com/blog/2012/02/17/up-and-blogging-with-mao/"/>
<updated>2012-02-17T00:34:00-06:00</updated>
<id>http://zachlendon.github.com/blog/2012/02/17/up-and-blogging-with-mao</id>
<content type="html"><![CDATA[<p>I’ve been meaning to get back into blogging for a while. Blogging seems like the 2006 thing to do, right? Nevertheless, I really enjoy the technologies I get to work with on an almost daily basis - namely iOS, Grails and various mobile frameworks (JQuery Mobile, Backbone.js, underscore.js, etc.), and hopefully I run across a few things that might be of use to some other people. At least I’d like to use this blog to help give an outlet to that illusion. Having a blog also lets me rant about things and have it stored online for an indeterminate amount of time - who doesn’t like having their words haunt them for years on end?</p>
<p>So with all that being said, I’ve had a long list of weak excuses why I hadn’t gotten a blog going, and high on the list was pretty things like</p>
<ul>
<li>Need to figure out where to host it</li>
<li>Need to figure out what software to use</li>
<li>Need to figure out what styling to use</li>
</ul>
<p>Well I’ve read enough to determine that <a href="http://octopress.org">Octopress</a> is the developer’s <a href="http://wordpress.org">Wordpress</a> (I probably first noted it being used on Matt Gemmell’s blog, and his post <a href="http://mattgemmell.com/2011/09/12/blogging-with-octopress/">his experiences blogging with Octopress</a>). I also determined <a href="http://github.com">GitHub</a> is as good of a place as any to host one, for starters at least - and works well with Git-hosted <a href="http://octopress.org">Octopress</a>. <a href="http://octopress.org">Octopress’s</a> default theme (which this blog uses) provides good enough styling for a developer-centric blog in my opinion - for the time being at least.</p>
<p>For now I’m using <a href="http://mouapp.com">Mouapp</a> for my Markdown editor - recommended by <a href="http://zanthrash.com/">@zanthrash</a> - while I thought I liked the idea of <a href="http://danimal.org/blog/2011/07/31/posting-to-octopress-from-marsedit/">posting to Octopress from MarsEdit</a>, I actually like the raw markdown editing better and being able to see the markup in the same window, vs. building html formatted posts. Plus it isn’t that hard to create a new post in Mao, save it to my local blog posts folder, and do a rake generate and deploy - and have it pushed to my github pages repo see it live online - immediately. I’m sure this will be a process that will scriptable to make it even easier to do with future posts.</p>
<p>A couple things I did note during the process - which probably are obvious to many but weren’t initially to me</p>
<ul>
<li>To ensure that individual blog pages get generated properly you must have the markdown section rake’s new_post command generates at the top of your markdown file</li>
<li>Strings should be in quotes in your Octopress _config.yml file, or you’ll likely get parse errors</li>
</ul>
<p>If anyone has tips to make the use of these technologies even better going forward please pass them along.</p>
]]></content>
</entry>
</feed>