100905 items (293 unread) in 22 feeds
MSNBC
(16 unread)
PHP
(62 unread)
Deals
(178 unread)
Web Development
(35 unread)
Frugal Blogs
(2 unread)
A best bet is this Carter's Girls' Print Pique Dress in four colors (Multi pictured) for $10 plus $6 shipping. That's $12 off and the lowest total price we could find. It's available in sizes 4 to 6X, although not all style/color combinations are available.
Note that no warranty information is provided.
Amazon Prime membership is $79 per year; however, Prime is free for college students for six months, and free for parents and caregivers for three months.
Also for in-store pickup only, Ace Hardware offers its Ace Women's Nitrile-Coated Garden Gloves for $1.99. (Click on page 2 of the circular to see this price.) This $1.99 mail-in rebate makes them free. That's $2 off and tied with our mention from four weeks ago as the best price we could find for these gloves. They're available in sizes S to L. Rebate ends May 31.
A best bet is this Ruby and Diamond Pendant in 10K White Gold (pictured) for $84.05 with free shipping. That's $205 off list and the lowest total price we could find for a similar ruby pendant by $15. It an AA+ quality ruby and features round-cut diamonds with H-I color and I1-I2 clarity.
We’ve all experienced it. A bad thunderstorm occurs and the lights flicker repeatedly. Sometimes, the power goes out, often preceded by a big energy surge that makes our lights get really bright for a second or two.
Such things are part of living on Earth and using electricity. Mother Nature is quite powerful, and as safe as we sometimes feel in our homes, nature can intervene all the time.
On top of that, there’s human error. About a year ago, the lights in our house all flashed really brightly, then everything went out. It turns out that there was a minor accident during the replacement of an electric pole not too far from our house.
There are also problems internal to one’s house. A fuse blows. A minor (or major) wiring issue occurs. These things can cause the electricity in your home to surge or go out.
Each of these rather common events can have a devastating impact on the plugged-in electronic devices in your home. Your television. Your computer (and monitor). Your DVD player. Even things like your microwave.
Sometimes, a surge won’t affect them at all. Sometimes, a surge can damage the circuitry inside the equipment, leaving it completely unusable. Sometimes, it just causes a minor problem, causing your device to work most of the time but have weird errors that you can’t figure out.
In those cases, you’re going to wind up replacing a device. I’ve seen people replacing televisions, DVDs, computers, computer monitors, and even electronic keyboards due to power surges – and that doesn’t include other effects that resulted in less-than-optimal use of the equipment (like a TV that randomly shuts off every hour or so).
The solution to this is simple: get some surge protectors for your electronic devices.
One common mistake is when people think that an ordinary power strip is actually a surge protector. It’s not. A power strip just allows you to use one outlet for several devices and offers no protection. A surge protector usually offers the same benefit as a power strip, but also protects your devices against unexpected power surges, and it usually only costs a few dollars more.
If you have an electronic device of significant value plugged straight into an outlet or an unprotected power strip, get a surge protector now and move your device(s) to that surge protector. It’s a simple, one-time action that can keep your device from becoming damaged during an electrical storm.
My solution is to never buy an ordinary power strip. There are surge protectors out there that basically function the same as power strips, but offer protection for your devices. It’s worth the extra few dollars for such protection, especially when compared to a damaged computer or television.
This post is part of a yearlong series called “365 Ways to Live Cheap (Revisited),” in which I’m revisiting the entries from my book “365 Ways to Live Cheap,” which is available at Amazon and at bookstores everywhere. Images courtesy of Brittany Lynne Photography, the proprietor of which is my “photography intern” for this project.
A best bet is the Bobbi Brown Everything Mascara (pictured) for $24. Add two to your cart and apply coupon code "BOBBI" to cut the price for all three to $48. With free shipping, that's $24 under the cost of ordering it directly from Bobbi Brown. It's available in Black.
A best bet is this Jos. A. Bank Men's Tropical Blend 2-Button Suit with Pleated Trousers in Light Tan Plaid (pictured) or Light Brown Plaid for $159 with free shipping. That's $536 off and the lowest total price we could find. It's available in sizes 36 to 46 in short, regular, and long, although not in all size/color combinations.
Of note, this used unit may have dents, scratches, or missing parts. No warranty information is provided.
A best bet are the adidas Women's Techfit Three-Quarter Tights in four colors (Phantom pictured) for $40. The above coupon code knocks them to $28. With $5 for shipping, that's the lowest total price we could find by $7. They're available in sizes from S to XL, although not in all size/color combinations.
Following your purchase, you will receive an email with instructions and a redemption code to receive your free photo book.
Note that size XXL is also available for 99 cents more, and 3XL for $1.99 more.
A best bet is this Dolce Vita Men's Designer Slim Fit Suit in Metal Black (pictured) for $169.99 before coupon, $149.99 after. With free shipping, that's $20 under our mention five days ago and the lowest total price we could find. It's available in sizes 34 to 60 in short, regular, and long lengths, although not in all combinations.
Of note, the 77kids stores will be closing soon, as announced on their Facebook page.
Need just a few more pieces? Today only, Kids Woot offers a similar Art 101 177-Piece Wood Art Set for $24.99 plus $5 for shipping.
Whether you're keeping track of must-read stories using Pocket, browsing hilarious videos on Squrl, hunting down amazing photography on National Geographic, or keeping up with the election season with NBC Politics, msnbc.com's Rosa Golijan has the apps for you. Want more tech news, silly puns, or amusing links?
A best bet is the IZOD Men's Short Sleeve Stripe Polo (pictured) for $18.64 with free shipping for Prime members. That's $39 off and the lowest total price we could find. It's available in sizes from M to XXL.
A best bet is the 5.8-lb. Dell Inspiron 15 Intel Core i3 2.3GHz 15.6" LED-Backlit Widescreen Notebook (pictured), model no. I15N-3000BK, for $399.99 with free shipping. That's $10 under our mention from three days ago and the lowest total price we could find. Sales tax is added where applicable. It features an Intel Core i3-2350M 2.3GHz dual-core Sandy Bridge processor, 15.6" 1366x768 LED-backlit widescreen LCD, 6GB RAM, 500GB hard drive, DVD burner, 802.11n wireless, Bluetooth, 6-cell battery, and Windows 7 Home Premium 64-bit. (Update: Link corrected.)
Each week, I highlight ten things each week that inspired me to greater financial, personal, and professional success. Hopefully, they will inspire you as well.
1. Emerson on moving on
Today is just one day. When it’s finished, move on. Don’t dwell on the mistakes. Get up tomorrow and do better.
“Finish every day and be done with it. You have done what you could; some blunders and absurdities no doubt crept in; forget them as soon as you can. Tomorrow is a new day; you shall begin it serenely and with too high a spirit to be encumbered with your old nonsense.” – Ralph Waldo Emerson
Each day is a chance to paint your masterpiece.
2. Green leaves in abundance
I love the period between late spring and midsummer where the green leaves are in thick abundance on nearly every tree.
Walks in the woods this time of year are particularly enjoyable. Thanks to Julia for the picture.
3. Dag Hammarskjold on achievement
Whenever you achieve something, it feels great. However, it’s often just a stepping stone to something even bigger.
“Never measure the height of a mountain until you have reached the top. Then you will see how low it was.” – Dag Hammarskjold
Focus on the goal at hand. Don’t worry about what’s even further down the road.
4. David Eagleman on death
What are you going to do to live as long as possible?
“There are three deaths. The first is when the body ceases to function. The second is when the body is consigned to the graved. The third is that moment, sometime in the future, when your name is spoken for the last time.” – David Eagleman
When your name is spoken for the last time, your impact on the world has almost completely gone away. What can you give to the world to have enough impact to keep your name alive?
5. Rembrandt’s Self-Portrait
There are so many things I love about this painting. Whenever I look at one of Rembrandt’s works, I feel like I can see the simple strokes he used that somehow merge together to create something much more.
6. George Bernard Shaw on how others measure you
Most people end up thinking of you in terms of some point in the past where they made up their mind about you, and there’s very little you can do to change it.
“The only man I know who behaves sensibly is my tailor; he takes my measurements anew each time he sees me. The rest go on with their old measurements and expect me to fit them.” – George Bernard Shaw
The best you can do is to try to make sure each moment interacting with others is a good one.
7. David Foster Wallace on the complexity of life
There is no piece of advice or any sort of writing that will give you all the answers you seek. Even the longest article or book is just the vaguest outline.
“What goes on inside is just too fast and huge and all interconnected for words to do more than barely sketch the outlines of at most one tiny little part of it at any given instant.” – David Foster Wallace
8. Elvis Costello singing That Day Is Done
This was performed at Linda McCartney’s memorial concert, and both Linda and Paul were friends with Elvis.
9. Aristotle on considering things
Just because you disagree with something doesn’t mean you should consider it or think about it.
“It is the mark of an educated mind to be able to entertain a thought without accepting it.” – Aristotle
Why do you disagree with it? If you can’t explain why, it’s time to learn.
10. Buddha on anger at others
Being angry with someone is almost always a waste of time. You’re not going to change that person through your anger.
“Being angry with another person, what can you do to him? Can you destroy his virtue and his other good qualities? Have you not come to your present state by your own actions, and will also go hence according to your own actions? Anger towards another is just as if someone wishing to hit another person takes hold of glowing coals, or a heated iron-rod, or of excrement. And, in the same way, if the other person is angry with you, what can he do to you? Can he destroy your virtue and your other good qualities? He too has come to his present state by his own actions and will go hence according to his own actions. Like an unaccepted gift or like a handful of dirt thrown against the wind, his anger will fall back on his own head.” – Gautama Buddha
Instead, channel that emotion into something positive for yourself.
You can purchase up to two years at this annual rate.
A best bet is Kingdoms of Amalur: Reckoning for Windows for $29.99. That's $6 under our mention from five weeks ago and the lowest price we could find for a download by $30.
Of note, Sears offers a Shop Your Way Max 3-Month Membership (formerly known as ShipVantage) for $0.02 via coupon code "MAXFREE4". This program yields free shipping on millions of items with no minimum purchase required, discounted shipping upgrades, and more. Note that this code will not stack with the above 20% off code.
Of note, get a $2 Amazon Instant Video Credit for free when you post your favorite movie from Amazon Instant Video to your Facebook wall.
Note: A $35 activation fee applies.
Better yet, apply coupon code "SEARSSTYLE20MAY" to cut the price further to $7.99.
newegg now offers it for the same price.

Truncated by Planet PHP, read more at the original (another 952 bytes)
For eight months, I had the opportunity to intern with the YUI Team at Yahoo, while I was completing my engineering degree. Today, I’d like to share the top ten things that I learned from my experience with YUI.
A Bit of BackgroundThe YUI Team is primarily responsible for the development and maintenance of the YUI Library. The library is a collection of user-interface components, class management tools, and utilities written in JavaScript and CSS that make it easier for developers to create web applications. Unlike other libraries, YUI is not just about DOM manipulation; it has a rich set of class management tools and a robust event system that makes it easier to develop in JavaScript.
Apart from that, the YUI Team is also responsible for YUI Theater, a very popular resource for web developers, along with a myriad of smaller tools such as YUI Builder, YUI Compressor, YUI Test and more.
1. Understanding the LibraryMy role at the team was to primarily help develop components for the library, although I helped out a little bit with YUI Theater as well.
When I walked into Yahoo on my first day, I had zero real-world experience with YUI. I was familiar with JavaScript, jQuery, HTML/CSS and PHP, but that was basically it when it came to web programming. I’m sure many of you are or have been in the same boat as me.
My first task was to learn the syntax and architecture behind YUI3. Fortunately, I had a good understanding of raw JavaScript, and this helped immeasurably. A lot of front-end engineers know certain libraries really well, but many don’t know how they works under the hood. To build plugins or modules on top of a library (such as YUI3 or jQuery), it’s useful to have a fundamental understanding about how that library works, and in this case, how JavaScript as a language works.
What I learned: Having a fundamental understanding of raw JavaScript (no libraries) forces you to understand the language better and makes you a better developer in the long run.
Think about it. Each JavaScript library is different and has its own idiosyncracies. The only commonality is that they are rooted in JavaScript. Doesn’t it make sense to understand the idiosyncracies of JavaScript first? Take some time out to build something small without using a library. You’ll be glad you did.
Js Fundamentals LinksThe YUI Team relies on Git as their primarily version control system and the project is hosted on Github. Previous to my internship at YUI, I used Git for my own small projects, and this generally consisted of the following steps:
git pull origin mastergit add "<insert a large number file>"git commit -m "<insert some general comment>"git push origin masterI’m sure many of you have done what I just described. Generally, as the project time gets longer, the commits get harder to understand! However, in a large team with a dozen developers, this method breaks down really fast. Firstly, master in YUI3 (and many other large projects) is considered to be stable code that has been unit-tested, so commits generally do not go there until a later stage. Generally, each developer works on a separate branch and merges into master once they are confident in their code.
What I learned: You don’t have to be a guru, but understanding the basics of Git (branching, merging, reverting and solving merge conflicts) can go a long way to keeping your repos clean and organized.
A lot of things are possible to do with Git and I’ve found that, rather than learning them ahead of time, it’s more useful to research when you need to do something in particular. More importantly, my experience has taught me to be careful when experimenting with Git. It’s better to ask someone when trying a new command or trying it on a mock repo instead of messing up the commit history of the live repo. :)
Git Basics Links 3. Modular Architecture for Web AppsSeparating code into different JavaScript files to maintain a modular architecture can be painful for new developers. As a result, we often see large JavaScript files with hundreds of lines of code in them.
<DOCTYPE html>
<html>
<head>
<script>
//My entire application's JS right here in one single spot. Uh oh.
</script>
...
This introduces tight coupling between objects and isn’t good practice when it comes to object-oriented programming. Even worse, you may have global variables that makes your app fail in unexpected ways. We’ve all been guilty of this in the past. Luckily, libraries like YUI can help you with this.
What I learned: When writing an entire web application in JavaScript, it’s very important to have well-organized code; maintaining a spaghetti-coded project will only cause you headaches and nightmares.
One of the most important aspects of YUI3 is its modular nature. As a developer, you only pull down the code that you need. This reduces coupling, keeps file sizes small, and improves your code’s readability.
For instance, you could do this in a file called counter.js:
YUI.add("counter", function(Y) {
//Write code specific to my counter object
}, '1.0', {requires: ['node']); //Say what my counter object code needs to run. YUI will pull these in first.
And then in a separate JS file, such as app.js:
YUI_config = {
modules: {
'counter': { fullpath: 'path/to/counter.js', requires:['node'] }
}
};
YUI().use('counter', function(Y) { //Pull down my counter module.
var counter = new Y.Counter({…}); //Create a counter instance
});
Instead of having one big file called app.js, we’ve split it up based on functionality into counter.js and app.js.
The growing popularity of NodeJS made me want to start building some stuff with it. Being a front-end engineer, I like being able to use JavaScript on the server. Although NodeJS is not suitable for everything, it’s worth learning.
JS on the server does not have the same limitations as client-side JS because there is no DOM, but a library such as YUI can still help you with software architecture on the server – things such as class and object management, and custom events.
What I learned: YUI 3 is not all about DOM manipulation — it also contains a robust set of class/object management tools, not to mention our powerful custom events. All of these tools are immediately useful in server-side programming.
Being on the YUI Team, I was able to explore Dav Glass’ NodeJS-YUI3 module, which wraps up YUI functionality for use in a NodeJS environment. Remember that, apart from web sites, NodeJS is a useful tool for creating command-line utilities as well. Having good software architecture is always a big plus!
It comes down to code managementIf you aren’t a YUI3 user, I still recommend finding a set of tools to help organize your JavaScript on the server. Due to the inherent async nature of JavaScript and its propensity for callbacks, you need to be careful or you’ll quickly end up with spaghetti-code.
5. Hacking Using YUIWhat I learned: Stay focused and keep hacking.
Hacking is one of my favourite things to do when I am trying to learn something new. When trying to learn a new tool, there’s nothing like sitting down and building something with it.
Similar to jQuery, YUI3 has a distinct set of widgets, utilities and CSS frameworks. On one of the Yahoo Hack Days, I had the chance to sit down and try some of these out. As I’m a fan of cricket, I made a mobile web app called Crictainment. Let me outline some of the parts of YUI that I found to be particularly useful for hacking.
Y.ScrollView
Hacking a web app using YUI is pretty straightforward. For example, if you want a scrollview that is flickable with your fingers:
YUI().use("scrollview", function(Y) {
var scrollview = new Y.ScrollView({
srcNode:"#scrollable",
height:"20em"
});
});
And in the HTML:
<div id="scrollable" class="yui3-scrollview-loading">
<ul>
<li>AC/DC</li>
<li>Aerosmith</li>
<li>Bob Dylan</li>
<li>Bob Seger</li>
...
</ul>
</div>
The scrollview module figures out whether it should be horizontal or vertical based on the width and height of its container. More information on the YUI3 scrollview is here.
Y.AnimAnother useful utility when hacking something is the Animation utility. It helps to spruce up your application with some nice animation that leverages CSS on capable browsers, and defaults to JS timer-based animations on older browsers.
var myAnim = new Y.Anim({
node: '#demo',
to: {
width: 0,
height: 0
}
});
myAnim.on('end', function() {
myAnim.get('node').addClass('yui-hidden');
});
Y.YQL
Another really important tool that is great for hacking is YQL – Yahoo Query Language. I’m sure most of you have used it, but for those who haven’t, think of it as a way to access a whole host of different APIs using a standard syntax. YUI and YQL work really well together through the Y.YQL module. For instance, here we are getting the recent activity from Vimeo:
YUI().use('yql', function(Y) {
Y.YQL('select * from vimeo.activity.everyone where username="basictheory"', function(r) {
//r now contains the result of the YQL Query
});
});
Try out this query here. I use YQL regularly to access different APIs in a consistent manner.
Regardless of whether you are using YUI, jQuery, or any other library, building something with it is a great way to become familiarized with your development toolbox. When hacking, don’t worry too much about the technical details – just set a time limit and just build it!
6. YUI App FrameworkOne of the newer additions to YUI has been the App Framework. It’s similar to Backbone.js, but with YUI’s sophisticated event infrastructure under the hood. The App Framework made it easy for me to build MVC-style web applications.
Just like any other tool, I built something with the App Framework to understand how it worked. Once again, I can’t stress the benefits of hacking on products. For the App Framework, I built TopForty with a friend of mine.
The Model
The YUI App Framework is built on the MVC design pattern. Let’s start with the model. If you aren’t familiar with MVC, take a look at this tutorial.
For TopForty, we were able to get a large JSON array of the most talked-about songs from Twitter. Each object in the array looked like this:
{
"title": "PayPhone",
"position": 1,
"video_id": "5FlQSQuv_mg",
"song_id": 627,
"artist": "Maroon 5",
"duration": 232,
"images": […]
}
We wrapped this up in a Y.Model subclass called Y.SongModel. Here’s the class definition:
Y.SongModel = Y.Base.create('songModel', Y.Model, [], {
initializer: function (config) {
...
}
}, {
ATTRS: {
title: {},
artist: {},
images: {
value: []
},
nowPlaying: {
...
}
...
}
});
This allowed us to listen for events whenever one of the ATTRS in the model changed and take appropriate action. For example, if the current song being played was changed:
//When the model's isPlaying attribute changes, call the handleIsPlayingChange function.
this.model.after('isPlayingChange', this.handleIsPlayingChange, this);
The View
Each model was represented visually by a View, which is essentially just HTML. For TopForty, we had a SongView that looked something like this:
<div class="song yui3-u" id="songModel_2">
<img class="song-img" src="...">
<div class="song-desc">
<div class="song-number">2</div>
<div class="song-movement">
<img class="movement-icon" src="...">
</div>
<div class="details">
<h4 class="song-name">Turn on the lights</h4>
<p class="artist-name">Future</p>
</div>
</div>
</div>
We wrapped this up in a Y.View subclass called Y.SongView. Each View requires a render() method that is called when displaying the view on the page, and a initializer() method where events are subscribed. If you want, you can establish a relationship between your view and a Model or Model List instance by attaching event handlers to them in a custom initializer() method like we see below:
Y.SongView = Y.Base.create('songView', Y.View, [], {
initializer: function (config) {
var model = this.get("model");
model.after('change', this.render, this);
model.after('destroy', this.destroy, this);
},
render: function() {
...
},
destroy: function() {
...
},
showSongOverlay: function() {
...
}
},{
ATTRS: {
container: Y.one("#songContainer")
}
});
By doing this, we were able to have songview specific methods such as displayNowPlayingIndicator() and showSongOverlay() on the Y.SongView instance rather than as separate functions.
The Router, formally known as the Controller, is what makes everything connect and work. Remember that in classical MVC, all user actions traditionally go through the controller. In web apps, the router uses HTML5 history to change URLs and controls the state of the application.
For example, in TopForty clicking on a song changes the URL to the song’s ID. The URL change is picked up by the router, which loads the YouTube video for the song in question:
Y.AppRouter = Y.Base.create('appRouter', Y.Router, [], {
// Default route handlers inherited by all CustomRouter instances.
index: function (req) {
// ... handle the / route, load up the UI, etc.
},
loadSongFromUrl: function (req) {
// ... handle the /song/:songId' route ...
//Get the youtube video from the model with an id of :songId and play it.
}
}, {
ATTRS: {
// The specific routes that we are interested in listening to:
routes: {
value: [
{path: '/', callback: 'index'},
{path: '/song/:songId'', callback: 'loadSongFromUrl'}
]
},
}
});
What I learned: Leveraging the MVC design pattern can help you create anything from simple non-interactive views to rich applications.
I hope I have been able to give you a taste of what is possible with the YUI App Framework. For a more thorough walkthrough, I recommend the following links. If you aren’t a fan of the App Framework, I recommend you look into the equivalent jQuery alternatives. Backbone.js in particular has a large following with good documentation.
For developers, writing code is often the easy and fun part. The hard part is figuring out how old, buggy code works and trying to fix past errors. To minimize this, a lot of organizations including Yahoo emphasize the importance of Test Driven Development (TDD). With TDD, you write the tests first, then add the functionality until all your tests pass. Want to add a new feature? Write a new test, then code the feature.
I was new to TDD when I joined the YUI team. Unfortunately, JS does not have the best testing suite. User interactions in particular are hard to test. Furthermore, we have to test in all the major browsers.
One of the primary ways we conduct unit-testing is through YUI Test.
What I learned: Following test-driven development principles makes you more productive as a programmer.
YUI Test is a complete testing framework for JavaScript and Web applications. You can use the simple JavaScript syntax to write unit tests that can be run in web browsers or on the command line, as well as functional tests to be run in web browsers.
Here’s how you set it up:
// Create a new YUI instance and populate it with the required modules.
YUI().use('test', function (Y) {
// Test is available and ready for use. Add implementation
// code here.
});
Now, we can start writing some unit tests!
var testCase = new Y.Test.Case({
name: "TestCase Name",
//---------------------------------------------
// Setup and tear down
//---------------------------------------------
setUp : function () {
this.data = { name : "Tilo", age : 23 };
},
tearDown : function () {
delete this.data;
},
//---------------------------------------------
// Tests
//---------------------------------------------
testName: function () {
Y.Assert.areEqual("Tilo", this.data.name, "Name should be 'Tilo'");
},
testAge: function () {
Y.Assert.areEqual(23, this.data.age, "Age should be 23");
}
});
YUI Test supports equality assertions (areEqual()) which uses ==, sameness assertion (areSame()) which uses ===, special value assertions such as isFalse(), isNotUndefined(), mock objects and more.
After writing our tests, we can run it as shown below. The results of the test can be outputted as XML, JSON, jUnitXML, or TAP.
Y.Test.Runner.add(testCase); Y.Test.Runner.run();Don’t forget to test, regardless of what you use.
If you don’t use YUI Test, that’s fine. However, I want to stress the importance of testing your code. I’m guilty even today of writing code without writing tests and it’s probably okay if it’s your own small personal project. All of us cut corners sometime. However, if you’re programming for a client or an organization, following TDD principles will save you (and your clients) from a headache.
8. Minifying and Linting Your JavaScriptServing up JavaScript files without any minification or lint step can be risky. Minification can often compress the file-size by over 30%, making your page load faster for the end-user. Linting is a good way to ensure your JavaScript follows good coding practices so that errors are minimized.
During my time at Yahoo, we used YUI Compressor and YUI Builder to compress and build our code. The build step includes concatenation and linting through JSLint. I’m sure most of you have used JSLint at some stage, and I do believe it helps improve your coding style.
What I learned: Ask yourself if a certain practice reduce your possibility of errors and if so, follow it.
Now, I’m not going to talk in depth about YUI Compressor and Builder because those may not be the tools of your choice. Usually for my own projects, I use the JSLint NPM Module to check my code.
What I want to stress on is the need to perform these steps prior to pushing code out to production. As JavaScript does not have a compilation step, many developers are accustomed to pushing their JavaScript out to production, comments and all included. Again, this is okay for small projects, but perhaps you should consider minifying and linting your code to ensure users get the best experience.
Additional ReadingThe YUI Team has a great following of friendly, active, and knowledgable developers just like most large open-source projects. As developers working on open-source software, I learned that I can’t sit under a rock and code all day, even if I want to. Developing is as much about writing code as it is about making sure the code you write is actually helping someone solve a problem.
This took me a while to learn, but it’s an important lesson. When writing software, the goal isn’t to use the coolest technology or the latest stack or the hottest language. It’s about the user. When writing OSS, the users are often other developers who build on top of your product. Small things such as weekly updates, tweets, and IRC communication can actually help you more than hours of coding.
What I learned: It’s not about the technology or the stack or the language, it’s about the user.
In my first month of interning at YUI, I wrote about 500 lines of code which I had to scrap because most of it was repetitive and did not address the end goal. I was guilty of coding too early without figuring out the requirements of the project. A good rule is to spend a third of your time gathering requirements, a third of your time writing tests and the final third implementing your software.
10. “When all you have is a hammer, everything looks like a nail.”I want to conclude by sharing the most important thing that I think I learned in my internship. I consider it to be more important than any technical knowledge that I gained. By working on a library that is not the most popular, I realized the importance of diversifying my skills and being flexible as a developer. I’ve seen how much work it takes to maintain an open-source project and have gained a lot of respect for everyone who release their well-written code for free.
What I learned: Choose a library because it’s the right one for the job, not because it’s your favourite. Understand that different pieces of software serve different purposes, and sometimes understanding the difference is tough.
If you’re in software development, new techniques, languages and concepts pop up all of the time. We all feel those nagging doubts every now and then: “can I keep up with the changes and stay competitive?” Take a moment, and sum a line from my favourite movie, Casablanca: “The fundamental things apply, as time goes by.”
What’s true for love, is true for code.
What’s true for love, is true for code. The fundamental things will always apply. If you have an understanding of the underlying ideas of software development, you will quickly adjust to new techniques. In this tutorial, we will discuss three basic principles and mix them with many more. They provide a powerful way of managing the complexity of software. I’ll share some of my personal opinions and thoughts, which, hopefully, will prove useful when it comes to applying them to code and real-world projects.
Principle – Don’t Repeat YourselfA basic strategy for reducing complexity to managable units is to divide a system into pieces.
This principle is so important to understand, that I won’t write it twice! It’s commonly referred to by the acronym, DRY, and came up in the book The Pragmatic Programmer, by Andy Hunt and Dave Thomas, but the concept, itself, has been known for a long time. It refers to the smallest parts of your software.
When you are building a large software project, you will usually be overwhelmed by the overall complexity. Humans are not good at managing complexity; they’re good at finding creative solutions for problems of a specific scope. A basic strategy for reducing complexity to managable units is to divide a system into parts that are more handy. At first, you may want to divide your system into components, where each component represents its own subsystem that contains everything needed to accomplish a specific functionality.
For example, if you’re building a content management system, the part that is responsible for user management will be a component. This component can be divided into further subcomponents, like role management, and it may communicate with other components, such as the security component.
As you divide systems into components, and, further, components into subcomponents, you will arrive at a level, where the complexity is reduced to a single responsibility. These responsibilities can be implemented in a class (we assume that we’re building an object-oriented application). Classes
contain methods and properties. Methods implement algorithms. Algorithms and – depending on how obsessive we want to get – subparts of
algorithms are calculating or containing the smallest pieces that build your business logic.
The DRY principle states that these small pieces of knowledge may only occur exactly once in your entire system.
They must have a single representation within it.
Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.
Note the difference between the piece of knowledge, and its representation. If we’re implementing the database connection in our CMS, we will have a code snippet that will initalize the database driver, pass the credentials, and save a reference to the connection in a variable. The code snippet is part of the knowledge, it’s about how something is achieved. The variable with the reference to the connection is the representation of that knowledge – and this can be used by other parties. If the database credentials change, we will have to change the snippet – not its representation.
In a perfect application, every small piece of business logic encapsulates its knowledge in a representation, namely a variable or a class property.
This variable itself is encapsulated in a class that can be described as a representation of a responsibility. The class is encapsulated in a component that can be described as a representation of functionality.
This can be proceeded until we reach the top level of our software project – that is, a stack of representations with increasing complexity. This way of looking at the complexity of software is called modular architecture, and DRY is an important part of it.
Software architecture is about managing complexity.
Achieving DRYness
DRY is a philosophy that packages logic into representations.
There are many ways of achieving DRYness. Hunt and Thomas suggested (among other things) code generators and data transforming. But, essentially, DRY is a philosophy that packages logic into representations.
As every part of your application can be seen as representation, every part exposes specific fragments of your underlying logic: The user management exposes access to registered users of the CMS, the user class represents a single user and exposes his properties (like the username). It retrieves the properties, via the representation of the database.
DRY and modular architecture require good planning. To achieve a representational hierachy from bottom-up, divide your application in a hierarchy of logically separated smaller parts and let them communicate with each other. If you have to manage larger projects, organizing them into components and using DRY within the components is a good idea. Try to apply the following rules:
The database driver is a simplified example, as there are many more layers involved in the real world (such as a specific database abstraction layer), and there is much more you can do to encapsulate logic – especially diving into design patterns. But even if you’ve just started with coding, there’s one thing to keep in mind:
When you find yourself writing code that is similiar or equal to something you’ve written before, take a moment to think about what you’re doing and don’t repeat yourself.
In the real world, applications that are a 100% DRY are hard, if not impossible, to achieve. However, applications that are unDRY to an unacceptable degree – and therefore hard to maintain – are quite common. Hence, it’s not surprising to learn that more than 50% of all software projects fail – if you’re taking a look at the code.
Many people tend to think that bad code is produced by bad coders. In my experience, this is very much an exception. More often than not, bad code is produced by bad account managers and an overall misconfiguration of process management in companies.
An ExampleBad code is rarely produced by bad coders.
DRYness is achieved by good planning.
As an example, say you’re hired as a technical consultant by a company that has problems with code quality and maintenance. You review the source and you see hacks and code duplication – the code is not DRY. This is a symptom of bad code quality, it’s not the reason. If you take a look at the version control system – aka the history of the code – chances are that you may find hacks that were introduced at times near deadlines and milestones. Take the time to review what changes are made, and you will likely be confronted with a change in requirements.
As noted above, DRYness is achieved by good planning. Forced changes on a tough deadline are forcing developers to implement dirty solutions. Once the code is compromised, the principle of DRY is likely to be sacrificed completely upon further changes.
There’s a reason why the most successful corporations in the IT business were founded by people with very good technical understanding – or even coders themself: Bill Gates, Mark Zuckerberg, Steve Wozniak, Steve Jobs, Larry Page, Sergey Brin and Larry Ellison know (or knew) what efforts are needed to implement something. Contrary, many companies tend to lay the requirements for engineering into the hands of account managers, and the conceptual part in the hands of business consultants…people who have never implemented anything.
Hence, many technical concepts work only in Powerpoint, Photoshop, and on 27″ widescreen displays. This may have been a successful approach in the days of, more or less, static websites, but it’s not nowadays – with interactive applications on multiple devices. Because coders are the last in the line, they are the ones who have to apply quick fixes on errors in the concept. If this is accompanied by an account manager, who can’t stand up to a client that likes to make last-minute changes, plans are thrown in the garbage, and something quick and dirty is implemented. The code becomes unDRY.
This example is a bit extreme (nevertheless, I have witnessed such scenarios), but it demonstrates that DRY is a theoretical concept, which is challenged by various parties in the real world. If you’re working in a company that forces you to work in this manner, you might suggest some changes to the process (like introducing technical expertise at an earlier stage of technical projects).
If you have a hands-off approach, keep reading! The You ain’t gonna need it principle will come to the rescue.
Principle – Keep it Simple StupidThe simplest explanation tends to be the right one.
In the late 19th century, physicists struggled to explain how gravity, magnetism and optics interact, when it comes to large distances – like the distances in our solar system. Hence, a medium named aether was postulated. It was said, that light is traveling through this medium, and that it’s responsible for effects that couldn’t be explained otherwise. Through the years, the theory was expanded with assumptions that adjusted the aether postulate to the results of experiments. Some assumptions were arbitrary, some introduced other problems, and the whole theory was quite complex.
An employee of the swiss patent office, Albert Einstein, suggested to get rid of the whole aether theory when he introduced a simple, yet revolutionary idea: All the oddness in calculating with large distances would fade away if we’d accept that time is not a constant; it’s relative. This incredibly of out-of-the-box thinking to come to the simplest explanation with the fewest assumptions to select between competing scenarios is referred to as Ockhams’s Razor.
There are similar concepts in many areas. In software development (and others), we refer to it as KISS. There are many variants for this acronym, but they all mean that you should strive for the simplest way of doing something.
Substantial progress in the history of mankind was achieved by lateral thinkers.
HTTP
The Hypertext Transfer Protocol is widely considered to be a perfect example for a simple solution: designed to transfer hypertext based documents, it is the backbone of highly interactive and desktop-esque applications nowadays. Maybe we have to find solutions for limitations in the protocol, and maybe we have to replace it someday. However, status quo is: based on a few request methods (like GET and POST), status codes and plain text arguments, HTTP has proved to be flexible and robust. That’s why HTTP has been repeatedly pushed to the limits by web developers – and is still standing.
We take this approach for granted, but the history of software development and standardization is full of overly complex and half-baked solutions. There’s even a dedicated made-up word for it: bloatware. Software like this is also described to be DOD, dead on arrival. I have a theory that is very similar to my theory of unDRY code, when it comes to bloatware … However, the success of the internet can be described as a success of simple, yet efficent solutions.
So what’s required to come to the simplest solution possible? It all comes down to maintainability and comprehensibility in software development. Hence, KISS kicks in during the phase of requirements engineering. When you think about how to transform a client’s requirements to implementable components, try to identify the following parts:
There are many people involved in the conceptual process, who do not have the technical expertise to make a reliable cost-benefit analysis
I was once working on a project, where the client wanted to import Excel spreadsheets into his crew management software. This was a clear match. Excel is a proprietary software with a complex document format. The format is complex, because it’s feature-rich: You can add graphs and other things to it – features that were not needed by the client. He was simply interested in the numbers. Thus, implementing the Excel import would require the implementation of a lot of unnecessary functionality. On top of that, there are multiple versions of Excel versions, and Microsoft fires off another release each year. This would have been hard to maintain, and it comes with additional costs in the future.
We ended up implementing a comma-separated-value import. This was done with a few lines of code. The overhead of the data was really small (compare an Excel sheet to it’s CSV equivalent) and the solution was maintainable and future-proofed. Excel was ready to export CSV anyway (as well as other programs that the client might want to use in the future). Since the solution was low-priced as well, it was a good application of the KISS principle.
To sum up: try to think out-of-the box if a task looks complicated to you. If someone is explaining to you his requirements, and you’re thinking that it’ll be tough and complex to implement, you’re right under almost any circumstances. While some things are just that – hard to implement – overcomplicated solutions are quite usual. This is the case because there are many people involved in the conceptual process, who do not have the technical expertise to make a reliable cost-benefit analysis. Hence, they don’t see the problem. Double-check the requirements whether they are really stripped down to the essence that the client needs. Take the time to discuss critical points and explain why other solutions might be more suitable.
Principle – You “Ain’t Gonna Need ItCoding is about building things.
When Google+ launched, Mark Zuckerberg – founder of Facebook – was one of the first who created an account in the social network that was aiming to take his own down. He added just one line to the About me section: »I’m building things.«. I honestly think that this is a brilliant sentence, because it describes the pure essence of coding in a few simple words. Why did you decide to become a coder? Enthusiasm for technical solutions? The beauty of efficiency? Whatever your answer is, it may not be »building the 1.000.001th corporate website with standard functionality«. However, most of us are making money that way. No matter where you are working, you’ll likely be confronted with boring and repetitive tasks every now and then.
80% of the time spent on a software project is invested in 20% of the functionality.
The You ain’t gonna need it principle (YAGNI) deals with these tasks. It basically translates to: If it’s not in the concept, it’s not in the code. For example, it’s a common practice to abstract the database access in a layer that handles the differences between various drivers, like MySQL, PostgreSQL and Oracle. If you’re working on a corporate website that is hosted on a LAMP stack, on a shared host, how likely is it that they will change the database? Remember that the concept was written with budget in mind.
If there’s no budget for database abstraction, there’s no database abstraction. If the unlikely event of a database change does occur, it’s a natural thing to charge for the change request.
You may have noticed the difference between You ain’t gonna need it and DRY-driven modular architectures: The latter is reducing complexity by dividing a project into manageable components, while the former is reducing complexity by reducing the number of components. YAGNI is similar to the KISS principle, as it strives for a simple solution. However, KISS strives for a simple solution by trying to implement something as easily as possible; YAGNI strives for simplicity by not implementing it at all!
Theodore Sturgeon, an American sci-fi author, stated the law: »ninety percent of everything is crap«. This is a very radical approach, and not overly helpful in real-world projects. But keep in mind that “crap” can be very time consuming. A good rule of thumb is: roughly 80% of the time spent on a software project is invested in 20% of the functionality. Think about your own projects! Everytime I do, I am surprised by the accuracy of the 80:20 rule.
80% of the time spend on a software project is invested in 20% of the functionality.
If you’re in a company that is notorious for tight deadlines and imprecise concepts, this is a powerful strategy. You won’t be rewarded for implementing a database abstraction layer. Chances are that your boss does not know what a database abstraction layer even is.
While this concept may sound simple, it can be hard to differ the necessary from the unnecessary parts. For example, if you’re comfortable with a library or a framework that uses database abstraction, you won’t save much time in dumping it. The key concept is another way of looking at software: we’re trained to write future-proof and maintainable software. This means that we are trained to think ahead. What changes may occur in the future? This is critical for bigger projects, but overhead for smaller ones. Don’t think into the future! If a small corporate website does fundamental changes, they may have to start from scratch. This is not a significant problem compared to the overall budget.
Planning a ProjectWhen you’re preparing your to-do list for a project, consider the following thoughts:
Let’s go a little bit into detail! I already provided an example for the first item in the list: don’t wrap a database driver around a database abstraction layer. Be suspicious of everything that adds complexity to your software stack. Notice that abstraction is often provided by third party libraries. For example – depending on your programming language -, a persistence layer, like Hibernate (Java), Doctrine (PHP) or Active Record (Ruby) comes with database abstraction and object-relational mapping. Each library adds complexity. It has to be maintained. Updates, patches and security fixes have to be applied.
We implement features everyday, because we anticipate them to be useful. Hence, we think ahead and implement too much. For example, many clients want to have a mobile website. Mobile is a term of wide comprehension; it’s not a design decision. It’s a use case! People who are using a mobile website are, well, mobile. That means they may want to access other information or functionality than a user who visits the site laid back at his desktop. Think of a cinema site: Users on the bus will likely want to access the starting time of upcoming movies, not the 50 MB trailer.
Bad concepts can often be identified by the lack of non-functional requirements.
With an appropriate budget, you would perform a dedicated analysis of the requirements for mobile. Without this analysis, you will simply provide the same information as is on the desktop site. This will be just fine for many circumstances! Because mobile browsers are very clever in adjusting desktop sites to their display, a radical YAGNI approach might be to not write a mobile site at all!
Non-functional requirements do not describe behaviour of a software, they describe additional properties that can be used to judge the quality of software. Since describing software quality presumes knowledge about software, bad concepts can often be identified by the lack of non-functional requirements. Maintainability, level of documentation, and ease of integration are examples for non-functional requirements. Non-functional requirements should be measurable. Hence, »The page should load fast.« is too inconcrete, »The page should load in two seconds max during an average performance test.« is very concrete and measurable. If you want to apply the YAGNI principle, assume moderate non-functional requirements if they are not mentioned in the concept (or if they are mentioned, but inconcrete). If you are writing the non-functional requirements yourself, be realistic: A small corporation with 20-50 page visits a day does not require three days of performance tweaking – as the page should load fast enough because the server is not busy. If the corporation can increase the number of daily visits, a better server or hosting package shouldn’t be too expensive.
Last, but not least, remember the 80:20 rule-of-thumb!
Last, but not least, remember the 80:20 rule-of-thumb! We have to identify the time consuming parts. If a part is absolutly necessary, you have to implement it. The question should be: how will you implement it? Does it have to be the latest framework with a small community? Do you need to switch to the just-released version of a library if the documentation is not up to date? Should you use the new CMS, when not all extensions are available? How much research will be necessary to do so? »That’s the way we have always done it.« is not an exciting approach, but it’ll get the job done without surprises.
It’s important to understand that all of this does not mean that you can start writing dirty code with hacks along the way! You’re writing a lightweight application, not a messy one! However, You ain’t gonna need it is a practical approach. If it would cause many lines of code to reduce a few lines of code duplicates, I personally think that you may relate efforts to budget and some unDRYness is ok. It’s a small application. Hence, the added maintenance complexity is acceptable. We’re in the real-world.
Let’s come back to the inital thought: we like building things. When Beethoven wrote the Diabelli Variations, it was contract work. I don’t think he made compromises on budget. He ran the extra mile, because he did not want to write average music; he wanted to write a perfect composition.
I’m certainly not implying that we’re all geniuses, and that our brilliance should shine through every line of code, but I like to think of software architecture as compositions. I’m a passionate developer, because I want to build perfect compositions, and I want to be proud of the things I’m building.
SummaryIf you want to be an experienced and business-proofed developer, you have to master the You ain’t gonna need it principle. If you want to keep your passion, you have to fight against it every now and then.
Software principles are a way of looking at software. To me, a good principle should be based on a simple concept, but it should evolve to a complex construct of ideas when confronted with other techniques and philosophies. What are your favourite software principles?
Many web developers use SSH (“Secure Shell”) on a daily basis to manage their servers, back up files, work remotely, and a myriad of other tasks. Today, I’ll explain what SSH is, do a brief history review, and, lastly, teach you how to set it up on your remote server or even your local network. Let’s get started!
A Starting ExplanationIf you’re reading this, it’s likely that you’re at least somewhat acquainted with Terminal (or on Windows, something like Cygwin). If so, then you will understand this quick functional explanation of SSH.
SSH is essentially using a network connection to get into Terminal on another computer.
If you aren’t familiar with Terminal, there are a lot of explanations and beginner’s guides to Terminal, both here on Nettuts+ and elsewhere. The power of SSH is reliant on its simplicity; by offering you access to the Terminal of another machine, SSH cuts to the chase and gives you full control over a remote machine. If you understand the power of Terminal and its direct connection to the inner workings of your local machine, then you understand the power of SSH!
A Brief HistorySSH was developed in its infant state in 1995. The primary developer, Tatu Ylonen, developed it as the first secure way to administrate a remote UNIX system. Previous to SSH, the only tools that existed sent information like passwords in clear text.
So, How Do I Use It?There are a million guides out there on how to get set up with SSH. We will specifically cover one method today. First things first, you’ll want to make sure you have SSH on your system.
To determine if SSH is installed, run `which ssh`. If Terminal returns something along the lines of /usr/bin/ssh, then you’re good to go! Otherwise, follow one of these guides to get it installed.
In this article, I will assume the version you are using is OpenSSH; there are some configuration differences that depend on your version of SSH. We will be explaining how to set up a Mac to connect to a MediaTemple server through a single SSH command. Once you have SSH installed on your machine, you will need to make sure your target host has SSH enabled. SSH runs over port 22 by default; you can use a command line tool like nmap to ping your server to determine if port 22 is accepting incoming connections, like this:
sudo nmap -sS hostname.com
Of course, you probably have access to the administrative interface for the server. Make sure you look through the options and enable SSH. On a Mediatemple server, this configuration is located under the Server Control panel. Mediatemple SSH is accessible by using root@primarydomain.com. You can add users to the account, but for the sake of keeping things simple, we will use root@primarydomain.com.
Once SSH is enabled (and you have set a root password in your server admin), you can run the following line to SSH into your server.
ssh root@primarydomain.com
…where primarydomain.com is your MediaTemple primary domain. You will then be prompted for your password (which is the root password you set in the control panel). If you are not using MediaTemple, you can SSH directly to the IP address of your server as well.
If using shared hosting, it is likely that you will not be logging in as root. Instead, you will log in with a user account name. For example, if you are using a service like Site5, you may log in with a username at a subdomain, like this:
ssh username@malta.site5.com
Ultimately, these configurations will depend on your specific web server company. Refer to your host’s documentation for more information.
Once you are “shelled in”, you can execute commands and traverse the file system within Terminal. Depending upon on your level of access, you may be able to install things on your server using apt-get or wget commands. You can manage your Apache server, edit configuration files with a Terminal-based text editor, view error logs, clear caches, view files directly on a server to make sure they are the correct version, and plenty of other lower-level system administration tasks. Now, what if you wanted to do more, faster with SSH?
How Do I Use It… Better?There are a ton powerful things that SSH opens you up to. We will skip a few of them (as quite a few are more sysadmin-related, like tunneling). But we will go over a few useful tricks.
Super-quick Log-inYou’re thinking to yourself, “seems like there should be a faster way to do this.” You’re right. And there is. Instead of having to remember your domain, password, and username for every server, you can set up a few configurations that will allow you to speed up the process to something along these lines.
ssh myserver
With the right configuration, you could run this, and without having to enter any passwords, IP addresses, or long domain names, you’re in! Again, we will assume you are logging into a MediaTemple server. First, we will generate ssh keys. This is basically a set of encrypted keys that live in ~/.ssh on your local machine. You have a “public” key and a “private” key. So, first things first, open a new Terminal window and create the .ssh folder in your home directory.
mkdir ~/.ssh
Next you will generate your keys with the following line. (This comes directly from MediaTemple’s documentation.)
ssh-keygen -t rsa -b 2048 -f ~/.ssh/id_rsa -C "Comment about your key goes here."
This line will generate an ssh key of type rsa, with 2048 bits (for security), at the file location specified, with the comment specified. You will be promprted for a password, but it isn’t mandatory or necessary; not providing a password will enable you to automatically log in. The RSA type is for SSH protocol version 1. Type DSA is for protocol version 2. Check with your web server to find out which version they are using. Once your keys are generated, you will then run this to make sure your SSH configurations are set to the right permissions.
chmod 700 ~/.ssh && chmod 600 ~/.ssh/*
Next, you will upload your public key to your server. There are quite a few ways to do this; this way comes from MediaTemple’s docs as well.
cat ~/.ssh/id_rsa.pub | ssh root@example.com 'cat - >> ~/.ssh/authorized_keys'
This code is echoing your id_rsa.pub through a `|` (pipe) into the next command, which is an SSH into root@example.com, where you will run an echo and concatenation of what you piped in the first command. It sounds a bit complicated, so there are a few alternative ways to handle this. Essentially, you are going to want no line breaks and your public key on its own line in a file, called authorized_keys on your server in the ~/.ssh/ directory. So, if this is the first or only key you want on your server, you could run this command to copy it directly to that location.
scp ~/.ssh/id_rsa.pub root@example.com:.ssh/authorized_keys
This line is essentially saying, “copy this first file through SSH to the server at this location relative to my current home directory.”
Once your authorized_keys contains your public key, you can attempt to login to the server with ssh username@example.com. If you put your public key in the root user directory’s SSH configuration files, you will be able to login directly to root. You will be asked about a rsa fingerprint; go ahead and allow this action. It adds the server you are connecting to to a known_hosts file. This file can be used for a lot of different things, but particularly to secure yourself against what is called a “man-in-the-middle” attack. If you’d like to read a little more about this, check out this explanation.
If you can successfully log into your server, as if you had entered a password, your keys are working properly. The next step is to add a couple of lines for a shortcut to a configuration file on your machine . Open ~/.ssh/config in your favorite text editor (create it if it doesn’t exist) and add the following:
Host shortname HostName somehost.com User username
Where “shortname” is a nickname for the server you want to log into. For instance, “Host myserver” would allow me to do ssh myserver. The HostName is your server location, and of course the User is your username. You may have User root at this spot. Once this file is saved, you should be able to run a simple command to log into your server, like this:
ssh shortnameGit Without a Hub
Please Note: this section requires a bit of familiarity with Git.
You can use SSH to set up your own Git repos on your server! This is useful for companies who don’t want to expose their code on GitHub for whatever reason, and it’s great to be able to push directly from a local machine to a Git repo on your own server.
To set this up, make sure Git is installed on both the host and your local machine. You may have to go through your web server company to have Git installed. Next, run git init on your server in the location that you want your Git repo to be. Of course, you can do this a hundred different ways, but if you prefer to not have a bare repository, you can use branches to push to from your local machine. Here is a common workflow.
ssh user@example.com cd /path/to/repo git init git checkout -b staging git checkout master # disconnect from shell session using ctrl-d cd /local/repo git init git add . git commit -am "some message" git remote add origin user@example.com:/path/to/repo git checkout -b staging git push origin staging ssh user@example.com cd /path/to/repo git merge staging
Essentially what is happening here is you are logging into the server, changing to the desired repo path, creating a repository and adding a “staging” branch which you can push to from your local machine. Then, you are creating your local repo and a corresponding “staging” branch on your local machine, and adding files to track to the repo. Next comes an initial commit. You are then adding the remote repository as an alias of “origin”. Next, you are pushing the local staging branch to the “origin” alias’s staging branch. Finally, you are ssh’ing back into the server and merging the “staging” branch with the default “master” branch.
SFTP > FTPYou can also use SSH-powered FTP (file transfer protocol), which is essentially a more secure (encrypted) version of FTP that runs over port 22 (rather than the default FTP port 21). Most FTP clients support SFTP as well. FileZilla (for Windows) and Fetch (for Mac) are two popular (and free) SFTP/FTP clients.
Easy Access to Your Server Almost AnywhereAs long as you are around a computer that is connected to the internet, has a Terminal, and has SSH installed (any Mac connected to WiFi, for instance), you can get access to your server, via SSH. That’s the best part. You don’t require any configuration (assuming you haven’t set up any restrictions that require a matching pubkey), you can log in with your username and password from practically anywhere. There are even SSH clients for iOS and other mobile devices. This is a very powerful feature of SSH that is only paralleled in portability by browser-based applications.
A Local Code RepoHopefully you can see the power of SSH in a daily development cycle.
Let’s imagine that you and a few buddies are working on some code together. Let’s also say you have a local computer that you have full control over that you use as a development LAMP server with a few VM’s installed on it. You can use SSH locally to move files to and from your computer and the development machine. You can even set up a local Git (or svn, or Mercurial) repo, powered by SSH to keep everything in check. Perhaps you could even make the development machine the only machine that is connected to the remote server via SSH, so that the code has to go through a specific staging process before it can be put into production. The backbone of all of these actions is SSH!
Hopefully you can see the power of SSH in a daily development cycle, especially for teams using version control. As I noted previously, there is plenty of documentation and a myriad of other network-level tools based on or reliant upon SSH that will give you more control and power over your development process and your server. Who knows? Maybe, one day, you could double as a sysadmin after all!
Some Other Helpful LinksHere are some other helpful links to get you started with SSH. It’s been around for a while, so there is plenty of documentation floating around.
Thanks for reading!
Well, it happened; Panic finally released the long-awaited version two of their popular code editor, Coda. But does it live up to the hype? Well, that depends on what type of coder you are. Read the full review after the jump!
The Good
It's Beautiful
When Panic initially unveiled the tour video for their new editor, I was taken aback. The massive file icons only appeal to a very specific type of coder – and I'm not that person. I, as well as many others, found them to be considerably unattractive. Perhaps they'd be appealing, if you exclusively work on static designs, but as someone who stares at an image-less, black background for the better part of each day, these icons only take up space.
Luckily, they can easily be reduced to simple text, at which point the editor becomes infinitely more attractive. The icons are a gimmick.
Helpers
Clearly, Coda is playing to its core audience: PHP developers. While you can certainly code in any language, PHP receives the bells and whistles.
As a PHP developer myself, I won't deny that "WAT" is scattered throughout the language. Even after years and years, I still frequently refer to the PHP.net documentation to remember which order a function's parameters should be passed.
Do I set the haystack as the first or second parameter, when using
stristr?
This is one area where Coda shines. It provides instant documentation in the sidebar as you type.
What's unfortunate is that this feature is limited to, essentially, front-end developers: HTML, CSS, JavaScript, and PHP. Why?
Nonetheless, it's a helpful resource, though I'll note that, despite the fact that Panic lists JavaScript as being supported, it only offers to link me to the book documentation. A bug perhaps?
Clippings
Coda's native clippings feature has received a substantial upgrade as well. Rather than the awkward floating bar from Coda 1, the new version rests as its own panel in the sidebar.
Notable additions include multiple (tab separated) insertion points, and the ability to assign both tab triggers and keyboard shortcuts.
Sites
The standard "Sites" layout is back in Coda 2, which allows you to assign either local or remote directories to one click icons. No notes here; it's helpful, and the new ability to group sites into “lists” (or categories) is very much welcomed.
CSS Helpers
In Coda 1, the CSS GUI was available via its own tab, or panel; however, with this new version, it's baked into the editor. For instance, as you type background, the option to use a GUI to create a gradient will be provided, though I must admit that setting values and percentages wasn’t overly intuitive.
Note: did you notice in the image above that Coda only adds a single Webkit prefix? Yeah, we'll cover that shortly! :/
This same functionality is available for other properties too, such as box-shadow and padding.
Honestly, these aren't features that I'd ever imagine using; they'd only slow me down. But still, for newcomers, I'm sure they'll prove to be helpul.
Quick OpenIt's nothing new, but, if you need to quickly switch between files, you can press Control + Q, and type in the name of the file. It's not nearly as fast (or responsive) as Sublime Text's implementation, but will get the job done.
Built-In Transmit
The popular Transmit engine is essentially baked into the editor. This provides you with a full file browser for, not only your local files, but also through FTP, SFTP, WebDAV, and S3.
This is a huge plus for some, though, for me, I don't remember the last time that I uploaded files to my server with (S)FTP. Still a nifty feature, though, and the ability to publish a project with a keystroke is handy!
TerminalIntroduced in the first release of Coda, the Terminal panel is back. If you’re not the type of coder who has Terminal open at all times, accessible through a keyboard shortcut, then this will be a welcomed feature.
The Bad
Multiple Cursors
Really? No multiple cursor support? Coda provides a "block edit" functionality, but it's confusing, and not nearly as intuitive as Sublime Text's implementation.
In this editor's opinion, it was a massive mistake to not make multiple cursor support a top priority. I can't imagine using an editor that doesn't give me this ability.
Odd CSS SupportFrom a distance, Coda's CSS support appears to be fantastic. Auto-completion for the new CSS3 properties, a GUI for creating gradients and box-shadows, etc. What more could you want?
Well, yes that would be true, if those implementations were flawless – but they're not. For example, remember the nifty documentation functionality that I noted above?
It's very helpful…and limiting at the same time. You might be surprised to find that, if I type the official transform property, nothing shows up in the sidebar (there's also no auto-completion for the property). The documentation only registers if I specificially use -webkit-transform. Yep, the official version doesn't work – and neither does -moz-transform (or any of the other versions, for that matter).
Webkit Love AffairOh, and as for Opera and Microsoft prefixes (
-oand-ms, respectively), you can forget about it. Zero auto-completion support. They might as well not even exist.
There's no denying that Coda's Webkit-based support is fantastic. It makes sense; the live preview uses the Webkit engine, so they need to ensure that the gradients (and other CSS3 properties) that the GUI produces will render correctly.
But, particularly when considering the huge debates that have centered around CSS and Webkit in the last half year, Panic's decision to seemingly endorse Webkit-exclusive CSS3 properties is a terrible one. Why will it auto-complete -webkit-animation, but not any of the others (ms, o, moz)? Why does it render a Webkit gradient, but not for the other vendors?
When questioned (on Twitter), Panic noted that it's incredibly simple to manually add the other prefixes. True; no one is denying that. But why should we have to? Do it for us.
If convenience were the only downside, that would be one thing, but the problem is that their decision to only provide Webkit generation and auto-completion endorses the notion (especially for newcomers) that it's okay to tailor designs specifically for one engine. Note: a smattering of Mozilla prefixes are provided, but not nearly as many.
I Wish…If, behind the scenes, Coda would maintain a list of every CSS3 property, along with its required prefixes, that would be huge. Don't make me memorize whether or not Microsoft now provides its own prefix for gradient support. Be a good role model, and do it for me. Sheesh, don't make me build a tool, like Prefixr, to get around this.
GitGit integration in Coda 2 is certainly not a minus, but more of an "ehh." The previous version of Coda provided Subversion support (while the community moved to Git). So, sure, adding a UI for Git was a smart move.
It very well might be helpful to those who aren't entirely comfortable in the Terminal, but, still, I wonder how many of these folks are truly using version control. Do they understand what a branch, stage, or commit is? My instinct is that, if you do, then you'll absolutely prefer to perform these sorts of commands in the Terminal – likely using your own custom aliases to speed things up.
Nonetheless, it's a feature that might be helpful to some – just not me.
Command PaletteConsidering the fact that Sublime Text 2, in barely a year, has become the golden editor amongst web developers, you would think that Coda might adopt some of the most touted features in that editor.
Why isn't there a helpful command palette that allows me to specify which action to execute with text? I can't always remember the keyboard shortcut for creating a split; why can't I pull up the command palette, and type "split"?
At this point, I consider a command palette to be essential in my workflow, yet Coda is missing one entirely.
Installing PluginsWe're still in the early days of Coda 2; as such, there currently aren't any plugins available on Panic's website. Hopefully, once some are released, there will be an easy, Package-Control-esque method for installing them. Please don't make me manually download and install these.
In Sublime Text, if I want CoffeeScript syntax highlighting, I can literally have it in ten seconds, without leaving the editor. Will Coda provide this same convenience? My instincts point to "no," which is not a good sign. The success of an editor has more to do with the community’s involvement than anything else.
Vi-Mode OptionThe success of an editor has more to do with the community’s involvement than anything else.
Remember when I noted that Coda was made for a very specific type of coder? Yeah, well that type of coder likely doesn't use Vim (or Git-based deployment). For me, though, it's the difference between using Coda and not. I'm heavily dependent upon these keyboard commands, but Coda has left me in the dark. Why? Sublime Text offers Vi (Vintage) support; even Chocolat does.
I'd imagine that, at some point in the near future, support will be provided through a plugin, but, nonetheless, when considering the resurgence of Vim in the last few years, shouldn’t support should be baked in as an option? Maybe so, maybe not; the truth is that the huge majority of Coda users have no interest in this feature, so I won’t knock Coda too much for this!
Only Four Color SchemesI grant you that this is petty, but why does Coda only provide four color schemes for the editor? Why not offer a dozen or so? We coders love our themes, but will now have to resort to scouring the web for custom skins. Or, when considering the fact that Solarized is one of the most popular, cross-editor themes on the web, why not provide that as a built-in choice? Chocolat does.
Admittedly, things like this are small annoyances, but, still, they're just that… annoying.
A Great Sidebar…ButUpdate – here’s a plugin that provides lots of alternative color scheme options.
Clearly, Panic spent a great deal of time on the new sidebar functionality. With all of these helpful features, though, why I am limited to just one view at a time? Maybe I want a file browser on top, and the documentation viewer below? It would be far more convenient if I could stack these panels.
Configurability
Sublime Text is sometimes criticized because its configuration options page is essentially a JavaScript object.
The irony is that I consider this to be one of its best features! I have control over everything! Need to change the tab-width, font-size, or a keyboard shortcut? Easy – it takes five seconds. What about on a per-file basis? That’s doable too. With Coda, though, I virtually have no clue how to configure it to my preferences – beyond the basic settings that are provided in the Preferences pane.
Closing ThoughtsThe updates aren’t indicative of today’s modern developer.
As I proof this article, I realize that it has come across as somewhat critical. The truth is that Coda 2 is a solid editor – much better than its predecessor. While it does have some bugs, it was just released. Give the amazing Panic team time to listen to community input and release updates/patches. And, certainly, missing functionality can often be provided, via plugins.
I'm only capable of reviewing an editor based upon on my own needs. Personally, I'm most concerned with speed, convenience, and Vi support. Instead of Panic focusing on what will make me more efficient at my job, it sort of feels as if they’ve tacked on a lot of eye candy that doesn’t necessarily help me much. The updates also aren’t indicative of today’s modern developer. Where is Less, HAML, or CoffeeScript syntax highlighting? So yes, it’s a solid editor, but, at the same time, it’s not one that was tailored for more serious developers.
When considering Coda's stand-out features:
I wish that they had instead focused more on speed – not necessarily the speed of the editor, but the speed of my workflow.
How can I accomplish my daily tasks more quickly?
From this perspective, Coda 2 doesn’t provide anything new.
While I've only had a day with Coda, I must admit that something feels a bit…off. It doesn't feel as intuitive as I would have expected from Panic. Frequently, I find myself confused over how to execute simple operations. "Oh, I have to double-click to open this file." There's also no denying that the following is initially very confusing:
So we have a sidebar of files, then another panel in the middle for files, and then, lastly, remote files. This is what I was greeted to after opening Coda for the first time.
I've since learned that you have to get away from the notion that the traditional sidebar is specifically for displaying files. In the image above, it just happens to be set to the "Files" tab, which accounts for the doubled-files issue. Nonetheless, it took me a moment to come to grips with this.
Will I Switch to Coda 2?There’s this part of me that wants to use Coda. It’s beautiful and feels modern. But, then again, the simple fact is that I’m far more efficient in Sublime Text 2. After years of waiting, I was hoping for more. Bells and whistles are great, but, when you get down to it, there’s nothing overly innovative here. But that’s just me; what do you think?
You might be wondering why I haven’t mentioned the new Air Preview feature. Well, I’d very much like to, but haven’t yet been able to get it to work. I’ll update this article once I do!
If you haven’t heard, there’s a new kid in town: CanJS. What better way to dive into what this new framework offers than to build a contacts manager application? When you’re done with this three-part series, you’ll have all the tools you need to build your own JavaScript applications!
Choosing the Right ToolBuilding a JavaScript application without the right tools is difficult. While jQuery is great at what it does, a DOM manipulation library doesn’t provide any infrastructure for building applications. This is specifically why you need to use a library, like CanJS.
CanJS is a lightweight MVC library that gives you the tools you need to build JavaScript apps.
CanJS is a lightweight MVC library that gives you the tools you need to build JavaScript apps. It provides all the structure of the MVC (Model-View-Control) pattern, templates with live binding, routing support and is memory safe. It supports jQuery, Zepto, Mootools, YUI, Dojo and has a rich set of extensions and plugins.
In part one, you will:
Excited? You should be! Now let’s get coding.
Setting Up Your Folders And HTMLYou’ll need to create a folder for your application. Inside this folder, you need four sub-folders: css, js, views and img. Your folder structure should look like this when you’re done:
Save this as index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>CanJS Contacts Manager</title>
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/contacts.css">
</head>
<body>
<div class="container">
<div class="row">
<div class="span12">
<h1>Contacts Manager</h1>
</div>
</div>
<div class="row">
<div class="span3">
<div class="well">
<nav id="filter"></nav>
</div>
</div>
<div class="span9">
<div id="create"></div>
<div id="contacts"></div>
</div>
</div>
</div>
<script src=" [ajax.googleapis.com] <script src="js/can.jquery.min.js"></script>
<script src="js/can.fixture.js"></script>
<script src="js/contacts.js"></script>
</body>
</html>
At the bottom of the page, we load jQuery, CanJS, the fixture plugin and your application code (contacts.js).
Building Your UI With ViewsThe CSS and images for this tutorial are in included in the source files, which can be downloaded above.
Views are client-side templates that are used to render parts of your app. CanJS supports multiple templating languages, but this tutorial will be using EJS (Embedded JavaScript), which is packaged with CanJS and supports live binding.
EJS templates look like HTML but with magic tags where you want dynamic behavior (using JavaScript). There are three types of magic tags in EJS:
runs JavaScript code, runs a JavaScript statement, and writes the escaped result into the resulting HTML, runs a JavaScript statement and writes the unescaped result into the resulting HTML (used for sub-templates).Templates can be loaded from a file or script tag. In this tutorial templates will be loaded from EJS files.
Displaying ContactsTo render contacts, you’ll need an EJS template. Save the following code as contactsList.ejs within your views folder:
<ul class="clearfix">
<% list(contacts, function(contact){ %>
<li class="contact span8" <%= (el)-> el.data('contact', contact) %>>
<%== can.view.render('views/contactView.ejs', {
contact: contact, categories: categories
}) %>
</li>
<% }) %>
</ul>
contactLists.ejs will render a list of contacts. Let’s examine the template code here in more detail:
<% list(contacts, function(contact){ %>
The EJS list() helper invokes a callback function on each contact in the list. When used with an observable list, the list() helper will use live binding to re-run anytime the length of the list changes.
<li class="contact span8" <%= (el)-> el.data('contact', contact) %>>
The code above uses an element callback to add the contact instance to the data of the <li>. Everything after the arrow is wrapped in a function that will be executed with el set to the current element.
<%== can.view.render('views/contactView.ejs', {
contact: contact, categories: categories
}) %>
This code renders the contactView.ejs sub-template for each contact. can.view.render() takes a template and data as its parameters and returns HTML.
Sub-templates are an excellent way of organizing your views into manageable chunks. They also help simplify your templates and promote DRY (Don’t Repeat Yourself). Later in this tutorial, you’ll re-use this template to create contacts. Save this code as contactView.ejs in your views folder:
<a href="javascript://" class="remove"><i class="icon-remove"></i></a>
<form>
<div class="row">
<div class="span2">
<img src="img/contact.png" width="100" height="100">
</div>
<div class="span3">
<input type="text" name="name" placeholder="Add Name"
<%= contact.attr('name') ? "value='" + contact.name + "'" : "class='empty'" %>>
<select name="category">
<% $.each(categories, function(i, category){ %>
<option value="<%= category.data %>" <%= contact.category === category.data ? "selected" : "" %>>
<%= category.name %>
</option>
<% }) %>
</select>
</div>
<div class="span3">
<label>Address</label>
<input type="text" name="address"
<%= contact.attr('address') ? "value='" + contact.address + "'" : "class='empty'" %>>
<label>Phone</label>
<input type="text" name="phone"
<%= contact.attr('phone') ? "value='" + contact.phone + "'" : "class='empty'" %>>
<label>Email</label>
<input type="text" name="email"
<%= contact.attr('email') ? "value='" + contact.email + "'" : "class='empty'" %>>
</div>
</div>
</form>
Each property of a contact is placed in an <input> tag. These will be used to add and update a contact’s information.
Any time EJS encounters attr() while processing a template, it knows that the surrounding code should be turned into an event handler bound to that property’s changes. When the property is changed elsewhere in the app, the event handler is triggered and your UI will be updated. This is referred to as live binding. EJS Live binding is opt-in. It only turns on if you use attr() to access properties.
Let’s look at one of the <input> tags from the contactView.ejs to see how this works:
<input type="text" name="name" placeholder="Add Name"
<%= contact.attr('name') ? "value='" + contact.name + "'" : "class='empty'" %>>
The code in the magic tags will become an event handler bound to the contact’s name property. When we update the name property, the event handler is run and the HTML will be updated.
Organizing Application Logic Usingcan.Control
can.Control creates an organized, memory-leak free, stateful control that can be used to create widgets or organize application logic. You create an instance of a Control on a DOM element and pass it data your control will need. You can define any number of functions in your Control and bind to events.
When the element your Control is bound to is removed from the DOM, the Control destroys itself, cleaning up any bound event handlers.
To create a new Control, extend can.Control() by passing it an object containing functions you want to define. In part two, event handlers will be passed in as well.
There are a few important variables and functions present in every Control instance:
this – A reference to the Control instancethis.element – The DOM element that you created the instance onthis.options – An object containing any data passed to the instance when it was createdinit() – Called when an instance is createdAdd the following snippet to your contacts.js file to create the Control that will manage contacts:
Contacts = can.Control({
init: function(){
this.element.html(can.view('views/contactsList.ejs', {
contacts: this.options.contacts,
categories: this.options.categories
}));
}
})
When an instance of Contacts is created, init() will do two things:
can.view() to render contacts. can.view() accepts two parameters: the file or id of the script tag containing our template code and data. It returns the rendered result as a documentFragment (a lightweight container that can hold DOM elements).can.view() into the Control’s element using jQuery’s .html().A Model abstracts the data layer of an application. Two models are needed in this application: one for contacts and one for categories. Add this code to contacts.js:
Contact = can.Model({
findAll: 'GET /contacts',
create : "POST /contacts",
update : "PUT /contacts/{id}",
destroy : "DELETE /contacts/{id}"
},{});
Category = can.Model({
findAll: 'GET /categories'
},{});
A model has five static methods that you can define to create, retrieve, update and delete data. They are findAll, findOne, create, update and destroy. You can overwrite these functions to work with any back-end, but the easiest way to define a Model is using REST service, as exemplified in the code above. You can safely omit any static methods that won’t be used in an application.
It’s important to point out here that the model instances in CanJS are actually what we call ‘observables’. can.Observe provides the observable pattern for objects and can.Observe.List provides the observable pattern for arrays. This means you can get and set properties using attr() and bind to changes in those properties.
The findAll() method returns a Model.list, which is a can.Observe.List that triggers events when an element is added or removed from the list.
Fixtures intercept AJAX requests and simulate their response with a file or function. This is fantastic for testing, prototyping or when a back-end isn’t ready yet. Fixtures are needed to simulate the REST service the models in this application are using.
But first, you’ll need some sample data for the fixtures to use. Add the following code to contacts.js:
var CONTACTS = [
{
id: 1,
name: 'William',
address: '1 CanJS Way',
email: 'william@husker.com',
phone: '0123456789',
category: 'co-workers'
},
{
id: 2,
name: 'Laura',
address: '1 CanJS Way',
email: 'laura@starbuck.com',
phone: '0123456789',
category: 'friends'
},
{
id: 3,
name: 'Lee',
address: '1 CanJS Way',
email: 'lee@apollo.com',
phone: '0123456789',
category: 'family'
}
];
var CATEGORIES = [
{
id: 1,
name: 'Family',
data: 'family'
},
{
id: 2,
name: 'Friends',
data: 'friends'
},
{
id: 3,
name: 'Co-workers',
data: 'co-workers'
}
];
Now that you have some data, you need to wire it up to fixtures so you can simulate a REST service. can.fixture() takes two parameters. The first is the URL we want to intercept and the second is a file or function that is used to generate a response. Often URLs you want to intercept are dynamic and follow a pattern. In this case, you should use templated URLs. Simply add curly braces to the URL where you want to match wildcards.
Add the following to contacts.js:
can.fixture('GET /contacts', function(){
return [CONTACTS];
});
var id= 4;
can.fixture("POST /contacts", function(){
return {id: (id++)}
});
can.fixture("PUT /contacts/{id}", function(){
return {};
});
can.fixture("DELETE /contacts/{id}", function(){
return {};
});
can.fixture('GET /categories', function(){
return [CATEGORIES];
});
The first four fixtures simulate the GET, POST, PUT and DELETE responses for the Contact model, and the fifth fixture simulates the GET response for the Category model.
Your application has Models for your data, Views to render contacts, and a Control to hook everything up. Now you need to kickstart the application!
Add this to your contacts.js file:
$(document).ready(function(){
$.when(Category.findAll(), Contact.findAll()).then(
function(categoryResponse, contactResponse){
var categories = categoryResponse[0],
contacts = contactResponse[0];
new Contacts('#contacts', {
contacts: contacts,
categories: categories
});
});
});
Let’s take a closer look at what is happening in this code:
$(document).ready(function(){
Wait for the DOM to be ready using jQuery’s document ready function.
$.when(Category.findAll(), Contact.findAll()).then(
function(categoryResponse, contactResponse){
Call findAll() on both models to retrieve all of the contacts and categories. Since findAll() returns a Deferred, $.when() is used to make both requests in parallel and execute a callback when they are finished.
var categories = categoryResponse[0], contacts = contactResponse[0];
Get the list of model instances from the response of the two findAll() calls. The responses are arrays, with the first index being the list of model instances retrieved.
new Contacts('#contacts', {
contacts: contacts,
categories: categories
});
Create an instance of the Contact Control on the #contacts element. The list of contacts and categories are passed into the Control.
When you run your application in a browser, you should see a list of contacts:
Wrapping Up
That does it for part one of this series! You’ve been introduced to the core of CanJS:
In the next lesson (to be published May 28th), you’ll create a Control and View to display categories and use routing to filter contacts. Hope to see you there!
Questions? Ask away below!
Tired of the “techie” trying to explain to you what web hosting is all about from his bedroom-recorded Skype video? Ever get confused with all the funny terms they drop, like “Shared Hosting” and “Dedicated Servers”? We’ve all been there at one point or another! Well, the guys over at InMotion Hosting created a quick, visual guide that will get you up and running with the jargon in no time!
By InMotion Hosting ConclusionIf you’re still new to this world, and have questions about the hosting world, leave a comment below, and one of us will do our best to help!
When I started designing and developing themes and templates for the ThemeForest marketplace five years ago, customer support was easy. I’d get an email or two a day, answer them, and move on.
But as sales picked up, things became complicated. One or two emails turned into five, which turned into ten, which turned into twenty! I’d respond to a question, and get another question in response. Emails were lost. Customers were frustrated. It was all a big mess.
Email was no longer a tenable support solution.
Two years ago, I decided that enough was enough. Email was no longer a tenable support solution. I needed to find a better way.
So I did some digging around for a support solution. And believe me, there are plenty out there. Some are free — mostly poorly designed, buggy DIY scripts to install and run on your own server. Some aren’t — prettier, feature-rich … and expensive. Fifty bucks a month? For a guy like me, forget it.
Simply put, there was nothing out there that appealed to someone doing well — but not getting rich — selling online. So I decided to build one. Something simple. Something with the features I needed, and nothing else. And something that played nice with the Envato API.
Two years later, I finished Ticksy — and I love it. It’s sleek, easy to use and getting some great reviews.
Designing TicksyMost importantly, it’s doing a great job handling my support requests — the reason I built it in the first place!
As any developer knows, simple on the outside invariably means complicated on the inside. And, while Ticksy was a labor of love, building it was anything but easy.
Naming it? That’s a different story. Support ticket system. “Tick.” “Sy.” Ticksy. Get it?
I spent roughly three months designing the UI – buttons and form fields on the login screen, tabs and dropdowns in the app and more. Product management had to be spot-on, too. The initial Envato integration made it possible for me to tie every ticket directly into one of my products in the marketplace. Eventually, I worked product verification into the mix — only actual owners (not pirates) of my themes could submit tickets.
They liked it. No more email!
I developed the first, basic version of Ticksy using PHP and MySQL in about five months — continuing to support customers via email in the meantime (yuck). Finally, by the end of 2010, I was able to deploy the system for my own business. Customers started using it. They liked it. No more email! I felt great; a huge load was off my shoulders.
The original Ticksy dashboard, circa 2010
Then, something unexpected — and pleasant — happened. People started asking me about the system. The customers actually enjoyed using it as much as I did and wanted to know how they could use it in their businesses. I had no answer at the time. This was my system. I built it for me. Were there other people like me that would use something so simple?
Turns out there were. People wanted Ticksy. So I was back to the drawing board.
Ticksy For AllTwo partners and I redesigned the UI and backend to allow for multiple systems, added social sign-in buttons (Facebook and Twitter), got feedback from friends and family and added a few other features. I essentially redesigned/developed the entire Ticksy system, and launched a beta build in early 2011.
I had around ten beta users using it full-time, and they loved it. Months went by, bugs were squashed, the UI was refined and I let it get some good use for a long time. If I was going to release this to the public and expect people to pay for it, it had to work well. I let it go for a year before deciding to release it to the public. My product was polished. Ticksy was ready for the limelight.
After many months of testing, developing a marketing site (powered by WordPress), creating a marketing video (with the help of VideoHive’s own Bryan Drake) and planning everything to the last detail, we launched Ticksy to the public in March.
The Ticksy dashboard, 2012
If you’re a designer, you know the feeling of taking a product live. It’s amazing. But it’s also a bit scary. What if people don’t like it? What if it doesn’t sell?
Thankfully, with Ticksy, my fears were quickly assuaged. The initial group of users loved it, talking up its simplicity, its reliability and even its price point. Like me, they were freed from the headaches of email-based support — without having to turn to a cheaply built free solution or an expensive, bloated “do-it-all” app.
More to ComeAs a designer, it was supremely validating. And it was a reminder to keep working.
A developer’s work is never done. And despite the project’s success, that’s certainly been true for Ticksy.
As initially released, Ticksy was an intuitive, bloat-free, web-based customer support solution that could be customized to match any existing brand. After months of work, it’s still that — and more. Now, end users can label their tickets “public” or “private,” bringing an element of the traditional community forum to support. Search functionality for customers is enabled. And enhanced FAQs, notification systems and better Envato integration are on the way soon. (You can check out our roadmap here.)
Summing it UpTicksy has been a two-year journey of discovery and hard work.
For me, Ticksy has been a two-year journey of discovery and hard work. But it’s been worth it.
What started as a project to simplify my own work has taken on a life of its own. I never expected Ticksy to see the light of day outside my office. But it’s incredibly vindicating to check the growing list of users and read their feedback. Really, it’s every developer’s dream. Ticksy has truly arrived. And, with each user, it’s making web-based customer support just a little bit simpler.
The process of taking an idea from sketch to launch is a significant achievement. If you have any, more specific, questions about the process (such as the tools we used for reporting bugs, version control, etc.), let me know in the comments, and we’ll talk!
This tutorial will cover the basics of CSS regions, which allow us to flow content within various sections of the page. Confused? You probably are; read on!
Demo View It Online CSS Region SupportAt the time of this writing, the CSS Regions specification is a working draft and things may change! Techniques mentioned in this tutorial might change in implementation. The aim of this tutorial is to give a basic understanding of CSS Regions and what might be coming to a browser near you.
Check out caniuse.com for a list of browsers which have some support for CSS Regions. This tutorial will only use webkit/non-vendor-prefixed examples for simplicities sake.
If you find the demo is not working for you it may be because CSS Regions needs enabling, here are instructions for Chrome (should work for Chrome, Chrome Canary & Chromium )
Demonstrating CSS Regions in a simple manner actually requires very little code!
To get into the flow of things lets go through a quick example of how to use CSS Regions.
First, we need some dummy text which is contained within the paragraph tag, below that you’ll notice two empty divs each with a class of “regions”. Using CSS Regions we will allow our “example-text” to flow into the region divs.
<p class="example-text">"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."</p> <div class="regions"></div> <div class="regions"></div>
.example-text {
-webkit-flow-into: example-text-flow;
padding: 0;
margin: 0;
}
.regions {
-webkit-flow-from: example-text-flow;
border: 1px solid black;
padding: 2px;
margin: 5px;
width: 200px;
height: 50px;
}
If you try viewing the code above in a Webkit browser that supports CSS Regions, you should see something like:
All styling (apart from -webkit-flow-from) which has been applied on the “regions” class is only there to make it evident where the regions are, the two lines of CSS that we really care about are:
-webkit-flow-into: example-text-flow; -webkit-flow-from: example-text-flow;
These will be discussed in more detail below
Theflow-into Property and Named Flows
The flow-into property accepts an identifier as its value, once we apply the flow-into property onto an element and pass it an identifier, that element then becomes part of a “named flow”. As soon as an element becomes part of a named flow it is taken out of its usual flow – this essentially means the element will not be rendered on the page, for example:
<p class="example-text">I will not be rendered on the page</p>
.example-text {
-webkit-flow-into: example-text-flow;
}
There are some exceptions to this however, if the flow-into property is given the value ‘none’ the element will not be part of a named flow and thus be subject to usual rendering on the page. As you might have guessed, another case where an element which is part of named flow will be displayed is when it has a valid region associated with it.
Multiple elements can be put into the same named flow:
#example-1 {
-webkit-flow-into: my-named-flow;
}
#example-2 {
-webkit-flow-into: my-named-flow;
}
If there is an associated region for “my-named-flow”, an attempt will be made to flow the two elements (#example-1 & #example-2) into their corresponding region chain.
It’s not just simple bits of text which can be put into a named flow, we can put images too! Having the ability to put all sorts of HTML elements into named flows is a powerful thing. For example, let’s say we had two individual lists but from a presentation point of view we wanted to show them together. One way to go about this is to use CSS selectors to target only the portions we need and put them into a named flow, example (extract of code):
<article id="css3">
<header><h4>CSS3</h4></header>
<ul>
<li>Media Queries</li>
<li>Text Shadows</li>
<li>3D Transforms</li>
</ul>
</article>
<article id="html5">
<header><h4>HTML5</h4></header>
<ul>
<li>Canvas</li>
<li>Drag-and-drop</li>
<li>Web Storage</li>
</ul>
</article>
<div id="buzzwords-region"></div>
/* Only put the list within the article into a named flow */
#css3 > ul {
-webkit-flow-into: buzzwords-flow;
}
#html5 > ul {
-webkit-flow-into: buzzwords-flow;
}
#buzzwords-region {
-webkit-flow-from: buzzwords-flow;
border: 1px solid green;
}
(Demo of the above.) If you look at the source code you’ll notice that our HTML5 & CSS3 lists have been merged into a new buzzword list. Take note of how the headers of those lists were not targeted in the CSS selectors and thus are not part of any named flow, they are therefore still rendered on the page and are not contained within a region.
flow-from Property
The flow-from property is able to take a container, for example an empty div and turn it into a region. For it to turn into a valid region it must have an associated named flow. As discussed, named flows are created by using the flow-into property and then using an identifier as its value.
<div class="region"></div>
.region {
-webkit-flow-from: my-named-flow;
}
Similar to flow-into property, we can assign the value of ‘none’ to the flow-from property, this makes the corresponding element nothing more than a container e.g. it will not act as a region.
When applying the flow-from property on an element, it will not have a height or width which is why you’ll need to set these in order to view the contents of the region. Regions may only act as region chains for a single named flow, so multiple elements may flow-into a single named flow however a region can only flow-from a single named flow. A region will maintain the styles of its source element; this means if the source element has the colour value of red the text will continue to appear red when rendered as part of a region.
The CSS Region specification has been considering using @region rules for specific region styling.
“An
@regionrule contains style declarations specific to particular regions.”
This could allow for CSS syntax (similar to that of media queries) such as:
/* A regular style on all paragraph tags */
p {
font-weight: normal;
}
@region #my-region {
/* When text from the p element is flowing into #my-region, use the styling below */
p {
font-weight: bold;
}
}
Being able to do this would allow region specific styling to be applied on the source, currently, setting for example “font-weight: bold” on a region will not cause content displayed within it to be bold – the styling must be applied on the source element.
JavaScript APIAt the time of this writing, there is some support in Webkit-based browsers to access CSS Region information through JavaScript. Using JavaScript, we can pick out a flow according to its identifier and find out what regions are associated with it. We can even find out the status of a particular region, there may be a use case where empty regions need specific handling, using JavaScript we can accomplish this.
Accessing a flow by nameA named flow can be accessed in JavaScript using document.getFlowByName(‘identifier’) (You can try these in the developer console in the demo.)
var flow = document.webkitGetFlowByName('example-1-text');
The returned object is something called a WebkitNamedFlow. flow.contentNodes will contain an array of HTML Elements which are being used as the source for the flow being accessed.
Discovering which regions belong to which content nodeThe ability to detect which content belongs to which regions can prove useful in various situations.
var flow = document.webkitGetFlowByName('example-4-text');
var main_content = document.querySelector('#example-4 .main_content');
flow.getRegionsByContentNode(main_content);
In the code above, we first find our flow on the page using the flow name, then using the Selectors API we access a piece of content on the page and pass it as an argument to getRegionsByContentNode(), this returns a list of elements which are being used as regions for that particular piece of content.
Finding out whether or not a piece of content fits inside a regionWe can very quickly determine the status of a region in regards to how content inside it fits.
//This is not a very efficient selector, see [https:] var region7 = document.querySelector('#example-4 .regions > div:nth-child(7) '); region7.webkitRegionOverflow //"fit"
element.regionOverflow will return different values depending on how it’s handling the source content. There are four possible values: ‘overflow’, ‘fit’, ‘empty’ and ‘undefined’
You can try this out using developer tools in Chrome, on the demo page, right click on one of the blue boxes (a region) and select ‘Inspect Element’. You may find the source element is selected rather than the region div. Try to find the nearest div with the class of ‘regions’ and select one of the child divs (they should appear empty). At this point you can hit the Escape key on your keyboard which should bring up the console; now as a handy shortcut you can type $0 into the console to access the selected element. Now try typing:
$0.webkitRegionOverflow //'overflow', 'fit', 'empty' or'undefined'
A Few Notes and Opinions
Hello! Thanks for reading this tutorial, here are some extra bits of information you may be interested in.
There are dozens of JavaScript testing frameworks, but most of them function in, more or less, the same way. However, Douglas Crockford’s JSCheck is considerably different from most. In this tutorial, I’ll show you how it’s different and why you should consider using it!
Crockford describes JSCheck as a “specification-driven testing tool.
Crockford describes JSCheck as a “specification-driven testing tool.” When using the frameworks you’re used to, you would write a test for a given piece of functionality, and, if that test passes, declare that the given functionality is working correctly. However, it’s possible that you might miss some of edge cases or exceptions that your tests don’t cover.
While uncovering edge cases isn’t the express purpose of JSCheck, it is a nice side benefit. The main idea behind JSCheck is this: the specification you write will actually describe how the code you are testing should work. Then, JSCheck will take that specification (called a claim in JSCheck-lingo), and generate random tests to prove the claim. Finally, it will report the results to you.
Sounds interesting? Read on! Sounds familiar? You might have used the Haskell testing tool, QuickCheck, on which JSCheck was based.
Some Code to TestOf course, before actually writing our claim, we’ll want to have some code to test. Recently, I wrote a mini-password scorer, similar to the functionality on HowSecureIsMyPassword.net. It really isn’t fancy: you just pass the function a password and get a score back. Here’s the code:
passwordScorer.js(function () {
var PasswordScorer = {};
PasswordScorer.score = function (password) {
var len = password.length,
lengthScore = 0,
letterScore = 0,
chars = {}
if (len >= 21) { lengthScore = 7; }
else if (len >= 16) { lengthScore = 6; }
else if (len >= 13) { lengthScore = 5; }
else if (len >= 10) { lengthScore = 4; }
else if (len >= 8) { lengthScore = 3; }
else if (len >= 5) { lengthScore = 2; }
var re = [ null, /[a-z]/g, /[A-Z]/g, /\d/g, /[!@#$%\^&\*\(\)=_+-]/g];
for (var i = 1; i < re.length; i++) {
letterScore += (password.match(re[i]) || []).length * i;
}
return letterScore + lengthScore;
};
(typeof window !== 'undefined' ? window : exports).PasswordScorer = PasswordScorer;
}());
It’s pretty simple code, but here’s what’s going on: the score is made up of two sub-scores. There’s a starting score, that’s based on the length of the password, and then an additional score for each character, 1 point for each lowercase letter, 2 points for each uppercase letter, 3 points for each number, and 4 points for each symbol (from a limited set).
So, this is the code we’re going to test: we’ll randomly generate some passwords with JSCheck and make sure they get an appropriate score.
Writing our ClaimNow we’re ready to write our claims. First, head over the JSCheck Github page and download the jscheck.js file. I like to run my tests in the terminal, via NodeJS, so add this single line to the very bottom of the file:
(typeof window !== 'undefined' ? window : exports).JSC = JSC;
This won’t affect the way the file behaves in the browser at all, but it will make it work as a module within Node. Notice that the jscheck.js file exposes JSC as the single global variable for the whole library. If we weren’t making this adjustment, that’s how we’d access it.
Let’s open passwordScorerSpec.js and start things:
JSC = require("./../vendor/jschec";).JSC;
PasswordScorer = require("./../lib/passwordScore";).PasswordScorer;
Since I’m running these tests in NodeJS, we’ll have to require the modules we want. Of course, you’ll want to make sure that paths match your file locations.
Now, we’re ready to write our first claim. Of course, we use the JSC.claim method. This method accepts three parameters, with an optional fourth. The first parameter is just a string, a name for the claim. The second parameter is called the predicate: it’s the actual testing function. Very simply, this function should return true if the claim is true, and false if the claim is false. The random values that JSCheck will generate for the test will be passed as parameters to the predicate.
But how does JSCheck know what type of random values to hand the predicate? That’s where the third parameter, the specifier comes into play. This is an array, with an item for each parameter for predicate. The items in the array specify what types to give the predicate, using JSCheck’s specifier functions. Here are a few of them:
JSC.boolean() returns either true or false.JSC.character() takes a min and max character and returns a single character from that range. It can also take a single character code and return that character.JSC.integer() will return a prime number. Or, pass it a single parameter to get an integer (whole number) between 1 and the parameter, or two parameters for an integer in that range.You get the idea. There are other specifiers, and we’ll use some now as we write our first claim.
JSC.claim("All Lowercase Password";, function (password, maxScore) {
return PasswordScorer.score(password) <= maxScore;
}, [
JSC.string(JSC.integer(10, 20), JSC.character('a', 'z')),
JSC.literal(26)
]);
Our first parameter is a name. The second is the testing function: it receives a password and a max score, and returns true if the score for that password is less than or equal to the max score. Then, we have our specifier array. Our first parameter (the password) should be a string, so we use the JSC.string() method: it can take two parameters, the number of characters in the string, and value for those characters. As you can see, we’re asking for a password between 10 and 20 characters. For the value, we’re using the JSC.characters() method to get random characters between ‘a’ and ‘z’.
The next value is our maxScore parameter. Sometimes, we don’t want the randomness that JSCheck offers, and this is one of those times. That’s why there’s JSC.literal: to pass a literal value the predicate. In this case, we’re using 26, which should be the max score for any all-lowercase password between 10 and 20 characters.
Now we’re ready to run the test.
Running our ClaimBefore we actually run the claim and get the report, we have to setup the function that will receive the report. JSCheck passes the report to a callback function of JSC.on_report. Hence:
JSC.on_report(function (str) {
console.log(str);
});
Nothing fancy. Now, all that’s left is to call JSC.check(). Now, we can head to our terminal and run this:
node path/to/passwordScorerSpec.js
Behind the scenes, JSCheck runs the predicate 100 times, generating different random values each time. You should see your report printed out.
All Lowercase Passwords 100 of 100 pass 100
They all passed, but that’s not much of a report, eh? Well, if any of our tests had failed, they would have been included in the report. However, you can adjust the level of output with the JSC.detail function: pass it a number between 0 and 4 (inclusive) to get anything for no output to all the test cases. The default value is 3.
Remember how I said that JSC.claim could take a fourth parameter? It’s called a classifier, and it receives the same parameters that the predicate receives. Then, it can returns a string to classify, or group, our test cases. I’ll admit I wasn’t really sure where this would be useful until I was creating the above example claim. See, I made a mistake in the predicate and compared the score to the maxScore with the operator instead of the operator, so any passwords that scored 26 points were failing. I was seeing reports that looked something like this:
All Lowercase Passwords 96 of 100
FAIL [12] ("vqfqkqqbwkdjrvplkrx";,26)
FAIL [21] ("nhgkznldvoenhqqlfza";,26)
FAIL [62] ("eclloekuqhvnsyyuekj";,26)
FAIL [78] ("rvrkfivwtdphrhjrjis";,26)
pass 96 fail 4
It’s still not entirely obvious why some tests are failing. So I added a classifier function that grouped the test cases by score: like I said, the function takes the same parameters as the predicate, and it returns a string. Every test case that gets the same string back from the classifier will be grouped together in the report.
function (password, maxScore) {
return PasswordScorer.score(password) + " points";;
}
This function should be the last parameter of our claim. Now, you’ll get a report that’s something like this:
All Lowercase Passwords 96 of 100
FAIL [4] 26 points:("illqbtiubsmrhxdwjfo";,26)
FAIL [22] 26 points:("gruvmmqjzqlcyaozgfh";,26)
FAIL [34] 26 points:("chhbevwtjvslprqczjg";,26)
FAIL [65] 26 points:("kskqdjhtonybvfewdjm";,26)
14 points: pass 8
15 points: pass 5
16 points: pass 12
18 points: pass 10
19 points: pass 12
20 points: pass 11
22 points: pass 12
23 points: pass 8
24 points: pass 10
25 points: pass 8
26 points: pass 0 fail 4
You can see how the tests are grouped by how many points the passwords are worth. Now, it’s easy to see that the only passwords that fail the tests are the passwords that score 26 points. And while the problem here was with the test, and not the code, it still shows how it can be useful to add a classifier function to your claims.
Final ThoughtsSo, at the end of the day, it JSCheck worth using? Here’s what I think: it’s not something you’re necessarily going to use with every code base, but sometimes you’ll find it useful to be able to create random test cases that will rigorously test a given piece of code. When that’s what you want to do, I haven’t seen a tool better for that than JSCheck.
JSCheck has a few other options and a bunch of specifiers that we haven't reviewed in this tutorial; head over to JSCheck.og to read about those. Otherwise, I’d love to hear your thoughts about JSCheck in the comments!
This tutorial will help you prepare a Magento install for high traffic, better load times, and simpler ongoing site management. Ready?
RequirementsYou can download the finished code for this tutorial, or launch the “magento-basic” Quickstart from your Pagoda Box account to test a working site.
Step 1: Set Up Git Locally (SFTP will work as an alternate)Fair Warning: This tutorial may change your life. Pagoda Box is not traditional hosting. The teachings in this article will not only help scale Magento, but it also lays the groundwork for a progressive development-to-production workflow.
Note: If you already use Git, you can skip this section. If not, the guide Setting Up Git provides specific instructions for creating an SSH Key, as well as links for downloading and installing Git (also below).
While it is possible to use just SFTP on Pagoda Box, the officially recommended (and most efficient) workflow integrates Git into your daily development. Git enables features like collaboration, uniform code distribution, deploys, deploy history and rolling back code. While most of these features are available to FTP users, using Git makes integration seamless.
If you want to fully take advantage of Pagoda Box, download Git, and Learn the Basics. Depending on your operating system, set up may vary slightly. Regardless of your OS, the commands are identical once Git is installed.
Using Git to manage collaboration and version control may involve a brief learning curve. However, there are generally only three commands we’ll use on an ongoing basis to commit changes locally, then deploy to Pagoda Box:
git add . – Adds local files to your repositorygit commit -m "some message about what you've done" – Commits your changesgit push pagoda --all – Pushes changes to Pagoda Box Repository (auto-deployed by default)We’ll use these later.
Step 2: Install the Pagoda Box Terminal Client
*
/ \
/ \
+_/ / / | \ \ \_+
||*|||*||
|+||*||+|
/ \
+_/ / / | \ \ \_+
||*|||*||
|+||*||+|
____ _ ____ ___ ____ _ ____ _____ __
| _ \ / \ / ___|/ _ \| _ \ / \ | __ ) / _ \ \/ /
| |_) / _ \| | _| | | | | | |/ _ \ | _ \| | | \ /
| __/ ___ \ |_| | |_| | |_| / ___ \| |_) | |_| / \
|_| /_/ \_\____|\___/|____/_/ \_\____/ \___/_/\_\
Welcome To Your Pagoda Box Terminal Client.
-----------------------------------------------
-----------------------------------------
---------------------------------
Enjoy.
Pagoda Box provides a Terminal Client that lets you clone, create, deploy, destroy, rename and rollback an application from the command line. Later in this tutorial, we’ll use the client to create a secure tunnel to the live Magento database with Sequel Pro (the process is similar for other database managment tools like HeidiSQL).
The Pagoda Box Terminal Client is a rubygem, so installation is pretty simple. First off, Ruby needs to be installed. Installation is different for each operating system.
Once Ruby is installed, simply run the following command to install the Pagoda RubyGem:
On Mac or Linux:
$ sudo gem install pagoda
On Windows:
$ gem install pagoda
Then, to verify you have the Pagoda Gem installed properly, run:
$ pagoda list
If this is the first time you’ve used the Gem, it will ask for your Pagoda Box Username and Password. After you’ve entered those, expect to see a list of your Pagoda Box applications. If you haven’t created any applications, the list will be blank.
If you get an error, it’s most likely invalid credentials. You can verify or change which credentials the gem uses by editing the file located on your local computer at ~/.pagodarc. Make sure to exactly match the credentials you use in your Pagoda Box account. (Note: this is a hidden file, so you’ll need to enable hidden files or open via the terminal. Also note that the file stores your credentials twice, so edit both if needed.)
Note: Skip this step if you already have a working local Magento install.
If you don’t have it already, ensure you are using local webserver and database management software. There are several options available, depending on your operating system. A common option for Mac is MAMP, or WAMP for Windows. Both are free and easily set up.
Once your local development environment is set up, go ahead and download Magento, then follow the official guide to install Magento locally.
Feel free to use Magento’s auto install script to set up the application in your local environment. However, due to Pagoda Box’s distributed cloud architecture, the script will not install Magento directly in your production environment. The Pagoda Box workflow and architecture requires you to make code modifications locally, commit, then deploy to production. This workflow accommodates collaboration and development > staging > production best practices.
Step 4: Configure PHP Using a Boxfile
Note: On Pagoda Box, a YAML Boxfile can be included in the root of your code repository. While the Boxfile is optional, it does provide advanced features, like manipulating your hosted environment on each deploy. We’ll use the Boxfile extensively in this Tutorial to simplify tasks, and to make the respository reusable on Pagoda Box.
Create a file named “Boxfile” in the root of your local Magento installation, then copy the following into your Boxfile (explanation below):
web1:
name: mag-app
shared_writable_dirs:
- media
- var
php_version: 5.3.8
php_extensions:
- pdo_mysql
- mysql
- simplexml
- mcrypt
- hash
- gd
- dom
- iconv
- curl
- soap
Create / Name the Web Cluster
This Boxfile serves several purposes. First, it creates a web1 component, then names it mag-app.
Second, the Boxfile identifies media and var as shared writable directories. This allows users to upload images, video, and other media to a distributed Magento cloud site without instances writing themselves out of sync.
When a directory is marked as writable, the contents are no longer deployed to Pagoda Box from your local repository. Any time local files need to be deployed to these directories, they must be manually copied via SSH or SFTP. You may also use SSH/SFTP to transfer files from Pagoda Box to your local machine as needed.
PHP Version and ExtensionsThe Boxfile also declares which PHP version and extensions will be included in your web instances as they deploy. This way, both the environment and the application are versioned together, so rolling back to a previous deploy includes the correct PHP version and extensions. The list of PHP extensions in this Boxfile was taken from Magento’s official system requirements.
Tip: Once Git is installed in your local environment, use the
.gitignorefile to ignore the writable directories specified in your Boxfile. Identifying these directories inside the.gitignorefile helps reduce the size of your repo, and your deploy time. In addition to the writable directories, you can also add thedownloaderdirectory to the.gitignorefile, since it’s used locally, and not on Pagoda Box.
Once you’ve installed Git and the Terminal Client, configured the Boxfile and finalized your local source code, you’re ready to launch on Pagoda Box.
Step 5: Create a Free Pagoda Box AccountIf you don’t already have one, create a free Pagoda Box account. You will not need to enter a credit card to install Magento for testing.
If you have not already done so, follow this guide to Add an SSH Key in your Pagoda Box Admin panel. The guide will provide specific instructions for setting up an SSH Key on either Mac or Windows.
Step 6: Upload Magento to Pagoda BoxOnce you’ve created a Pagoda Box account and set up an SSH Key, go to the Home Page in your new account and click the “New Application” button to create a new application.
Note: This tutorial names our sample application “magento”. The app name is also used for the Pagoda Box repository, the subdomain for the freshly deployed application (magento.pagodabox.com), and the username in SFTP mode. Replace “magento” with “your-app-name-here” where appropriate throughout the remainder of this tutorial.
Upload to an Empty Repo (recommended for this tutorial)
Next, choose from the 3 options to launch your Magento site. Since you already have a customized version of Magento locally, select ‘Empty Repo’ to deploy using SFTP or Git, name your application, and click “Launch Application”.
You’ll be asked to select your preferred deployment method (Git or SFTP). Click on your preference, and follow the instrutions on-screen.
Git OptionYou can copy and paste the on-screen instructions from the Pagoda Box dashboard to your terminal after using Terminal to change directory (cd) to the root of your project.
The pasted commands do the following:
git init – Initialize your Magento project as a Git Repositorygit add . – Add all files from the project to the repogit commit -m 'your commit message' – Commit files with a message that allows you to quickly scan deploy history in the future, in case you need to revert or modify changesgit remote add pagoda git@git.pagodabox.com:magento.git – Add Pagoda Box as a remote (the specific git url for your application appears on both this screen, and in your app dashboardgit push pagoda --all – Push your local code to the Pagoda Box remote repository. As long as you’re on the “master” branch (which is the default), Pagoda Box will automatically deploy your code, and carry out the instructions we set in the Boxfile. Auto-deploy can be turned off in the Admin dashboard, or configured to deploy automatically from a Git branch other than Master.If you opted for SFTP, Pagoda Box will guide you through establishing credentials and a password. Connect via SFTP to Pagoda Box, and upload your Magento source code in the code directory.
Step 7: Create a Database
There are two ways to create a database on Pagoda Box. Each has benefits, explained below:
Create a DB in the BoxfileThe Boxfile will automatically create a database component on deploy, as long as that component (db1, db2, etc.) doesn’t already exist. Declaring the database in the Boxfile saves a bit of time now, and makes deploying multiple Magento sites from a standardized code base much simpler in the future. (Note: Only cloud DBs can be deployed from the Boxfile. If you need a larger, dedicated or redundant database, see the Dashboard option later in this Step.) Add the following to your Boxfile:
db1: name: mag-db type: mysql
Your updated Boxfile should look like this:
web1:
name: mag-app
shared_writable_dirs:
- media
- var
php_version: 5.3.8
php_extensions:
- pdo_mysql
- mysql
- simplexml
- mcrypt
- hash
- gd
- dom
- iconv
- curl
- soap
db1:
name: mag-db
type: mysql
Then commit changes to the updated file and push changes to Pagoda Box:
$ git commit -m "pagoda config" $ git push pagoda --allAlternate: Create a DB in the Dashboard
You can also create a database from the Pagoda Box Dashboard. This is where you add a larger, dedicated or redundant database.
First, click “Add Database” in the Dashboard.
Pagoda Box will step through a series of screens to configure your database, depending on your choices. If you’ve chosen the Dedicated option, you will be asked to size your database as follows:
Cloud databases usually deploy within minutes. If you chosen Dedicated, don’t get impatient. You may wait for up to 90 minutes for a big server to be provisioned to your specifications.
Step 8: Configure DB Credentials for ProductionYour database automatically generates credentials when it’s created on Pagoda Box. We’ll use those credentials to configure Magento in production.
However, since Magento will be used in both local environments and in production, we need to supply different database credentials for each. We’ll use Deploy Hooks in the Boxfile to simplify this process by executing scripts or commands during deploy.
In the case of Magento, we’ll swap the local.xml file upon deploy. That way, without manually switching credentials, the app/etc/local.xml file will automatically have local database credentials in development, but production database credentials on Pagoda Box.
First, create a directory named pagoda in root, then copy Magento’s app/etc/local.xml to the new directory.
Next, edit local.xml to include Pagoda Box database credentials from your account dashboard. Note that Pagoda Box uses 3 levels of authentication, so that even if your credentials are compromised, other users cannot access your database.
Swap local.xml Configs on Deploy
Add the following into your Boxfile, under the web1 section to create the Deploy Hook.
after_build: "mv pagoda/local.xml app/etc/local.xml"
Your updated Boxfile should look like this:
web1:
name: mag-app
shared_writable_dirs:
- media
- var
php_version: 5.3.8
php_extensions:
- pdo_mysql
- mysql
- simplexml
- mcrypt
- hash
- gd
- dom
- iconv
- curl
- soap
after_build:
- "mv pagoda/local.xml app/etc/local.xml"
db1:
name: mag-db
type: mysql
Then commit changes and push to Pagoda Box:
$ git add . $ git commit -m "pagoda config" $ git push pagoda --allStep 9: Migrate the Database
Export Your Local DBWith the same tools you use to manage a local database, you can securely manage a live database on Pagoda Box. We’ll use Sequel Pro for this example, but the process is similar for tools like HeidiSQL.
When the Magento install script ran locally, it created several tables in the local database. Those tables need to be migrated to production.
First, export your local database using your database manager: File > Export.
Now choose a location, and Save the export.
Establish a Secure DB ConnectionNow establish a database tunnel. Using the Pagoda Box Terminal Client, specify the app whose database you are trying to access, and the ID of the database component (e.g. db1), as in this example:
$ pagoda -a magento tunnel -c db1 --OR-- $ pagoda --app=magento tunnel --component=db1
Once the tunnel is established, use Sequel Pro (or similar) to connect to the database using the Host and Port provided by the Pagoda Terminal Client…
And the username and password in your Pagoda database credentials. These were automatically created with your database, and may be found in the Pagoda Box Dashboard under the database component (see example in Step 8).
Import and Update the Production DBNext, import your database into production using Sequel Pro (or similar): File > Import. Now select the database export file, and Open.
Finally, since we ran the install script locally, it’s necessary to adjust the base url directly in the database before browsing the site. While you are still connected to the Pagoda Box database in Sequel Pro, navigate/filter to the core_config_data table and edit the value for the following paths:
web/unsecure/base_url web/secure/base_url
The values for each should look something like this:
Step 10: Configure Mail
To protect your IPs from being flagged as spam, Pagoda Box uses the SMTP mail protocol to send email via third party mail provider SMTP credentials. In English, that means you need a company (like Gmail) that provides mail services.
Regardless of which mail provider you choose, enter account credentials from that provider in your Pagoda Box dashboard. It should look something like this:
Step 11: Cron Jobs (Optional)
A few recurring tasks in Magento (e.g. sending newsletters, log cleaning, customer notifications, etc.) need to happen periodically. The cron.php file located in Magento’s root will trigger these tasks. We’ll set up a Cron Job in the Pagoda Box admin panel to run cron.php every 15 minutes. (Note: To configure Magento specific tasks, see their Official Guide.)
Cron Jobs can be added or updated via the Boxfile, then deployed to Pagoda Box. To schedule a task at 15 minute intervals, add the following to your Boxfile under the web1: component (change the “magento” to point to your own app name / subdomain):
cron:
- "*/15 * * * *": "curl -s -o /dev/null [magento.pagodabox.com]
Your updated Boxfile should look like this:
web1:
name: mag-app
shared_writable_dirs:
- media
- var
php_version: 5.3.8
php_extensions:
- pdo_mysql
- mysql
- simplexml
- mcrypt
- hash
- gd
- dom
- iconv
- curl
- soap
after_build:
- "mv pagoda/local.xml app/etc/local.xml"
cron:
- "*/15 * * * *": "curl -s -o /dev/null [magento.pagodabox.com] db1:
name: mag-db
type: mysql
Alternate: Cron Jobs in the Dashboard
In the Pagoda Box admin panel under the Cron tab, add the following (change the “magento” to point to your own app name):
Command: curl -s -o /dev/null [magento.pagodabox.com]
Schedule: */15 * * * *
It should look like this:
Part 2 – Optimization: Redis, Scaling & Benchmarking
You’ve already gotten the heavy lifting out of the way. Your Magento application is scalable, and changes are easily deployed across all instances with $ git push pagoda --all.
In the follow-up article, we’ll optimize Magento, add a Redis cache, SSL and Domain aliases, then scale the application for benchmarking and production. See you soon!
As Tuts+ Premium continues to sky-rocket, our ability to post better, more frequent, content has improved dramatically as well. This week, we have a variety of fun new things available to members!
Course – How To Be A Terminal Pro
In this fifteen-episode course, you’ll learn how to take advantage of that scary app you never touch: Terminal! We’ll begin with the obligatory “hello world” command, and work our way up to advanced usage.
Course – JavaScript Testing With Jasmine
Admit it: you say that you test your JavaScript, but, in reality, you…don’t. That’s okay; the idea of testing JavaScript is a relatively new thing. And unfortunately, there aren’t too many “hold your hand” resources for getting up and running. Well, that all changes with this course. We start from scratch, and slowly work our way up the testing chain.
Along the way, we’ll take advantage of the fantastic Jasmine BDD (Behavior-Driven Development) framework to make our tests as readable as possible. Upon completion of the course, you’ll not only have a robust test suite at your fingertips, but your tests will also make for fantastic documentation!
eBook - Client Centric Web Design
Other than Internet Explorer, what is the most challenging part of your job? Did you answer clients? If so, this eBook, by Paul Boag, is for you. Discover how to keep your clients happy, maintain your own sanity and produce the most effective websites you have ever built, resulting in happier clients, better websites, and improved job satisfaction.
eBook - Building Websites for Return on Investment
‘Building Websites for Return on Investment’ uncovers the secrets of sites that successfully generate real return on investment. This book will enable you to transform your website from an expense to a measurable source of income.
Article – Are You Using CoffeeScript?
For those who haven’t yet had the chance to dig into CoffeeScript, this article will get you up and running in no time!
Coming Soon to Tuts+ PremiumNothing above pique your interest? That’s okay; we have plenty of amazing new content on the near horizon, including the following courses:
Already a member? What requests do you have? Haven’t signed up yet? Well now’s the perfect time!
This tutorial will cover the process of creating a basic Joomla module from an existing widget. We’re going to take a widget from Mixcloud, learn how to convert it into a Joomla module and, finally, distribute the extension to JED (Joomla Extension Directory).
If you’ve never created a Joomla module before, this article is the best place to start!
Step 1: Setting up Our Files
Basic Files
For every module created for Joomla, there are two files that absolutely need to be present. One file is a configuration XML file that will hold the module details and parameters and a PHP file that will control our module. For the purpose of this tutorial, we will create a folder called mod_mixcloud_widget and, within it, using your favorite code editor, create mod_mixcloud_widget.php and mod_mixcloud_widget.xml. Before we move on to the next step, create a single HTML file named index.html.The index.html file that was created will be used to hide the contents of the module folder when viewing via a browser.
Now that you’ve added the core files, it’s time to add the template files. We do this by creating a folder called tmpl. Within that folder, we create default.php and index.html. The file default.php will be the module template that will take generated information and output them into clean HTML format on the page.
Lastly, while inside our root module folder, we create a new folder called language. This folder will have two files: en-GB.mod_mixcloud_widget.ini and en-GB.mod_mixcloud_widget.sys.ini which will be used to make our module internationable with the option of having different languages.
After following each step, you should have the following file structure
Each Joomla Extension installed has an XML file, which is referred to as a manifest or Install file. This file contains metadata details, such as author, version, description etc. It is also used as a configuration file for module prameters. For the purpose of this tutorial, we will be creating a manifest file for a Joomla 2.5 module. Add the following snippet to your XML file.
<?xml version="1.0" encoding="utf-8"?> <extension type="module" version="2.5" client="site" method="upgrade"> <name>MOD_MIXCLOUD_WIDGET</name> <author>B4ucode</author> <creationDate>May 2012</creationDate> <copyright>Copyright (C) 2011 - 2012. All rights reserved.</copyright> <license>GNU General Public License version 2 or later;</license> <authorEmail>info@b4ucode.com</authorEmail> <authorUrl>www.b4ucode.com</authorUrl> <version>1.0.0</version> <description>MOD_MIXCLOUD_WIDGET_XML_DESCRIPTION</description>
The primary tag extension has a few attributes:
module.new and upgrade. We will be using upgrade, in case of any future updates to the module, it will simply upgrade what is currently there.The other set of tags are metadata tags which hold information about the module which will be used during installation and the administration of the module.
Module FilesAs mentioned before, the manifest file holds information about the files used in the module. During installation, Joomla checks the manifest file for all the files that should be added to the system. If any of the files are missing, then Joomla will give an error explaining the files that are missing. Any files found in the module, that aren’t listed in the XML file are not added to the system with the others. Add the following snippet to your manifest file.
<files> <filename module="mod_mixcloud_widget">mod_mixcloud_widget.php</filename> <folder>tmpl</folder> <filename>index.html</filename> <filename>mod_mixcloud_widget.xml</filename> </files>
Language FilesInstead of writing a line for each file in a folder, we simply use the folder element. This element will instruct the installer to install all the files in this folder.
This element holds the language files that are to be installed with the module. For the purpose of this tutorial, only one language will be used. When there are more languages, you can simply change the prefix of the files and the tag attribute to the exact language based on the Joomla framework.
<languages> <language tag="en-GB">language/en-GB.mod_mixcloud_widget.ini</language> <language tag="en-GB">language/en-GB.mod_mixcloud_widget.sys.ini</language> </languages>Adding Parameters
Some extensions don’t work right out of the box, but need certain settings added: these settings are called, parameters, and are defined in the manifest file.
The first element is config which holds other elements that will be displayed in HTML format. The element called field is the meat of our parameters where you can define what type of form data you wish to display. This element at its basic level is made up of some core attributes:
There are different attributes as they vary per form field type like size, filter, exclude, directory and more.
<config> <fields name="params"> <fieldset name="basic"> <field type="text" name="feed" default="" label="MOD_MIXCLOUD_WIDGET_FEED_TITLE" description="MOD_MIXCLOUD_WIDGET_FEED_DESC" /> <field name="color" type="color" default="" label="MOD_MIXCLOUD_WIDGET_COLOR_TITLE" description="MOD_MIXCLOUD_WIDGET_COLOR_DESC" /> <field name="width" type="text" default="300" size="40" label="MOD_MIXCLOUD_WIDGET_WIDTH_TITLE" description="MOD_MIXCLOUD_WIDGET_WIDTH_DESCRIPTION" /> <field name="height" type="text" default="300" size="40" label="MOD_MIXCLOUD_WIDGET_HEIGHT_TITLE" description="MOD_MIXCLOUD_WIDGET_HEIGHT_DESCRIPTION" /> </fieldset> <fieldset name="advanced"> <field name="layout" type="modulelayout" label="JFIELD_ALT_LAYOUT_LABEL" description="JFIELD_ALT_module_LAYOUT_DESC" /> <field name="moduleclass_sfx" type="text" label="COM_moduleS_FIELD_moduleCLASS_SFX_LABEL" description="COM_moduleS_FIELD_moduleCLASS_SFX_DESC" /> <field name="owncache" type="list" default="1" label="COM_moduleS_FIELD_CACHING_LABEL" description="COM_moduleS_FIELD_CACHING_DESC"> <option value="1">JGLOBAL_USE_GLOBAL</option> <option value="0">COM_moduleS_FIELD_VALUE_NOCACHING</option> </field> </fieldset> </fields> </config> </extension>
Finished XML FileYou may notice that we have written labels and description in some uppercase characters like
MOD_MIXCLOUD_WIDGET_FEED_TITLE. These are strings that we will make translatable when creating our language files.
If you’ve followed thus far, you should have a completed XML File like following
<?xml version="1.0" encoding="utf-8"?> <extension type="module" version="2.5" client="site" method="upgrade"> <name>MOD_MIXCLOUD_WIDGET</name> <author>B4ucode</author> <creationDate>May 2012</creationDate> <copyright>Copyright (C) 2011 - 2012 Open Source Matters. All rights reserved.</copyright> <license>GNU General Public License version 2 or later;</license> <authorEmail>info@b4ucode.com</authorEmail> <authorUrl>www.b4ucode.com</authorUrl> <version>1.0.0</version> <description>MOD_MIXCLOUD_WIDGET_XML_DESCRIPTION</description> <files> <filename module="mod_mixcloud_widget">mod_mixcloud_widget.php</filename> <folder>tmpl</folder> <filename>index.html</filename> <filename>mod_mixcloud_widget.xml</filename> </files> <languages> <language tag="en-GB">language/en-GB.mod_mixcloud_widget.ini</language> <language tag="en-GB">language/en-GB.mod_mixcloud_widget.sys.ini</language> </languages> <config> <fields name="params"> <fieldset name="basic"> <field type="text" name="feed" default="" label="MOD_MIXCLOUD_WIDGET_FEED_TITLE" description="MOD_MIXCLOUD_WIDGET_FEED_DESC" /> <field name="color" type="color" default="" label="MOD_MIXCLOUD_WIDGET_COLOR_TITLE" description="MOD_MIXCLOUD_WIDGET_COLOR_DESC" /> <field name="width" type="text" default="300" size="40" label="MOD_MIXCLOUD_WIDGET_WIDTH_TITLE" description="MOD_MIXCLOUD_WIDGET_WIDTH_DESCRIPTION" /> <field name="height" type="text" default="300" size="40" label="MOD_MIXCLOUD_WIDGET_HEIGHT_TITLE" description="MOD_MIXCLOUD_WIDGET_HEIGHT_DESCRIPTION" /> </fieldset> <fieldset name="advanced"> <field name="layout" type="modulelayout" label="JFIELD_ALT_LAYOUT_LABEL" description="JFIELD_ALT_module_LAYOUT_DESC" /> <field name="moduleclass_sfx" type="text" label="COM_moduleS_FIELD_moduleCLASS_SFX_LABEL" description="COM_moduleS_FIELD_moduleCLASS_SFX_DESC" /> <field name="owncache" type="list" default="1" label="COM_moduleS_FIELD_CACHING_LABEL" description="COM_moduleS_FIELD_CACHING_DESC"> <option value="1">JGLOBAL_USE_GLOBAL</option> <option value="0">COM_moduleS_FIELD_VALUE_NOCACHING</option> </field> </fieldset> </fields> </config> </extension>Step 3: Creating
mod_mixcloud_widget.php
The first thing you want to with your module is to add your copyright notice. If you intend to submit your module to JED [Joomla Extension Directory], you should add some GPL license information.
This is one of the checks done before approving a module into the directory. Directly below this, I use the statement defined('_JEXEC') or die; which is used in most PHP files to protect against hackers. This is also another requirement for approval on JED.
<?php
/**
* @package B4ucode
* @subpackage mod_mixcloud_widget
* @copyright Copyright (C) 2011 - 2012 B4ucode, Inc. All rights reserved.
* @license GNU General Public License version 2 or later;
*/
// no direct access
defined('_JEXEC') or die;
Next, we define our paramaters as variables. Developers sometimes define their paramaters as variables in order to have cleaner template files. In order to call a paramaters we use the $params->get() function and get the param name defined in our manifest file. Finally, we call the module Helper function, getLayoutPath, which will render our module template. The first argument of the function takes the module name that we are trying to call, then the second argument looks for the template we intend to render for that module. In this case, we get the parameter layout and in the second argument we set the default layout to be default. This layout is the exact name of the file that we have in our tmpl folder.
$width = $params->get('width',300);
$height = $params->get('height',300);
$feed = $params->get('feed');
$color = $params->get('color');
$moduleclass_sfx = htmlspecialchars($params->get('moduleclass_sfx'));
require JmoduleHelper::getLayoutPath('mod_mixcloud_widget', $params->get('layout', 'default'));
?>
Finished File
<?php
/**
* @package B4ucode
* @subpackage mod_mixcloud_widget
* @copyright Copyright (C) 2011 - 2012 B4ucode, Inc. All rights reserved.
* @license GNU General Public License version 2 or later;
*/
// no direct access
defined('_JEXEC') or die;
$width = $params->get('width',300);
$height = $params->get('height',300);
$feed = $params->get('feed');
$color = $params->get('color');
$moduleclass_sfx = htmlspecialchars($params->get('moduleclass_sfx'));
require JmoduleHelper::getLayoutPath('mod_mixcloud_widget', $params->get('layout', 'default'));
?>
Step 4: Creating default.php
At this point, we have created our manifest file with paramaters, took those same parameters and turned them into variables. In this step, we are going to use those variables in our template file. The template file will render all of our HTML for the module. Edit default.php from your tmpl folder and add the following snippets:
<?php /** * @package B4ucode * @subpackage mod_mixcloud_widget * @copyright Copyright (C) 2011 - 2012 B4ucode, Inc. All rights reserved. * @license GNU General Public License version 2 or later; */ // no direct access defined( '_JEXEC' ) or die( 'Restricted access' );
Adding our HTMLIt is recommended that you add the Copyright Notice and Restricted Access line to your PHP files.
For this tutorial, we will need the embed code from Mixcloud. Here is an example on getting the code . Our module’s purpose is to make the code reusable without having to get the embed each time we want to create or modify a Mixcloud Widget on our site. The default embed code comes with some extra information about the widget content which we don’t need. So for this tutorial, I have stripped it down to just the embed script. Paste this embed code to your default.php file.
<div><object width="480" height="480"><param name="movie" value=" [www.mixcloud.com] name="allowFullScreen" value="true"></param><param name="wmode" value="opaque"></param><param name="allowscriptaccess" value="always"></param><embed src=" [www.mixcloud.com] type="application/x-shockwave-flash" wmode="opaque" allowscriptaccess="always" allowfullscreen="true" width="480" height="480"></embed></object></div>Making the Script Configurable
If we wanted to embed one widget permanently, we could just zip up the module right now. However we don’t, so we are going to replace some of the attributes with our module params . We’re going to change the width, height, color and feed. Paste the following snippet over your embed script.
<div><object width="<?php echo $width; ?>" height="<?php echo $height; ?>"> <param name="movie" value=" [www.mixcloud.com] echo $feed; ?>&embed_uuid=&stylecolor=<?php echo $color; ?>&embed_type=widget_standard"></param><param name="allowFullScreen" value="true"></param><param name="wmode" value="opaque"></param><param name="allowscriptaccess" value="always"></param><embed src=" [www.mixcloud.com] echo $feed; ?>&embed_uuid=&stylecolor=<?php echo $color; ?>&embed_type=widget_standard" type="application/x-shockwave-flash" wmode="opaque" allowscriptaccess="always" allowfullscreen="true" width="<?php echo $width; ?>" height="<?php echo $height; ?>"></embed></object> </div>
Step 5: Creating Language FilesTake note of how we are just calling the variables from our controller file.
In step one, you might have noticed that we created a folder, called language, with two files. Then, in step two, we added some translatable text. In this section, we are going to translate that text to English. Insert the following snippet to the ini files, en-GB.mod_mixcloud_widget.sys.ini and en-GB.mod_mixcloud_widget.ini.
; B4ucode ; Copyright (C) 2011 - 2012 B4ucode. All rights reserved. ; License GNU General Public License version 2 or later; ; Note : All ini files need to be saved as UTF-8 - No BOM MOD_MIXCLOUD_WIDGET="Mixcloud Widget" MOD_MIXCLOUD_WIDGET_XML_DESCRIPTION="This module displays the Mixcloud Widget using feed and other paramaters" MOD_MIXCLOUD_WIDGET_ITEMS_LAYOUT_DEFAULT="Default" MOD_MIXCLOUD_WIDGET_FEED_TITLE="Feed Url" MOD_MIXCLOUD_WIDGET_FEED_DESC="Add the link to single/cloudcast" MOD_MIXCLOUD_WIDGET_COLOR_TITLE="Color" MOD_MIXCLOUD_WIDGET_COLOR_DESC="Add Style color" MOD_MIXCLOUD_WIDGET_WIDTH_TITLE="Width" MOD_MIXCLOUD_WIDGET_WIDTH_DESCRIPTION="Width of Widget" MOD_MIXCLOUD_WIDGET_HEIGHT_TITLE="Height" MOD_MIXCLOUD_WIDGET_HEIGHT_DESCRIPTION="Height of Widget"
If you look closely, you will notice that the text I used for the parameters now have an English translation. You can add other files and create translations in other languages!
Step 6: Packaging the ModuleAfter following all the steps thoroughly, your module is now installable, but we’d like to make a checklist of things to do before installation and distribution.
Addindex.html to Folders
It is recommended that you add a index.html file to each folder. This file, as mentioned previously, stops users from viewing the contents of a module folder directly in a browser. Add the following snippet to your files:
<html><body bgcolor="#FFFFFF"></body></html>Comparing our File Structure to the Manifest File
At this stage, it is recommended that you look at the files and folder defined in your manifest file, and make sure they exist in your module folder. Any file that does not exist can stop the installer from installing files, or, it may throw an error.
PackagingAfter our little checklist, we can package the module into a zip file, and install it.
By navigating to the modules manager, and selecting the module, you can modify the paramaters with your desired width, height, color and song feed.
Enable the module and check to see if it operates the way that it should.
Step 7: Submitting to JED Locating the Category
After registering to JED, one of the main things to note is that you cannot add an extension to multiple sections. So, choosing the appropriate section is very important. Find a suitable section by browsing the site then looking at the top left cornder you will see Submit Extension
Filling Out the Details
Once you’ve chosen to submit an extension, there will be a form to fill in all the details about your submission. Review the screenshots below to see the different fields to be filled out, and read the instructions carefully.
Success Image
Once all the appropriate fields and files have been submitted, you will see a message like the screenshot above; it will explain how many submissions are in the queue to be checked prior to yours.
ConclusionNow that you’ve learned how to create a Joomla module from a widget, go forth and code. With the basics of this tutorial, the same idea can be applied to Facebook plugins, Twitter widgets, Social bookmarkers, Youtube video embeds, and so much more. Happy Coding!
There’s a new player in town, and he brought new toys: The PHP World welcomes FLOW3, an enterprise application framework written and backed by the community of the TYPO3 CMS. FLOW3 can be used as standalone full-stack framework for your applications. It’s interesting, because it introduces some concepts of software development that haven’t been adapted to PHP before.
Among these new concepts is “Aspect Oriented Programming”. We will have a look on the theory of the pattern, and will set up a basic FLOW3 Application and weave in our own aspect!
Why Should I Care?If a new framework hits stable, an excellent question to rise might be: What can it do that the tools that I love can’t?
PHP already has an armada of excellent frameworks and most of them claim to be written with separation of concerns in mind. So does FLOW3.
In terms of software development this means, that the classes that are implementing the logic of your application should only care about one thing. For example, a mailer class should send mails. It should not retrieve potential receivers from a database.
All modern frameworks (including FLOW3) push a lot of patterns into the software stack that do a great job at separating the concerns of your business logic; among them the famous MVC that is separating your logic into different layers.
However, an application is not only built on business logic alone. As it grows, you may want to implement additional services, features, plugins or plugins of plugins. You surely don’t want this stuff in your business logic! But what are your options?
Let’s say you want to implement a logging service that writes some stuff to a text file every time a specific set is deleted from the database. Naturally, the logging service is not part of your database layer. But in order to make it work, you have to place some code right there, like this:
/**
* Delete a user
* @param UserModel $user
* @return void
*/
public function deleteUser(UserModel $user) {
$this->loggingService->log('removing user ' . $user->getName());
$this->users->remove($user);
}
Horrible, isn’t it? Let’s face it: The logging service adds dirt to your code. You have to touch the original code in order to implement it and you add an additional dependency, in this case an instance of the LoggingService.
Hence, you’re adding complexity. And you likely will have to add it all over your app (depending on what else you want to log).
You may come up with an pattern like an event dispatcher or a hook system like it’s implemented in Drupal and WordPress and then have your logger listen for events:
/**
* Delete a user
* @param UserModel $user
* @return void
*/
public function deleteUser(UserModel $user) {
$this->eventDispatcher->dispatch(self::USER_DELETED);
$this->users->remove($user);
}
This is better, but it’s still not cool. You’re code is still dependency aware: it has some code with the sole purpose of informing other parties that something happens.
This has some bad side-effects: What if you want to log something, but there’s not an event dispatched at that time? What if you haven’t written that code because you are writing a plugin? You’d have to change the original code – not once, but every time an update is available.
Let’s call the logging service an aspect and see how FLOW3 takes care of the problem.
Aspect WeavingOur logging service is only interested in one thing regarding the existing code: It wants to intercept it in some classes at various points (e .g. when something is deleted). With the implementation discussed above, the existing code controls the points of execution.
Wouldn’t it be nice if the service could define a set of rules at which point it would want to inject itself into the class?
This is a software development concept named Inversion of control. It decouples classes from another, because there’s no need to change existing code when you’re changing the service. The service takes care of this itself.
Let’s say we simply want to execute logging on every method in every class that starts with delete, like deleteUser(), deleteEntry() or whatever. A convenient way would be to write a rule into the logging service and let the framework take care of the correct execution of the code. This is remarkably easy in FLOW3:
/**
* An aspect that is executed on all methods that start with "delete".
*
* @FLOW3\Before("method(.*->delete.*())")
*/
public function logginMethod() {
// Do some logging here.
}
FLOW3 performs some really advanced PHP techniques under the hood.
The rule is named pointcut in AOP and there are some easy-to-learn ways to define them. The one above tells FLOW3 to inject the logging code in every class before every method that starts with delete.
It does so by using the method keyword and by adding wildcards (*) for the class and for parts of the method name. If you’re adding this code in an Aspect Class (as we do later), you’re good to go. You will be able to access properties of the class that you are targeting as well. The targeted class itself remains untouched. It may not yet be written – if it’s added to your app in the future, the rule will then match and the code will be injected.
Maybe you have read the above paragraph twice and are asking yourself: Is this magic? What’s the damn trick? – I sure did, when I was first introduced to AOP!
Well, AOP has been around for some time in the Java world. FLOW3 is heavily inspired by the AOP implementation of Spring, a Java Framework. The word was on the street, that the pattern can’t be transferred to interpreted languages, because it requires compilation.
The trick is, that the framework – or some tools like JAspect – intercepts the compilation process (from Java code to bytecode, that can be executed by the Java runtime) and do an analysis of the defined aspects. Whenever a set of rules (aka the pointcut) matches a method, it is weaved directly into the compiled code. Hence, the framework takes care of merging the code parts together:
FLOW3 did adapt that concept to the world of PHP by introducing an advanced cache. While most frameworks offer caching to increase performance, FLOW3 additionally uses the cache to weave aspects into the original classes.
If you are running your app, FLOW3 delivers the cached classes instead the ones you’ve written. Hence, your original code remains untouched.
This sounds complex – and it is: FLOW3 performs some really advanced PHP techniques under the hood, that are well worth to be explored! But: To you as an application developer, it’s not complicated at all. You don’t have to care, if you don’t want to: FLOW3 monitors your files for changes and if you make some, FLOW3 auto-commits them to the cache upon your next request. If an aspect matches to a method, it’s automatically weaved in – no further configuration needed.
It’s a complex pattern conveniently at your service!
A Few Words About the CacheEvery cool feature comes with a downside – so does FLOW3. At the moment, we just scratched the surface of what FLOW3′s caching-mechanism is capable of, but I want to give you an idea.
Take a look at the following code:
/**
* @FLOW3\Inject
* @var \YourApp\MailerServiceInterface
*/
protected $mailer;
/**
* Sends a mail.
*
* @param \YourApp\Model\User $user
* @param string $message
*/
protected function sendMail(\YourApp\Model\User $user, $message) {
// Sends a mail.
}
The consequence is that PHP is to some degree no longer an interpreted language.
This may look like some code that is just well documented. But it’s much more.
The first property annotation (FLOW3\Inject) tells FLOW3 to look for some class in your app that implements the MailerServiceInterface and injects it here (and maybe configure it before). This pattern is known as Dependency Injection.
The second class defines some type definitions in its comment’s annotations – and FLOW3 uses them. If you’re giving the method some data that can be used to build a User object of your model (for example an array with pure strings from a HTML form), FLOW3 auto-transforms the data safely to a User object and applies some security and validation rules to the second parameter (in this case a string).
This is (among some other tricks) done by weaving in some code into the cache.
The consequence is that PHP is to some degree no longer an interpreted language. The process of caching is somewhat similar to compiling and it has the same downsides: You can’t just save & refresh your browser, you have to wait for the cache to be warmed up during development. If you are committing code to your production environment, you have to flush the cache in order to see the changes.
On top of that, FLOW3 adds some kind of type-validation at runtime: You can’t pass an integer as second argument in the example above – it’ll throw an error. I really like this, because I have a background in strictly typed languages like Java and ActionScript. It also adds a lot of security to your application. But if you’re a PHP purist, this may be something that you don’t want.
Setting Up Flow3Alright, enough with theory. Let’s get our hands dirty.
Setting up FLOW3 is pretty straightforward, if you already have a PHP-capable server like XAMP or MAMP installed. Just grab a copy of the framework from FLOW3′s download site (or clone from git) and unpack it into your htdocs directory.
You should already be done now on Windows machines. Unix-based systems like MAC or Linux need to grant the required rights for building the cache. FLOW3 ships with a powerful command line tool, that packs this task into a one-liner:
# On linux: ./flow3 core:setfilepermissions chris www-data www-data # On Mac: ./flow3 core:setfilepermissions chris _www _www
Please replace chris with your username! This should be enough. However, I’ve installed FLOW3 on
various machines and have run into some problems. The most common ones are:
If you run into problems, that are not listed here, please post them in the comments!
Now fire up your favorite browser and navigate to [localhost] FLOW3 should welcome you with an introduction screen:
This is the Welcome Package. Every application in FLOW3 (and FLOW3 itself) is a package and packages can share code with each other. For example, once the new TYPO3 Version is finished, you can add it as a package and your application will then have an enterprise-ready CMS on top!
Let’s start our own package. It’s an easy task, because FLOW3′s command line tool will handle most of the work! Type in the following command:
# On linux / MAC ./flow3 kickstart:package Nettuts.AspectDemo # On Windows flow3 kickstart:package Nettuts.AspectDemo
This will create a ready-to-go package named Nettuts.AspectDemo in FLOW3′s Packages/Application folder. If you review the file structure, you will find many folders that do follow a specific convention.
The important ones are:
Building a full-featured app is beyond the scope of this tutorial, so we’ll go with the defaults. Please open the following URL: http://localhost/flow3/Web/index.php/Nettuts.AspectDemo
This is not much, but it’s a start. We want to write something to a logfile, every time this site is called. We do this by weaving in an aspect into the StandardController!
Writing a First AspectTake a look at the StandardController in theClasses/Controller Folder in your Nettuts.AspectDemo Package.
It implements a method, named indexAction(). FLOW3 has automatically configured our app in a way, that this method inside the StandardController gets called when we enter the URL above.
For the moment, we see that this method assigns some strings to the view:
/**
* Index action
*
* @return void
*/
public function indexAction() {
$this->view->assign('foos', array(
'bar', 'baz'
));
}
If you open the file index.html of this view in Resources/Private/Templates/Standard inside our package, you’ll see how these strings are processed by the templating engine.
However, our goal is to intercept the code execution before this method gets called.
At first, create an empty file named Access.log in FLOW3′s Log folder at flow3/Data/Logs. You may save this file wherever you want, but this is a nice place for log files.
Next, create a folder named Service in your Nettuts.AspectDemo/Classes Folder and inside of it, create another folder, and name it Logging. Then create a PHP file named LoggingAspect.php inside of it. It should have the following content:
<?php
namespace Nettuts\AspectDemo\Service\Logging;
use TYPO3\FLOW3\Annotations as FLOW3;
/**
* @FLOW3\Aspect
*/
class LoggingAspect {
/**
* Log a message if this site is called.
*
* @param \TYPO3\FLOW3\AOP\JoinPointInterface $joinPoint
* @FLOW3\Before("method(Nettuts\AspectDemo\Controller\StandardController->indexAction())")
* @return void
*/
public function logSiteAccess(\TYPO3\FLOW3\AOP\JoinPointInterface $joinPoint) {
$filePath = '/home/chris/www/flow3/Data/Logs/Access.log';
$message = 'The site has been accessed at ' . time() . '.' . PHP_EOL;
file_put_contents($filePath, $message, FILE_APPEND);
}
}
?>
This is a very basic class, and if you’d like to have an advanced logger, you may want to separate the logging from the intercepting aspect.
What’s going on here? At first, we define a namespace, which is your package name and the path to the actual class (without the class folder itself). Next, we import FLOW3′s annotation namespace. Thus, we can use FLOW3′s powerful annotation parser to configure our class.
That’s exactly what we do in the class comments. We define the class as aspect with the following annotation:
/** * @FLOW3\Aspect */
From now on, FLOW3 is aware that this class may define rules for the interception of the code flow and watches our codebase for matches.
This is the rule:
@FLOW3\Before("method(Nettuts\AspectDemo\Controller\StandardController->indexAction())")
We could use regular expressions or some other techniques to match multiple methods or classes, but we know exactly where we want to go: Before the indexAction() Method in the StandardController – and that’s exactly what we’re telling FLOW3.
Note the $jointInterface Parameter of the logSiteAccess() Method, that is provided to us by FLOW3. We don’t use it here, but it contains some useful data about the actual context. For example, if the intercepted method would have arguments, that we may want to use, we could access them like this:
$argument = $joinPoint->getMethodArgument('argumentName');
The code in our logSiteAccess() method, itself, is fairly simple. Just adjust the path to the Access.log to your environment and you’re good to go!
Now, again, open http://localhost/flow3/Web/index.php/Nettuts.AspectDemo – nothing should have changed. But when you open the Access.log file, you should see the following entry:
The site has been accessed at 1335715244.
If you can read this, the aspect has been weaved into the cache. Let’s have a look at the magic!
FLOW3 stores the cache at flow3/Data/Temporary/Development/Cache/Code/FLOW3_Object_Classes.
You will find a class named Nettuts_AspectDemo_Controller_StandardController_Original.php. This is the original class from our codebase.
But there is another class: Nettuts_AspectDemo_Controller_StandardController.php. This class extends our original class and it is build up purely by generated code. Among the many lines you’ll find something like this:
$this->FLOW3_Aop_Proxy_targetMethodsAndGroupedAdvices = array(
'indexAction' => array(
'TYPO3\FLOW3\Aop\Advice\BeforeAdvice' => array(
new \TYPO3\FLOW3\Aop\Advice\BeforeAdvice(
'Nettuts\AspectDemo\Service\Logging\LoggingAspect', 'logSiteAccess'
$objectManager, NULL),
),
),
);
This is were FLOW3 initiates the aspect and it’s weaved in directly into your class. The aspect gets executed right where we want it – without touching the original controller.
The code snippet initiates an instance of the BeforeAdvice class, with our aspect, the respective method and the $objectManager as arguments. The object manager contains the needed information to build the jointInterface, that is passed to the aspect.
If you’re interested in the details, have a look at the AOP Folder in the FLOW3 package!
ConclusionThe aspect oriented approach has many advantages waiting to be discovered.
The aspect oriented approach has many advantages waiting to be discovered. For example, FLOW3 uses AOP to intercept its own bootstrap to build up a firewall if you choose to use FLOW3′s security framework. Apart from that, we haven’t yet discussed the specific terminology that is commonly used in AOP.
However, FLOW3 ships with some other unique concepts like Domain-Driven-Design. It has a powerful Doctrine integration and a smart Templating Engine. We have only seen the tip of the iceberg!
What’s your opinion? Are you eager to learn more about the philosophy behind FLOW3? What’s your impression of AOP? Are you excited to have a robust port for the PHP world or do you think it’s too much overhead for an interpreted language?
We’re planning our next few Tuts+ sites, and would love your opinion and advice on which topics you think we should cover next! We’d be really grateful if you could take a minute to answer our quick poll and share your thoughts…
Have Your SayWe’ve been considering lots of different ideas for our next Tuts+ sites over the past few weeks, and wanted to also ask the opinion of our awesome community!
A selection of different concepts are included in the poll to the right, along with the option for you to submit your own ideas as well.
The important thing to note is that these are just ideas. Some of these are close to making our final cut, and others aren’t… We’d love to hear what you think, to help guide our decision.
Thanks for taking the time to offer your suggestion — I can’t wait to see what you have to say!
Win a 6-Month Tuts+ Premium MembershipOur poll will be running for the next couple of weeks, and we’ll be choosing one respondent at random to receive a six-month Tuts+ Premium membership!
To be entered into the giveaway, just leave a comment on this post to go into a bit more detail about your site suggestion. We’ll choose one comment at random to win the Tuts+ Premium membership when the poll ends.
Best of luck!
When you define a function within JavaScript, it comes with a few pre-defined properties; one of these is the illusive prototype. In this article, I’ll detail what it is, and why you should use it in your projects.
What is Prototype?The prototype property is initially an empty object, and can have members added to it – as you would any other object.
var myObject = function(name){
this.name = name;
return this;
};
console.log(typeof myObject.prototype); // object
myObject.prototype.getName = function(){
return this.name;
};
In the snippet above, we’ve created a function, but if we call myObject(), it will simply return the window object, because it was defined within the global scope. this will therefore return the global object, as it has not yet been instantiated (more on this later).
console.log(myObject() === window); // trueThe Secret Link
Every object within JavaScript has a “secret” property.
Before we continue, I’d like to discuss the “secret” link that makes prototype work the way it does.
Every object within JavaScript has a “secret” property added to it when it is defined or instantiated, named __proto__; this is how the prototype chain is accessed. However, it is not a good idea to access __proto__ within your application, as it is not available in all browsers.
The __proto__ property shouldn’t be confused with an object’s prototype, as they are two separate properties; that said, they do go hand in hand. It’s important to make this distinction, as it can be quite confusing at first! What does this mean exactly? Let me explain. When we created the myObject function, we were defining an object of type Function.
console.log(typeof myObject); // function
For those unaware, Function is a predefined object in JavaScript, and, as a result, has its own properties (e.g. length and arguments) and methods (e.g. call and apply). And yes, it, too, has its own prototype object, as well as the secret __proto__ link. This means that, somewhere within the JavaScript engine, there is a bit of code that could be similar to the following:
Function.prototype = {
arguments: null,
length: 0,
call: function(){
// secret code
},
apply: function(){
// secret code
}
...
}
In truth, it probably wouldn’t be quite so simplistic; this is merely to illustrate how the prototype chain works.
So we have defined myObject as a function and given it one argument, name; but we never set any properties, such as length or methods, such as call. So why does the following work?
console.log(myObject.length); // 1 (being the amount of available arguments)
This is because, when we defined myObject, it created a __proto__ property and set its value to Function.prototype (illustrated in the code above). So, when we access myObject.length, it looks for a property of myObject called length and doesn’t find one; it then travels up the chain, via the __proto__ link, finds the property and returns it.
You might be wondering why length is set to 1 and not 0 – or any other number for that fact. This is because myObject is in fact an instance of Function.
console.log(myObject instanceof Function); // true console.log(myObject === Function); // false
When an instance of an object is created, the __proto__ property is updated to point to the constructor’s prototype, which, in this case, is Function.
console.log(myObject.__proto__ === Function.prototype) // true
Additionally, when you create a new Function object, the native code inside the Function constructor will count the number of arguments and update this.length accordingly, which, in this case, is 1.
If, however, we create a new instance of myObject using the new keyword, __proto__ will point to myObject.prototype as myObject is the constructor of our new instance.
var myInstance = new myObject(“foo”); console.log(myInstance.__proto__ === myObject.prototype); // true
In addition to having access to the native methods within the Function.prototype, such as call and apply, we now have access to myObject’s method, getName.
console.log(myInstance.getName()); // foo var mySecondInstance = new myObject(“bar”); console.log(mySecondInstance.getName()); // bar console.log(myInstance.getName()); // foo
As you can imagine, this is quite handy, as it can be used to blueprint an object, and create as many instances as needed – which leads me onto the next topic!
Why is Using Prototype Better?Say, for instance, that we are developing a canvas game and need several (possibly hundreds of) objects on the screen at once. Each object requires its own properties, such as x and y coordinates, width,height, and many others.
We might do it as follows:
var GameObject1 = {
x: Math.floor((Math.random() * myCanvasWidth) + 1),
y: Math.floor((Math.random() * myCanvasHeight) + 1),
width: 10,
height: 10,
draw: function(){
myCanvasContext.fillRect(this.x, this.y, this.width, this.height);
}
...
};
var GameObject2 = {
x: Math.floor((Math.random() * myCanvasWidth) + 1),
y: Math.floor((Math.random() * myCanvasHeight) + 1),
width: 10,
height: 10,
draw: function(){
myCanvasContext.fillRect(this.x, this.y, this.width, this.height);
}
...
};
… do this 98 more times …
What this will do is create all these objects within memory – all with separate definitions for methods, such as draw and whatever other methods may be required. This is certainly not ideal, as the game will bloat the browsers allocated JavaScript memory, and make it run very slowly… or even stop responding.
While this probably wouldn’t happen with only 100 objects, it still can serve to be quite a performance hit, as it will need to look up one hundred different objects, rather than just the single prototype object.
To make the application run faster (and follow best practices), we can (re)define the prototype property of the GameObject; every instance of GameObject will then reference the methods within GameObject.prototype as if they were their own methods.
// define the GameObject constructor function
var GameObject = function(width, height) {
this.x = Math.floor((Math.random() * myCanvasWidth) + 1);
this.y = Math.floor((Math.random() * myCanvasHeight) + 1);
this.width = width;
this.height = height;
return this;
};
// (re)define the GameObject prototype object
GameObject.prototype = {
x: 0,
y: 0,
width: 5,
width: 5,
draw: function() {
myCanvasContext.fillRect(this.x, this.y, this.width, this.height);
}
};
We can then instantiate the GameObject 100 times.
var x = 100,
arrayOfGameObjects = [];
do {
arrayOfGameObjects.push(new GameObject(10, 10));
} while(x--);
Now we have an array of 100 GameObjects, which all share the same prototype and definition of the draw method, which drastically saves memory within the application.
When we call the draw method, it will reference the exact same function.
var GameLoop = function() {
for(gameObject in arrayOfGameObjects) {
gameObject.draw();
}
};
Prototype is a Live Object
An object’s prototype is a live object, so to speak. This simply means that, if, after we create all our GameObject instances, we decide that, instead of drawing a rectangle, we want to draw a circle, we can update our GameObject.prototype.draw method accordingly.
GameObject.prototype.draw = function() {
myCanvasContext.arc(this.x, this.y, this.width, 0, Math.PI*2, true);
}
And now, all the previous instances of GameObject and any future instances will draw a circle.
Yes, this is possible. You may be familiar with JavaScript libraries, such as Prototype, which take advantage of this method.
Let’s use a simple example:
String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g, ‘’);
};
We can now access this as a method of any string:
“ foo bar “.trim(); // “foo bar”
There is a minor downside to this, however. For example, you may use this in your application; but a year or two down the road, a browser may implement an updated version of JavaScript that includes a native trim method within the String‘s prototype. This means that your definition of trim will override the native version! Yikes! To overcome this, we can add a simple check before defining the function.
if(!String.prototype.trim) {
String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g, ‘’);
};
}
Now, if it exists, it will use the native version of the trim method.
ConclusionAs a rule of thumb, it’s generally considered a best practice to avoid extending native objects. But, as with anything, rules can be broken, if needed.
Hopefully, this article has shed some light on the backbone of JavaScript that is prototype. You should now be on your way to creating more efficient applications.
If you have any questions regarding prototype, let me know in the comments, and I’ll do my best to answer them.
With so many folks developing jQuery plugins, it’s not uncommon to come across one that just plain – for lack of better words – sucks. There’s no examples or documentation, the plugin doesn’t follow best practices, etc. But you’re one of the lucky ones: this article will detail the pitfalls that you must avoid.
jQuery is no stranger to those of you frequent Nettuts+. Jeffrey Way’s awesome 30 Days to Learn jQuery (and various other tutorials here and elsewhere) have led us all down the path to Sizzle-powered awesomesauce. In all the hype (and a lot of leaps in JavaScript adoption by developers and browser vendors), plenty of plugins have come onto the scene. This is partially why jQuery has become the most popular JavaScript library available! The only problem is that many of them aren’t too great.
In this article, we’ll focus less on the JavaScript specifically, and more on best practices for plugin delivery.
1 – You Aren’t Making a jQuery PluginThere are some patterns that are, more or less, universally accepted as “The Right Way” to create jQuery plugins. If you aren’t following these conventions, your plugin may… suck! Consider one of the most common patterns:
(function($, window, undefined){
$.fn.myPlugin = function(opts) {
var defaults = {
// setting your default values for options
}
// extend the options from defaults with user's options
var options = $.extend(defaults, opts || {});
return this.each(function(){ // jQuery chainability
// do plugin stuff
});
})(jQuery, window);
First, we are creating a self-invoking anonymous function to shield ourselves from using global variables. We pass in $, window, and undefined. The arguments the self invoking function is called with are jQuery and window; nothing is passed in for undefined, so that if we decide to use the undefined keyword within the plugin, “undefined” actually will be undefined.
This shields from other scripts potentially assigning a malicious value to
undefined, such astrue!
$ is passed as jQuery; we do it this way to ensure that, outside of the anonymous function, $ can still refer to something else entirely, such as Prototype.
Passing the variable for the globally accessible window object allows for more compressed code through the minification processes (which you should be doing, as well).
Next, we are using the jQuery plugin pattern, $.fn.PluginName. This is a way of registering your plugin to be used with the $(selector).method() format. It simply extends jQuery’s prototype with your new method. If you want to instead create a plugin that defines a function on the jQuery object, add it directly, like so:
$.PluginName = function(options){
// extend options, do plugin stuff
}
This type of plugin won’t be chainable, as functions that are defined as properties of the jQuery object typically don’t return the jQuery object. For instance, consider the following code:
$.splitInHalf = function(stringToSplit){
var length = stringToSplit.length;
var stringArray = stringToSplit.split(stringToSplit[Math.floor(length/2)]);
return stringArray;
}
Here, we are returning an array of strings. It makes sense to simply return this as an array, as this is likely what users will want to use (and they can easily wrap it in the jQuery object if they wish). In contrast, consider the following contrived example:
$.getOddEls = function(jQcollection){ //
return jQcollection.filter(function(index){
var i = index+1;
return (index % 2 != 0);
});
}
In this case, the user is probably expecting the jQuery object to return from $.getOddEls; so, we return the filter method, which returns the jQuery collection defined by the function that is passed. A good rule of thumb is to wrap returned elements in the jQuery function, especially if they can be chained; if you are returning arrays, strings, numbers, functions, or other data types, leave them unwrapped.
Arguably, the most important thing you can do when publishing your code is add the necessary documentation. The gap between what you explain to developers and what the code actually does or can do is the time that users don’t want to waste figuring out the ins and outs of your code.
Documentation is a practice that doesn’t have any hard-fast rules; however, it is generally accepted that the more (well organized) documentation you have, the better.
This process should be both an internal practice (within/interspersed throughout your code) as well as an external practice (explaining every public method, option, and multiple use cases thoroughly in a wiki or readme).
3 – You Aren’t Providing Enough Flexibility or CustomizabilityThe most popular plugins offer full access to variables (what most plugins refer to as “options” objects) that a user may want to control. They also may offer many different configurations of the plugin so that it is reusable in many different contexts. For instance, let’s consider a simple slider plugin. Options that the user might wish to control include the speed, type, and delay of the animation.
It’s good practice to also give the user access to classnames/ID names which are added to the DOM elements inserted or manipulated by the plugin. But beyond this, they may also want to have access to a callback function every time the slide transitions, or perhaps when the slide transitions back to the beginning (one full “cycle”).
It’s your job to think of all possible uses and needs for the plugin.
Let’s consider another example: a plugin that makes a call to an API should provide access to the API’s returned object. Take the following example of a simple plugin concep:.
$.fn.getFlickr = function(opts) {
return this.each(function(){ // jQuery chainability
var defaults = { // setting your default options
cb : function(data){},
flickrUrl : // some default value for an API call
}
// extend the options from defaults with user's options
var options = $.extend(defaults, opts || {});
// call the async function and then call the callback
// passing in the api object that was returned
$.ajax(flickrUrl, function(dataReturned){
options.cb.call(this, dataReturned);
});
});
}
This allows us to do something along the lines of:
$(selector).getFlickr(function(fdata){ // flickr data is in the fdata object });
Another way of publicizing this is to offer “hooks” as options. As of jQuery 1.7.1 and up, we can use .on(eventName, function(){}) after our plugin call to separate the behaviors into their own functions. For instance, with the plugin above, we could change the code to look like this:
$.fn.getFlickr = function(opts) {
return this.each(function(i,el){
var $this = el;
var defaults = { // setting your default options
flickrUrl : " [someurl.com"] // some default value for an API call
}
var options = $.extend(defaults, opts || {});
// call the async function and then call the callback
// passing in the api object that was returned
$.ajax(flickrUrl, function(dataReturned){
// do some stuff
$this.trigger("callback", dataReturned);
}).error(function(){
$this.trigger("error", dataReturned);
});
});
}
This allows us to call the getFlickr plugin and chain other behavior handlers.
$(selector).getFlickr(opts).on("callback", function(data){ // do stuff }).on("error", function(){ // handle an error });
You can see that offering this kind of flexibility is absolutely important; the more complex actions your plugins have, the more complex the control that should be available.
4 – You’re Requiring Too Much ConfigurationOk, so tip number three suggested that the more complex actions your plugins have, the more complex control that should be available. A big mistake, however, is making too many options required for plugin functionality. For instance, it is ideal for UI based plugins to have a no-arguments default behavior.
$(selector).myPlugin();
Certainly, sometimes this isn’t realistic (as users may be fetching a specific feed, for instance). In this case, you should do some of the heavy lifting for them. Have multiple ways of passing options to the plugin. For instance, let’s say we have a simple Tweet fetcher plugin. There should be a default behavior of that Tweet fetcher with a single required option (the username you want to fetch from).
$(selector).fetchTweets("jcutrell");
The default may, for instance, grab a single tweet, wrap it in a paragraph tag, and fill the selector element with that html. This is the kind of behavior that most developers expect and appreciate. The granular options should be just that: options.
5 – You’re Mixing External CSS Rules and Inline CSS RulesIt’s inevitable, depending upon the type of plugin, of course, that you will have to include a CSS file if it is highly based on UI manipulations. This is an acceptable solution to the problem, generally speaking; most plugins come bundled with images and CSS. But don’t forget tip number two – documentation should also include how to use/reference the stylesheet(s) and images. Developers won’t want to waste time looking through your source code to figure these things out.
Things should just…work.
With that said, it is definitely a best practice to use either injected styles (that are highly accessible via plugin options) or class/ID based styling. These IDs and classes should also be accessible, via options as previously mentioned. Inline styles override external CSS rules, however; the mixing of the two is discouraged, as it may take a developer a long time to figure out why their CSS rules aren’t being respected by elements created by your plugin. Use your best judgment in these cases.
6 – You Don’t Offer ExamplesAs a rule of thumb, inline CSS is bad – unless it’s so minimal to the point that it doesn’t warrant its own external stylesheet.
The proof is in the pudding: if you can’t provide a practical example of what your plugin does with accompanying code, people will quickly be turned off to using your plugin. Simple as that. Don’t be lazy.
A good template for examples:
jQuery, like any good code library, grows with every release. Most methods are kept even after support is deprecated. However, new methods are added on; a perfect example of this is the .on() method, which is jQuery’s new all-in-one solution for event delegation. If you write a plugin that uses .on(), people using jQuery 1.6 or earlier will be out of luck. Now I’m not suggesting that you code for the lowest common denominator, but, in your documentation, be sure to explain which version of jQuery your plugin supports. If you introduce a plugin with support for jQuery 1.7, you should strongly consider maintaining support for 1.7 even once 1.8 comes out. You should also consider taking advantage of new/better/faster features in jQuery as they come out.
8 - Where’s the Changelog?Encourage developers to upgrade, but don’t break your plugin too often! One option is to offer a “legacy” deprecated, non-supported versions of your plugin.
It’s time to bite the bullet if you haven’t learned how to use version control yet.
Along with keeping your jQuery version support/compatibility a part of your documentation, you should also be working in version control. Version control (specifically, via GitHub) is largely the home of social coding. If you are developing a plugin for jQuery that you want to eventually publish in the official repository, it must be stored in a GitHub repository anyway; it’s time to bite the bullet if you haven’t learned how to use version control. There are countless benefits to version control, all of which are beyond the scope of this article. But one of the core benefits is that it allows people to view the changes, improvements, and compatibility fixes you make, and when you make them. This also opens the floor for contribution and customization/extension of the plugins you write.
Additional ResourcesThe world doesn’t need another slider plugin.
Ok, we’ve ignored it long enough here: some “plugins” are useless or too shallow to warrant being called a plugin. The world doesn’t need another slider plugin! It should be noted, however, that internal teams may develop their own plugins for their own uses, which is perfectly fine. However, if you’re hoping to push your plugin into the social coding sphere, find a reason to write more code. As the saying goes, there’s no reason to reinvent the wheel. Instead, take someone else’s wheel, and build a racecar. Of course, sometimes there are new and better ways of doing the same things that have already been done. For instance, you very well might write a new slider plugin if you are using faster or new technology.
10 – You Aren’t Providing a Minified VersionThis one is fairly simple: offer a minified version of your code. This makes it smaller and faster. It also ensures that your Javascript is error free when compiled. When you minify your code, don’t forget to offer the uncompressed version as well, so that your peers can review the underlying code. Free and cheap tools exist for front-end developers of all levels of experience.
11 – Your Code is Too CleverRefer to tip number thirteen for an automated solution.
When you write a plugin, it is meant to be used by others, right? For this reason, the most effective source code is highly readable. If you’re writing countless clever one-liner lambda style functions, or your variable names aren’t semantic, it will be difficult to debug errors when they inevitably occur. Instead of writing short variable names to save space, follow the advice in tip number nine (minify!). This is another part of good documentation; decent developers should be able to review your code and understand what it does without having to expend too much energy.
If you find yourself calling variables “
a” or “x“, you’re doing it wrong.
Additionally, if you find yourself consulting documentation to remember what your own strange looking code is doing, you also likely need to be less concise and more explanatory. Restrict the number of lines in each function to as few as possible; if they stretch for thirty or more lines, there might be a code smell.
11.You Don’t Need jQueryAs much as we all love using jQuery, it is important to understand that it is a library, and that comes with a small cost. In general, you don’t need to worry too much about things like jQuery selector performance. Don’t be obnoxious, and you’ll be just fine. jQuery is highly optimized. That said, if the sole reason why you need jQuery (or a plugin) is to perform a few queries on the DOM, you might consider removing the abstraction entirely, and, instead, sticking with vanilla JavaScript, or Zepto.
13 – You’re Not Automating the ProcessNote: if you decide to stick with vanilla JavaScript, ensure that you’re using methods that are cross-browser. You might potentially need a small polyfill for the newer APIs.
Use Grunt. Period.
Grunt is a “task-based command line build tool for JavaScript projects”, which was covered in detail recently here on Nettuts+. It allows you to do things like this:
grunt init:jquery
This line (executed in the command line) will prompt you with a set of questions, such as the title, description, version, git repository, licenses, etcetera. These pieces of information help to automate the process of setting up your documentation, licensing, etc.
Grunt does far more than just make some customized boilerplate code for you; it also offers built in tools, like the code linter JSHint, and it can automate QUnit tests for you as long as you have PhantomJS installed (which Grunt takes care of). This way, you can streamline your workflow, as tests run instantly in the terminal on save.
14 – You’re Not TestingOh, by the way – you do test your code, right? If not, how can you ensure/declare that your code works as expected? Manual testing has its place, but, if you find yourself refreshing the browser countless times every hour, you’re doing it wrong. Consider using tools, such as QUnit, Jasmine, or even Mocha.
Testing is particularly useful when merging in pull requests on GitHub. You can require that all requests provide tests to ensure that the new/modified code does not break your existing plugin.
Some Helpful ResourcesIf the concept of testing jQuery plugins is brand new to you, consider watching our Premium-exclusive screencast, Techniques For Test-Driving jQuery Plugins. Additionally, we’re launching a new “JavaScript Testing With Jasmine” course later this week on the site!
We wouldn’t be doing you any favors by just telling you what you’re doing wrong. Here are some links that will help get you back on the right path!
If you are writing a jQuery plugin, it is vital that you stray away from the pitfalls listed above. Did I miss any key signs of a poorly executed plugin?
Each month, we bring together a selection of the best tutorials and articles from across the whole Tuts+ network. Whether you’d like to read the top posts from your favourite site, or would like to start learning something completely new, this is the best place to start!
We’ve Been in Kuala Lumpur!This month we’ve been attending an Envato company meet-up in Malaysia. We’ve had a fun time working together as a team, made lots of exciting plans for the future of Tuts+, and also had the chance to meet up with lots of our readers! Thanks to everyone who took the time to attend our community meet-up and, if you’re interested, you can find out a bit more about our trip here (and see a few photos!)
Psdtuts+ — Photoshop Tutorials
Use Photoshop CS6 to Create a Micro Machines Inspired Scene
Photoshop CS6 is packed with new features and effects that you can use in your work. In this tutorial we will utilize Photoshop’s new 3D capabilities as well as its new content aware features to create a Micro Machines inspired composition. Let’s get started!
Create a Snowy Landscape From Desert Photography in Photoshop – Tuts+ Premium Tutorial
Photoshop is a great tool because it allows us to be creative and produce imagery that would be impossible to create otherwise. In this Tuts+ Premium tutorial, author Tony Aubé will create a snowy landscape from desert photography and photos of sand. This tutorial is available exclusively to Tuts+ Premium Members. If you are looking to take your photo manipulation skills to the next level then Log in or Join Now to get started!
Create a Light Bulb Inspired Text Effect in Photoshop
Layer styles are a powerful and time saving feature that can help you apply amazing effects to your designs. In this tutorial we will use layer styles to create a light bulb inspired text effect in Photoshop. Let’s get started!
Meet Grunt: The Build Tool for JavaScript
If you’re working on a large project, you’ll no doubt have a build script or a bunch of task scripts to help with some of the repetitive parts of the process. You might use Ant or Rake, depending on the language the project is written in.
CSS Refreshers: Borders
Sure, we’re all familiar with borders. Is there anything new that could possibly be introduced? Well, I bet there’s quite a few things in this article that you never knew about!
Lightning Fast Folder and File Creation in Sublime Text 2
I’m frequently asked about how I’m able to create new directory structures and files so quickly in Sublime Text 2. Well the answer is that this functionality is not offered natively; instead, I use a helpful plugin. I’ll demonstrate it in this video.
Quick Tip: The Line of Action, Make Your Character Poses More Dynamic!
The line of action is a key ingredient to making your character’s poses look more dynamic. In this guide, we will explore what the line of action is and how it can be used to make your character poses come alive.
Free Vector Grunge Graphics for Designers and Illustrators
If you’re looking for free vector grunge graphics, such as distressed backgrounds, worn textures, dirty paint splatter, and more, then you’ve found a compilation worth downloading. We’ve collected an assortment of vector grunge illustrations, free vector grunge textures, and wickedly worn graphics available for free download. Jump in and grab these free grunge vectors now and start making grunge vector art for your next project.
Vintage Vector Design Workflow: Creating a Retro Flyer Design
This tutorial will cover the process of creating a vintage inspired retro flyer design. There are four main areas of concentration to achieve this look and feel: color, type, character and texture. We’ll review a complete vintage vector design workflow to create this retro flyer design. Let’s get started.
Quick Tip: Speed Up Your Workflow With Photoshop Actions
Avoiding repetitive tasks is always going to speed up your workflow. In today’s Quick Tip we’ll do just that, by utilizing Photoshop’s actions panel and combining it with hotkeys. Watch this quick screencast and I guarantee you’ll save tons of time next time you’re designing!
Building a Responsive Layout With Skeleton: Finishing Off
During previous screencasts in this series we’ve covered a lot of ground, building our responsive (or adaptive) layout with the Skeleton boilerplate. It’s now time to finish all the final details; arguably the most time-consuming part of any website build!
Adobe Photoshop CS6: Improvements for Web and UI Designers
Photoshop CS6 has been hailed as a huge improvement for web and UI designers. Im going to share with you some of the features that Photoshop CS6 Beta has to offer and demonstrate how they can help you in your web or UI design workflow.
A How-To Guide to Getting Started in Real Estate Photography
Real estate is one of the world’s most competitive industries. Dominated by ambitious agents looking for the next big sale, selling real estate is all about setting yourself apart from the competition. What better way to catch a buyer’s eye than the perfect photo of the perfect home? In today’s article, we’re taking a look at the exciting world of real estate photography.
A Simple Solution to White Balance and Exposure: The 18% Gray Card
An 18% gray card is a handy accessory that every serious photographer should keep in their bag. It doesn’t cost much and it barely takes up any space. If you encounter a situation where you have mixed lights, this unassuming piece of plastic helps you determine the white balance. It can also be used to determine the correct exposure.
An Expert Guide to Matting and Framing a Photo
The final printed image is the culmination of my journey in creating a piece of artwork that represents my view of the world around me. As photographers in the digital age we spend far too much time staring at our photographs on our computer screens and very little time holding them in our hands. I still take great pride in every print I produce. There are a myriad of options for printing your work today, from canvas wraps to Metal prints, however for me there is something timeless and classic about a finely Matted and Framed print.
In this tutorial you’ll learn how to create a complete character rig for a voodoo doll character in Maya using the Setup Machine and Face Machine plugins from Anzovin studios. You’ll learn how these plugins can save you valuable time during rigging by allowing you to utilize pre-built body and face rigs which can then be customized to fit you and your character’s specific needs.
In this tutorial we’re going to create a smooth, stylish countdown animation. You can use words, letters, logos or whatever you want to make this type of animation. As you can see it’s easy to set up and looks very stylish and attractive.
This week, Cgtuts+ has teamed up with our sister site Psdtuts+ to bring you this amazing two part, in-depth tutorial from Nacho Riesco. In this tutorial we are going to sculpt a Micro Bionic Insect with chemical war purposes using simple hard-surface modelling techniques with the Clipping Brush, Masking and much more. Head over to Psdtuts+ for the conclusion of this project where we’ll composite our render passes from Zbrush, and create the final image in Photoshop!
In today’s tutorial we’re going to take you step by step through everything you need to know to build your own rugged light dimmers. We use these exact dimmers on all our studio and on location shoots. Besides being extremely durable, these little devices provide a wider range of lighting options and are surprisingly valuable when you have to light a scene in a tight location.
Is Working On Stills Easier in After Effects or Photoshop?
We always tend to go to Photoshop for working with still images, but today I’d like to bring up a few thoughts about why working in After Effects might be a better solution for your next project.
Show A Motion Path With The StroMotion Effect
In this tutorial we will track freeze frames into a hand-held scene utilizing The Foundry’s CameraTracker to achieve an effect that is often referred to as “StroMotion”. We’ll be talking about different methods of how to remove the subject from the background and how to line everything up. Enjoy!
Sites That Serve Up Great Loops and Samples
Loops can form the foundation of a track, and are useful for quickly putting some ideas together when sketching out an arrangement. Samples provide us with sounds and colors to create our music with. But where can you download great loops and samples? Here are 30+ great places to start.
Every music producer worth his salt is in the process of building up a useful collection of useable sounds.
Morphing in Pro Tools
We’ve all seen how you can morph one face into another in the graphical world. In this screencast Rishabh Rajan shows us how to achieve the same thing with audio using Pro Tools.
D Mixing Part 7: Mastering, The Final Chapter (Part 1)
Although this is a series on mixing, it feels incomplete not to get into at least a brief discussion on master bus options and to discuss what exactly goes on when you print all your hard work to a single and final stereo file. Due to the depth of this topic, I am splitting it into two parts.
What Is Dart, and Why Should You Care?
In this tutorial, I’ll introduce you to Google’s new web programming language, Dart, and explain why you should like it and what you need to know about it. Learn about this new language and form some opinions about it – will it really replace JavaScript?
Accessing the Same Saved Data With Separate Flash and JavaScript Apps
In this tutorial I will show you how to access the same saved data in separate Flash and JavaScript apps, by storing it in HTML5 LocalStorage and using ExternalInterface to reach it with AS3. We will create the same app in both JavaScript and Flash to demonstrate that it is platform agnostic.
An ImpactJS Overview: Introduction
Impact is an incredibly powerful HTML5 game framework which takes advantage of modern browser’s canvas element and can also run on mobile or be compile into a native iOS app. In this video I will go over the framework, how to set up a project, some background into how to create classes in it and finally go over the core classes that make up the framework. This is a high level overview which will give you a general sense for how things work.
Mini Guide to Contact Form 7
Usually a website needs a contact form to communicate with the site owner. One of our favorites is Contact Form 7. Let’s see what it can do!
For a lot of WordPress projects these days we use custom post types. The WordPress development team created some handy methods to integrate them into your projects. But when you use custom post types, taxonomies and meta boxes frequently, it’s quite probable that you’re going to repeat yourself. That’s why we are going to use the power of these WordPress functions to build a more powerful class, which we can use to quickly register post types, taxonomies and meta boxes.
Using WordPress as an Intranet
When we talk about WordPress we usually associate it with either being a blogging platform or just another content management system, but what about as an Intranet? This tutorial will show you how you can turn your basic installation of WordPress into a robust Intranet for your business.
Create an Awesome Carousel, Version 2.0
Engage your users with stunning carousels! We’ll look at how easy and clean it can be to implement scrollable, interactive carousels in your iOS applications. With high configurability, you can have 3D, flat, rotating, and endless scrolling arrays for data, images, and buttons.
Corona SDK: Create an Alphabet Soup Game
In this tutorial series, you will learn how to create a minimalistic Alphabet Soup game. The goal of this game is to allow the player to pick words out from a jumbled set of letters. Read on!
iOS Quick Tip: Interacting with Web Services
At some point in your iOS development career, you will have the need to interact with a web service from within your app. You may need to access remote data, parse a social network feed, or even download some assets into your application. This quick tip will teach you to do so without using third party libraries!
Over the course of these last two weeks, I had the pleasure of attending an Envato meetup/conference in Malaysia. As you might expect, Envato is composed of ridiculously smart and talented folks…folks so smart that it quickly becomes intimidating!
But I’m not here to talk about the conference specifically; instead, I’d prefer to ramble a bit on one of my largest takeaways from the event.
The Old-Fashioned “Path”
One day during the conference, I had a chat with Envato’s current (temporary) lead development manager, Pete, about the traditional concept of advancement in a company. In many ways, we’re designed/brainwashed from an early age to follow a very specific path:
That’s a good thing, right? Well, in our industry, specifically, maybe not.
Management can be a bit of a scary word. It indicates Excel, not code. It indicates… management, not development.
Our life-long training tells us that this is what’s supposed to happen.
But, nonetheless, it’s still a higher level (hopefully higher paying) job. Our life-long training tells us that this is what’s supposed to happen, if we desire to be successful. You’ve worked hard; now you get to manage others (and maybe drink scotch). This is the path.
For creatives, though, does this sort of role make us happy?
Sure, we may have a bit more input into the direction of the business, but does it make us happier? For the last year or so, I’ve struggled with this very thing. I adore my current job: I’m able to help shape the future of education in the creative fields (more on that later this year), and spend all of my spare/free time learning how to be a more efficient developer. What could be better than that?
Still, though, that lingering feeling always rested in the back of my mind: I’m only “advancing” in the world if my job title/rank increases sporadically. I had (and have) no desire to change my current involvement/role in the company (Envato), but, nevertheless, felt that I should reach for these more traditional managerial roles.
The LessonThis old-fashioned notion of advancement is a silly metric for success.
Pete taught me that this old-fashioned notion of advancement is a silly metric for success. Instead, we have to reshape our perception of what both success and fulfillment are. Remember when I noted that Pete was the development manager at Envato? Well, technically, at his own choosing, he’s the temporary manager, while we search for a new development manager. Despite the fact that he’s certainly qualified for the job, he doesn’t want it – which I find incredibly admirable. Instead, his skills/desires rest firmly in things like software architecture. In his own words, that is where he is able to contribute most effectively to the company. So, a bit oddly, perhaps, he is currently in the position of finding and hiring his future manager.
Closing ThoughtsIt’s interesting how the older I get, the more and more I come back to this one word: “contentment.” It’s not about job titles, or vanity, or even money (to some extent); it’s simply about contentment. Do what you love, and forget those old-fashioned job titles and notions of success. Or…
Figure out where you’re most effective in your company, and do…that. Period.
Ready for a revelation? A concept that you’ve never considered before? Well here it is: never ever, ever pour a glass of wine into your Macbook keyboard. Or, more practically put, be extremely careful when there’s a drink of any kind near your laptop. If a spill occurs, and you don’t act quickly enough, you’ll find yourself staring at a massive bill for a new logic board, hard-drive, battery, and anything in between.
Accidents do happen — so plan on it. What’s more important is that you learn exactly what to do when these spills inevitably occur.
July, 2010 Step 1. Don’t Freeze. Unplug!
Ahhhh! The wife (meaning, you) accidentally spilled wine all over your keyboard. From personal experience, I can assure you that, for some odd reason, your first instinct with a massive computer spill is to freeze for five seconds or so, in shock. Don’t do this! Luckily Apple laptops are pretty helpful about automatically shutting down to prevent as many issues as possible before they happen. The more recent laptops even have liquid detection…though I’m certain that Apple is more interested in voiding your warranty than protecting you. For those uninformed, most laptop warranties do not cover spills.
Nonetheless, don’t waste a single second. Quickly unplug the computer, and shut it down.
Step 2. Flip that SuckaThe walls and carpet may have liquid on them as well, but ignore that. The computer is far more important right now.
The next step, which should occur within seconds of beginning Step 1, is to flip the laptop upside down, into an L-shape. Gravity will then force as much liquid to drain out of the keyboard as possible. Make sure that you lay it on a towel so that it can soak up the liquid.
Step 3. Open the BackLet gravity do its job. Immediately flip the laptop into an L-shape.
Using a tiny screw driver, unscrew the back-side of your laptop. This will, of course, vary, depending upon which model you’re using. I’m sure you can figure it out.
Step 4. Remove the Battery and Hard DriveParticularly on Macs, not all of the screws are the same size. Make a note of which screw goes where.
Before progressing, ensure your livelihood and touch some metal objects nearby to remove any potential static electricity from your body.
Needless to say, batteries and liquids — especially sugary, acidic liquids — don’t play nice together. Wherever yours is located on your laptop, remove it as quickly as possible. Next, get the hard drive out as well. We don’t want to risk any liquid seeping in, and corrupting your file system!
Step 5. Dry the InsidesAnd now that you know how easy it is to remove a hard drive, don’t ever again pay a person to upgrade your hard drive. It only takes a moment to do yourself. ;)
With a paper towel of some sort, begin cleaning the insides of your computer. Depending upon how much liquid was spilled, this may either be a quick or lengthy process. For yours truly, it took around ten minutes to clean everything.
Step 6. Rubbing Alcohol and CottonSome people prefer to use a hair dryer to clean the insides. This one is up to you; however, I’d encourage you to not do so. Play it safe and use a towel. We don’t want to risk frying the insides.
Next, we need to continue removing as much sugar and acid as possible from the logic board. Otherwise, over time, it can begin to corrode the wiring. Yeah, this isn’t good!
Using rubbing alcohol and a cotton swab, begin dabbing any stained areas — but be gentle. If you have access to the backside of your keyboard, clean that area as well. Unfortunately, on the newer Macbook Pro models, it’s extremely difficult to access this section. With past models, it was quite easy to remove the keyboard entirely, for cleaning purposes. Unfortunately, that’s no longer the case.
Step 7. Leave it AloneRubbing alcohol will help dissolve any remaining acid or sugar on the logic board.
Anxiety is a dangerous thing. Resist the urge to determine whether or not you’ve destroyed your laptop, and keep it off for a minimum of 72-120 hours (3-5 days). This will allow any remaining liquid to dry/evaporate first. Make sure that, while its drying, you keep the battery outside of the computer. This is mostly a precaution.
Step 8. Cross Your FingersKeep the computer off for 3-5 days — no questions asked. Do not turn it on during this window.
After 72-120 hours, reconnect the battery, screw everything back in, cross your fingers, say a prayer, and turn on the computer. Particularly if you’re using a newer Macbook Pro (2010+ models), you’ve done everything you can do. With hope, and more often than not, it’ll chime, and start-up like a charm. However, if the battery is dead, or the hard drive is corrupted, you’re next best option is to take it in for official repair. Of course, this will somewhat depend upon how skilled you are, when working under the hood.
About the AuthorJeffrey Way — me…yes, I’m speaking in third person — once committed a Cardinal Sin, and caused a massive wine spill into his Macbook Pro. Had he not followed these exact steps, he’d be forking over another life savings for a new Mac. Luckily, that was not the case.
Thanks for reading. Should the same ever happen to you, I hope this guide will help a bit!
Web development is an industry that’s in a state of constant flux with technologies and jargon changing and mutating in an endless cycle. Not to mention the sheer deluge of information one has to process everyday.
In this series, published monthly, we’ll seek to rectify this by bringing you all the important news, announcements, releases and interesting discussions within the web development industry in a concise package. Join me after the jump!
News and ReleasesAll of the important news in a single place: releases, announcements, companies bickering, security issues and all related hoopla.
The Infamous Semi-Colon Debate
Ahh, JavaScript. I think it has caused more conflict than thatHelen. This time around there’s been a flurry of activity around a single, missing comma. I don’t want to take you away from the drama so check out the Github discussion below, after grabbing some popcorn.
Personally, if you’re not CoffeeScript-ing, just add that darn semi-colon and call it a day.
Django Moves to Github, Finally
Most Pythonistas know Django. For those of you who don’t, Django is a high level Python framework that helps you out with web development. While Django has had a repo on Github for a long time now, it was in a state of desolation. You’d often see that the repo only held versions that were a lot behind the curve.
Well, no more of that. Django has finally moved to Github and the repo seems to be active. If you’re interested, Subversion was what was used to manage Django earlier, since 2005.
Meteor, a New Way to Build Apps
Tired of the current frameworks and technologies, a group of devs has created a full stack, JavaScript only framework.
Meteor is a set of new technologies for building top-quality web apps in a fraction of the time, whether you’re an expert developer or just getting started. I’m sure people are already tired of reading about JavaScript and the sheer number of frameworks sprouting out each day but this is really worth a look.
SPDY Gains More Traction With Firefox Beta
SPDY, developed by Google, is a networking protocol aimed at improving web page load times as well as web security.
As with anything web related, it takes eons to get anything standardized and browsers have already started implementing experimental versions of the technology. The latest Firefox beta adds support for the protocol, switching it on by default. It’s not the only browser to do — Chrome already includes support for SPDY while Opera does the same with its preview of version 12.
Microsoft Announces the Metro jQuery Mobile Theme
Microsoft, in order to increase awareness of its spectacularly clean Metro interface, has open sourced a Metro themed design for jQuery mobile. The theme includes a large number of Metro themed widgets and interactions and seems to work really well.
Firefox is Now At Version 12
Another month, another Firefox update. To be fair, the updates are now rather substantial this time around though.
This version introduces a ton of developer tools and improvements along with a far more streamlined update process. The list of developer related updates, the ones we really care about, makes for a rather interesting read so make sure to hit the link below.
CoffeeScript, Now With JavaScript’s Strict Mode, Updates to Version 1.3
Developers either love or hate CoffeeScript. I personally love it which is why you’re seeing this here. The latest version enforces the strict more at compile time helping you weed out those annoying, niggling issues.
Version 1.3 also brings you improvements to the REPL as well as a bunch of tweaks and improvements.
Rails Updates in Light of Recent Security Issues
With the havoc caused by the mass assignment issue last month, the Rails team has reconsidered their stance on this issue and has pushed out version 3.2.3 which changes the value of config.active_record.whitelist_attributes . There are also various, assorted fixes and improvements bundled into this version.
As web developers, the sheer amount of resources we can tap into increases exponentially with time. Here is just a quick look at some recently created resources that deserve your attention — everything from new books to scripts and frameworks.
Smooth.jsScroll PathSmooth.js takes an array of numbers or vectors and returns a parametric function that continuously interpolates that array. Smooth.js supports several interpolation methods, and flexible options for boundary behavior.
keymaster.jsjQuery Scroll Path is a plugin that lets you define your own custom scroll path. What this means exactly is best understood by checking out the demo. The plugin uses canvas flavored syntax for drawing paths, using the methods moveTo, lineTo and arc. To help with getting the path right, a canvas overlay with the path can be enabled when initializing the plugin.
HamKeymaster is a simple (100 LoC or so) micro-library for defining and dispatching keyboard shortcuts. It has no dependencies.
mailcheck.jsPHP Microframework for use with whatever you like. Basically just a fast router with nice syntax, and a cache singleton. Will add more things as I go, like perhaps an extension system, autoloader and some other stuff to make developing in PHP less irritating than it currently is.
Hammer.jsThe jQuery plugin that suggests a right domain when your users misspell it in an email address.
Monorail.jsHammer.js is a javascript library that can be used to control gestures on touch devices.
RainbowMonorail.js will never force you, and uses only what you need. Monorail.js will never force you to install anything not needed for your project. The goal is to use what you need. Anything other than creating a project will always be optional.
TimelineRainbow is a code syntax highlighting library written in Javascript. It was designed to be lightweight (1.4kb), easy to use, and extendable.
FakeS3There are lots of timeline tools on the web but they are almost all either hard on the eyes or hard to use. Create timelines that are at the same time beautiful and intuitive for users. Timeline is great for pulling in media from different sources. Just throw in a link from Twitter, YouTube, Flickr, Vimeo, Google Maps or SoundCloud and Timeline will format it to fit perfectly. More media types will be supported in the future.
retina.jsFakeS3 is a lightweight server that responds to the same calls Amazon S3 responds to.
It is extremely useful for testing of S3 in a sandbox environment without actually making calls to Amazon, which not only require network, but also cost you precious dollars.
Rucksackretina.js makes it easy to serve high-resolution images to devices with retina displays. When your users load a page, retina.js checks each image on the page to see if there is a high-resolution version of that image on your server. If a high-resolution variant exists, the script will swap in that image in-place.
jQuery PopBoxRucksack is a jquery plugin to arrange elements that can fit in the given width. It relies on the knapsack algorithm.
html2canvasjQuery PopBox is a simple balloon UI element inspired by 37Signals Highrise CRM.
Best of the InternetThis script allows you to take “screenshots” of webpages or parts of it, directly on the users browser. The screenshot is based on the DOM and as such may not be 100% accurate to the real representation as it does not make an actual screenshot, but builds the screenshot based on the information available on the page.
Often, you’re not really looking for a tutorial as much as you’re looking for a rant, an opinion or the musings of a tired developer or just something cool with absolutely zero real world use. This sections contains links to precisely those — interesting and cool stuff from the developer community.
PHP: a fractal of bad design
A detailed look at why most people, when given a gun with two bullets and asked to shoot Hitler or PHP, want to shoot PHP twice. As much of a PHP lover I am, I can’t help but agree with a lot of this article.
PHP Sucks! But I Like It!
A really well written counter point to the earlier article, backed by a lot of reasoned thinking.
The infernal semicolon
Brendan Eich chimes in with his stance on the entire JavaScript semi-colon issue.
How to be a successful programmer without a CS degree
A wonderfully detailed answer on StackOverflow that outlines how to be a kickass programmer without an expensive CS degree.
Zero downtime deploys for Rails apps
A very informative presentation that outlines how you should go on about running and upgrading your Rails apps.
On being “Senior”
A nice little read about the software industry and the rat race around titles.
Wrapping UpWell, that’s about all the major changes that happened in our industry lately.
Do you want us to cover more standard news? A focus on upcoming scripts maybe? Or just more interesting posts and discussions from the community? Let us know in the comments and thank you so much for reading!
Modern JavaScript libraries are quite the behemoths — just take a look at jQuery. When you’re creating a mobile app, or even just targeting modern browsers, a library that’s much more svelte and nimble becomes a tastier proposition.
Today, we’re going to look at one such library, named Zepto.
The Issue With Mixing Desktop Libraries and Mobile DevicesOne thing that has snuck past most radars has been the rise of mobile devices.
See, the internet, and the technology that powers it, has grown in leaps and bounds over the past years. We moved from static sites to web applications to dynamic web applications and then to real time, hyper responsive, thingamabob apps of today. One thing that has snuck past most radars has been the rise of mobile devices.
Think about it: a lot of us use smart phones and use it for browsing on a constant basis. Even at home, a non-trivial portion of my circle has adopted a tablet device for casual surfing and email. While this influx of devices is good from an exposure point of view, it isn’t without its caveats.
Instead of thinking of these devices as display constrained like consumers do, we, as developers, need to think of them in terms of resources and bandwidth. Not all them sport a hyper quad gajillion Ghz CPU or come with oodles of memory. And let’s not even get started with the bandwidth. A big portion of the browsing population is still stuck on these infernal excuses for a mobile internet connection.
I think you see where I’m going with this. Big, monolithic libraries like jQuery or Prototype definitely have their place but for this mobile era, I think there’s a place for something that’s a lot more nimble. And a lot of developers seem to agree with me.
All the Code to Make a Library Work Across Browsers Adds UpAnother big issue that I failed to mention is that contemporary libraries do a lot of cross browser stuff. In fact, a big draw of jQuery, initially, was how it abstracted away a lot of the cross browser quirkiness that front end developers had to work around. Even now, jQuery does a lot of heavy lifting under the hood to make sure nothing breaks in different browsers.
But if you’re a developer looking to cater to only contemporary devices, do you really need all this, dare I say, cruft? The short answer is no. By cutting out unnecessary code, you both:
Think that this issue is overblown? Here’s a random blob of code from jQuery’s source:
isPlainObject: function( obj ) {
// Must be an Object.
// Because of IE, we also have to check the presence of the constructor property.
// Make sure that DOM nodes and window objects don't pass through, as well
if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
return false;
}
....
Or something a little more esoteric:
// Perform a simple check to determine if the browser is capable of
// converting a NodeList to an array using builtin methods.
// Also verifies that the returned array holds DOM nodes
// (which is not the case in the Blackberry browser)
try {
Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
// Provide a fallback method if it does not work
} catch( e ) {
// The intended fallback
}
....
This may look fairly trivial but keep in mind this tends to adds up. If you’re only going to target modern browsers, either on desktops or mobiles, then there is no real need for all these additional checks and hacks. By cutting down your intended browsers, you win on both bandwidth and performance!
So What’s Zepto’s Deal?I hear you guys saying “Enough buildup! Tell us about the darn library already!”. So let’s get to it.
Zepto, as the title spoiled it for you, is a mobile JavaScript framework that rectifies both of the issues mentioned above. It has a very small code base and is feather weight at around 8kb.
It manages to be so svelte by mostly cutting out the cross browser stuff. When it was created, the main focus was to support Webkit only. The mobile version of Webkit to be exact. Now, it’s been expanded to work with desktop browsers too — but only modern ones. No more klutzing around to make things work this IE6!
Zepto’s API is jQuery compatible. If you use jQuery, you already know how to use Zepto.
Another area where Zepto manages to be small is how it manages to avoid feature bloat. The core library doesn’t seem to include any extraneous functionality. Even the AJAX and animation functionality are available as separate modules, should the need arise. For users that primarily use libraries for DOM traversal and manipulation, this is an utter god send.
And, oh, did I mention Zepto’s main party piece? Zepto’s API is jQuery compatible. If you use jQuery, you already know how to use Zepto.
Are Zepto and jQuery Interchangeable?Yes and no. Depends is a more apt answer.
Yes because Zepto’s core API mimics jQuery to a large extent. In order to make it easy to use and dramatically reduce the learning curve, Zepto emulates jQuery’s API. Most of the oft used methods, like DOM manipulation, are named pretty much the same and have the same parameters in the same order. The method signatures are the same, for the engineers out there.
Let’s look at a small example:
$('#element').html("Hey! Are you on the GW2 beta?");
Looks familiar? It should. This is the exact same code you’d use with jQuery to change the HTML of an element. As I mentioned, this isn’t limited to just this method. Most DOM operations are built the same way along with your utilities, like AJAX.
On the flip side, the API isn’t a 100% match. Zepto forgoes some methods present in jQuery that’s liable to break your code. And just as importantly, since Zepto is a subset of jQuery, you’ll probably miss out on specific functionalities that are built in — Deferred is a fine example. You simply can’t swap out jQuery with Zepto and expect everything to work.
And for me, the biggest hurdle are the methods that have been copied from jQuery but have a different signature and feature set. It becomes a little frustrating when you think you’re using a method right but you aren’t. The clone method’s ability to copy event handlers is a good example. Without looking at the source, I really wouldn’t have found this out.
Exploring the Core APIIf you’ve worked with jQuery before, everything below should be a snooze fest.
Enough chit chat, let’s dive into some code now. As with a lot of modern libraries, DOM traversal and manipulation is a core feature that everyone wants to perfect. Since the API and overall functionality is very similar to jQuery, I think you can safely assume everything is top notch.
Let’s take a look at some common DOM related functionality.
Modifying the HTML contents of a containerThis is the bread and butter of DOM operations: reading or changing the HTML contents of an element. With Zepto, it’s as simple as calling the html method on the container, and passing in the new HTML, if needed.
For example, this obtains the HTML of an element and stores it in a variable.
var containerText = $('#element').html();
Or if you want to change it to something else:
$('#element').html("Hola there!");
Pretty simple, right?
Prepend/Append an Element to a ContainerAs with jQuery, Zepto makes use of the append and prepend methods. And the invokation remains the same as well.
$('#element').append("<p>This is the appended element.</p>");
// or
$('#element').prepend("<p>This is the appended element.</p>");
Events
Events are the backbone of any modern application and Zepto provides you with a bunch of easy to use methods to get your job done. The bulk of the work is done through the on method.
$('#element').on('click', function(e){
// Your code here
});
Easy to read and easy to parse. If you’re feeling old school and feel like using bind, delegate or live methods, don’t. Just like with jQuery, they’re deprecated here.
Any modern low level library needs to provide an easy to use wrapper around AJAX and Zepto doesn’t let you down here. Here is an example of a super simple AJAX request.
$.ajax({
type: 'POST',
url: '/project',
data: { name: 'Super Volcano Lair' },
dataType: 'json',
success: function(data){
// Do some nice stuff here
},
error: function(xhr, type){
alert('Y U NO WORK?')
}
});
Things might look a little complicated but what we’re doing can be boiled down to:
As with jQuery, there are separate method for a GET or POST request or ones to merely load up some web content.
AnimationsWhat will the world come to without a few animations? Zepto exposes the almighty animate method that should handle most of your animating needs.
$('#element').animate({
opacity: 0.50, top: '30px', color: '#656565'
}, 0.5)
We’re basically selecting the element to be animated, invoking the animate method and specify the properties to be animated as well as the time it should take to finish animating. Zepto does the rest.
Or if you just need to show and hide an element, the toggle should work just fine.
A Look at the Touch Events and Other NicetiesI think you get the point here — Zepto’s DOM, animation and events API emulates that of jQuery to a large extent. And as we all know, jQuery is darn good with those things. If you’ve worked with jQuery before, you shouldn’t face too much trouble here.
Zepto does provide you with a few touch specific events that you can leverage in your apps. These include:
Here’s a quick example, delightfully swiped from Zepto’s documentation.
<ul id=items>
<li>List item 1 <span class=delete>DELETE</span></li>
<li>List item 2 <span class=delete>DELETE</span></li>
</ul>
<script>
// show delete buttons on swipe
$('#items li').swipe(function(){
$('.delete').hide()
$('.delete', this).show()
})
// delete row on tapping delete button
$('.delete').tap(function(){
$(this).parent('li').remove()
})
</script>
When a list item is swept, every other list element’s delete button is hidden and only the current’s is displayed. Tapping a delete button removes that button’s parent li item to be removed from the DOM.
This should be fairly similar to how you generally handle events, except you’ve binding your handlers to different events, that’s all.
Wrapping UpConsidering what and whom I develop for, this is perfect for me; but as it always is, your mileage may vary.
Well, that’s about almost all there is to Zepto. At it’s core, it was meant to be a lean, cruft free version of jQuery that could be used on mobile devices. Over time, it has morphed into a lean library that does away with supporting archaic browsers.
Considering what and whom I develop for, this is perfect for me; but as it always is, your mileage may vary. You may be locked into using jQuery plugins that require non-trivial modifications to make it work under Zepto or just have more faith in jQuery.
Either way, you really need to give Zepto a try to see how it fits in with your workflow before writing it off. I did and I love it!
Well, that’s all from me today. Let me know what you think in the comments below and thank you so much for reading!
Closures are often viewed as an arcane art in the land of JavaScript. Once mastered, they allow you to write some truly amazing JavaScript. This article will get you up to speed on the magic of JavaScript closures.
What Is A Closure?One of the key truths of JavaScript is that everything is an object. This, of course, includes functions.
A closure is nothing more than a function object with a related scope in which the function’s variables are resolved.
Closures get their name because of the way they close over their contents. Consider the following bit of JavaScript:
topping = "anchovi";
function pizzaParty(numSlices) {
var topping = "pepperoni",
innerFunction = function() {
var topping = "ham";
console.log(" .....But put " + topping + " on " + numSlices + " slices");
};
console.log("This pizza is all about the " + topping);
innerFunction();
}
pizzaParty(3);
If you open up your favorite console and run that bad boy, you will be greeted with a delicious message to the effect of “This pizza is all about the pepperoni ….. But put ham on 3 slices.” This example illustrates some key concepts of JavaScript that are crucial to getting a hold on closures.
A Closure is a Function Object
How many function objects are in the above code? Well… we have our pizzaParty function, and nested in that function is innerFunction. Math hasn’t always been my strong suit, but 1 + 1 = 2 in my book. Each function object has its own set of variables, which are resolved in each function’s scope.
Closures can’t be fully understood without a firm grounding in scope. JavaScript’s scope mechanism is what allows each function to have its own topping variable, and without it we might have too much pepperoni, too little ham, or *gasp* … some anchovies at our pizza party. Let’s use a quick illustration to better illustrate this idea.
Variable Accessibility Works Outside-InFunctions are executed using the scope that was in effect when the function was defined. It has nothing to do with the scope in effect when the function is called.
The green arrows show that accessibility works from the outside in. Variables defined in the scope outside of a function are accessibile from within it.
If we were to omit the topping variable from inside the pizzaParty function, then we would get a message like “This pizza is all about the anchovi”, but since pizzaParty has a topping variable within its own scope; those salty suckers will never get near our pizza party.
Likewise, the numSlices parameter can be accessed from within innerFunction because it is defined in the scope above – in this case the scope of pizzaParty.
The red arrows show that variables in scope for a function are never accessible outside of that function. This is the case only when a variable meets one of the following conditions:
var keyword is being used.
Omitting the var keyword when setting a variable will cause JavaScript to set the closest named variable in outer functions all the way up to the global scope. So, using our example, the ham topping in innerFunction cannot be accessed from pizzaParty, and the pepperoni topping in pizzaParty cannot be accessed out in the global scope where the anchovi dwells.
Lexical scope means functions are executed using the variable scope in effect when the function was defined. It has nothing to do with the scope in effect when the function is called. This fact is crucial to unlocking the power of closures.
Now that we understand what a closure is, and what scope means for closures, let’s dive into some classic use cases.
Using Closures For PrivacyClosures are the way to conceal your code from the public eye. With closures, you can easily have private members that are shielded from the outside world:
(function(exports){
function myPrivateMultiplyFunction(num,num2) {
return num * num2;
}
//equivalent to window.multiply = function(num1,num2) { ...
exports.multiply = function(num1,num2) {
console.log(myPrivateMultiplyFunction(num1,num2));
}
})(window);
With closures, you can easily have private members that are shielded from the outside world.
Let’s break it down. Our top level function object is an anonymous function:
(function(exports){
})(window);
We invoke this anonymous function right away. We pass it the global context (window in this case) so we can “export” one public function, but hide everything else. Because the function myPrivateMultiplyFunction is a nested function, it exists only within the scope of our closure; so we can use it anywhere inside this scope, and only in this scope.
JavaScript will hold a reference to our private function for use inside the multiply function, but myPrivateMultiplyFunction cannot be accessed outside of the closure. Let’s try this out:
multiply(2,6) // => 12 myPrivateMultiplyFunction(2,6) // => ReferenceError: myPrivateMultiplyFunction is not defined
The closure has allowed us to define a function for private use, while still allowing us to control what the rest of the world sees. What else can closures do?
Using Closures For Meta-ProgrammingClosures are quite handy when it comes to generating code. Tired of remembering all those pesky key codes for keyboard events? A common technique is to use a key map:
var KeyMap = {
"Enter":13,
"Shift":16,
"Tab":9,
"LeftArrow":37
};
Then, in our keyboard event, we want to check if a certain key was pressed:
var txtInput = document.getElementById('myTextInput');
txtInput.onkeypress = function(e) {
var code = e.keyCode || e.which //usual fare for getting the pressed key
if (code === KeyMap.Enter) {
console.log(txtInput.value);
}
}
Capturing A Moment In Time
The above example isn’t the worst, but we can use meta-programming and closures to make an even better solution. Using our existing KeyMap object, we can generate some useful functions:
for (var key in KeyMap) {
//access object with array accessor to set "dyanamic" function name
KeyMap["is" + key] = (function(compare) {
return function(ev) {
var code = ev.keyCode || ev.which;
return code === compare;
}
})(KeyMap[key]);
}
Closures are so powerful because they can capture the local variable and parameter bindings of the function in which they are defined.
This loop generates an is function for every key in KeyMap, and our txtInput.onkeypress function becomes a bit more readable:
var txtInput = document.getElementById('myTextInput');
txtInput.onkeypress = function(e) {
if(KeyMap.isEnter(e)) {
console.log(txtInput.value);
}
}
The magic starts here:
KeyMap["is" + key] = (function(compare){
})(KeyMap[key]); //invoke immediately and pass the current value at KeyMap[key]
As we loop over the keys in KeyMap, we pass the value referenced by that key to the anonymous outer function and invoke it immediately. This binds that value to the compare parameter of this function.
The closure we are interested in is the one we are returning from inside the anonymous function:
return function(ev) {
var code = ev.keyCode || ev.which;
return code === compare;
}
Remember, functions are executed with the scope that was in place when they were defined. The compare parameter is bound to the KeyMap value that was in place during a loop iteration, and so our nested closure is able to capture it. We take a snapshot in time of the scope that was in a effect at that moment.
The functions we created allow us to skip setting up the code variable everytime we want to check the key code, and we now have convenient, readable functions to use.
At this point, it should be relatively easy to see that closures are vital to writing top notch JavaScript. Let’s apply what we know about closures to augmenting one of JavaScript’s native types (gasp!). With our focus on function objects, let’s augment the native Function type:
Function.prototype.cached = function() {
var self = this, //"this" refers to the original function
cache = {}; //our local, lexically scoped cache storage
return function(args) {
if(args in cache) return cache[args];
return cache[args] = self(args);
};
};
This little gem allows any and every function to create a cached version of itself. You can see the function returns a function itself, so this enhancement can be applied and used like so:
Math.sin = Math.sin.cached(); Math.sin(1) // => 0.8414709848078965 Math.sin(1) // => 0.8414709848078965 this time pulled from cache
Notice the closure skills that come into play. We have a local cache variable that is kept private and shielded from the outside world. This will prevent any tampering that might invalidate our cache.
The closure being returned has access to the outer function’s bindings, and that means we are able to return a function with full access to the cache inside, as well as the original function! This small function can do wonders for performance. This particular extension is set up to handle one argument, but I would love to see your stab at a multiple argument cache function.
Closures in the WildAs an added bonus, let’s take a look at a couple uses of closures in the wild.
jQuery
Sometimes, the famous jQuery $ factory is not available (think WordPress), and we want to use it in the way we typically do. Rather than reach for jQuery.noConflict, we can use a closure to allow functions inside to have access to our $ parameter binding.
(function($){
$(document).ready(function(){
//business as usual....
});
})(jQuery);
Backbone.js
On large Backbone.js projects, it might be favorable to have your application models private, and then expose one public API on your main application view. Using a closure, you can easily acheive this privacy.
(function(exports){
var Product = Backbone.Model.extend({
urlRoot: '/products',
});
var ProductList = Backbone.Collection.extend({
url: '/products',
model: Product
});
var Products = new ProductList;
var ShoppingCartView = Backbone.View.extend({
addProduct: function (product, opts) {
return CartItems.create(product, opts);
},
removeProduct: function (product, opts) {
Products.remove(product, opts);
},
getProduct: function (productId) {
return Products.get(productId);
},
getProducts: function () {
return Products.models;
}
});
//export the main application view only
exports.ShoppingCart = new ShoppingCartView;
})(window);
Conclusion
A quick recap of what we learned:
Thanks so much for reading! Feel free to ask any questions. Now let’s enjoy the pizza party!
If you’re working on a large project, you’ll no doubt have a build script or a bunch of task scripts to help with some of the repetitive parts of the process. You might use Ant or Rake, depending on the language the project is written in.
But what do you use if the project is primarily JavaScript? That’s the problem Ben Alman set out to solve when he created Grunt.
What is Grunt, Anyway?What exactly is Grunt? Well, the README on Github says
Grunt is a task-based command line build tool for JavaScript projects.
Here’s the idea: when working on a JavaScript project, there are a bunch of things you’ll want to do regularly. Like what, you ask? Well, like concatenating given files, running JSHint on your code, running tests, or minifying your scripts. If you’re pasting your JavaScript into JSHint online, you probably realize that there’s a better way to do it; even if you’re using cat to concatenate files or a command line minifier, it would be nice to have a single, unified set of commands for all those extra tasks, that worked for every single JavaScript project, right?
That’s what Grunt aims to be. It has a bunch of built-in tasks that will get you pretty far, with the ability to build your own plugins and scripts that extend the basic functionality.
For more Grunt intro goodness, see Ben’s post on his personal blog and the Bocoup blog.
How Do I Install Grunt?Grunt is built on Node.js, and it’s available as a package via the Node package manager (npm). You’ll want to install it globally, so use this command:
npm install -g grunt
You’ll notice it installs quite a few dependencies; there are other npm packages that Grunt uses. Once that’s done, you’re all set to go!
How Do I Use Grunt?As you know, Grunt is a command line tool; therefore, I’ll assume you have a terminal window open for the rest of this tutorial.
Let’s start by creating a sample project directory; we’re not actually going to be building a project here, but we’ll be seeing how Grunt works in this directory. Once you’re within that directory, run the grunt command (according to the documentation, if you’re on Windows, you might have to run grunt.cmd). You’ll probably see something like this:
<FATAL> Unable to find 'grunt.js' config file. Do you need any --help? </FATAL>
Before you can really leverage Grunt to its fullest potential, you’re going to need a grunt.js file in the project directory. Thankfully, Grunt can auto-generate a grunt.js file—and some other project skeleton material—with the init task, which can run without a grunt.js file in place. But grunt init still isn’t enough to get your project started, as you’ll see if you run it. You need to choose a type of project to generate. Running grunt init will give you a list of project types to choose from:
jquery: A jQuery pluginnode: A Node modulecommonjs: A CommonJS modulegruntplugin: A Grunt plugingruntfile: A Gruntfile (grunt.js)If your project doesn’t really match any of the first four project types, you can use the final one: gruntfile: it just creates a basic grunt.js that you can fill in. So, let’s give this a try, with the jQuery plugin template. Run grunt init:jquery in your terminal.
You’ll notice a lot of initial output. If you take the time to read the template notes, you’ll see that we’re going to have to fill in a few values, like project name and project title. In fact, after that note, you’ll see something like this:
Please answer the following: [?] Project name (jquery.demo)
Whenever you initialize a project, Grunt will ask you a series of questions, so it can fill in a few options. That value in the parentheses? That’s the default suggestion, based on the project type and the name of the project directory. If you want to change it, write your own project name at the end of the line and hit ‘enter’; otherwise, just hit ‘enter’ to use the default name.
Keep going and fill in the rest of the fields. For a jQuery plugin project, here’s what else you’ll need to give it:
A lot of these have default values; if you want to use the default value, just hit enter for that line; to leave the field blank, you can just type “none.” Once you’ve gone through all the options, you’ll see that Grunt is creating some basic project files. Like what? Like this:
LICENSE-GPL
LICENSE-MIT
README.md
grunt.js
libs
|-- jquery
| |-- jquery.js
|-- qunit
|-- qunit.css
|-- qunit.js
package.json
src
|-- jquery.demo.js
test
|-- jquery.demo.html
|-- jquery.demo_test.js
As you can see, this gives us a good start: not only do we have our plugin file (src/jquery.demo.js), we also have Qunit tests (test/jquery.demo_test.js). And these aren’t empty files, either. They’ve got some initial content, with a so-super-basic jQuery plugin and unit tests. Go ahead and check out the contents of these files, you’ll see what I mean.
Grunt does more than set up the project for you.
Of course, Grunt does more than set up the project for you. Notably, our project now has grunt.js: a project-specific configuration file; because of the options it sets, we’re now able to use Grunt’s other built-in tasks. Soon we’ll crack it open and make some adjustments, but for now let’s run some tasks.
If you run grunt with no options now, we’ll run the default task, if one has been set. In the case of a jQuery plugin project, that’s equivalent to running these four commands:
grunt lint: checks your JavaScript against JSHintgrunt qunit: runs your Qunit testsgrunt concat: concatenates your project files together and puts the new file in a dist foldergrunt min: minifies the file concat put out.I should note something about the Qunit tests here: Qunit tests are meant to run in the browser by default; just open tests/jquery.demo.html (or your equivalent) in the browser. However, the grunt qunit test wants to run them on the terminal, which means you need to have PhantomJS installed. It’s not difficult: just head over to phantomjs.org and download and install the latest version. If Grunt can find that in your path, it will be able to run the Qunit tests from the terminal.
So, running grunt should give you output to similar to this:
As you can see, each of our four tasks have run. If any of them were to fail, the rest of the tasks would be cancelled (unless you call Grunt with the --force flag).
Already, we’ve gotten a lot of great functionality out of Grunt, using it just as it comes. However, let’s crack open that grunt.js file and do some configuring.
Inside grunt.js, you’ll see that all configuring is done by passing an object literal to grunt.initConfig(). Let’s look at a few of the properties of our config object.
pkg
This property points to the package.json file that Grunt created in our project directory. Having a package.json file is part of the CommonJS Packages spec; it’s a single place where most of the metadata about the project (name, version, homepage, repository link … many of the values you set when initializing the project) can be stored. However, this pkg property does more than point to the package file: notice the syntax: '<json:package.json>'. That’s one of Grunt’s built-in directives: it actually loads the JSON file, so Grunt (or you) can access all the properties in the package.json file from the pkg property.
meta
The meta property is an object with only a single property: a banner. This banner is the comment that goes at the top of concatenated or minified project files. As you can see, it’s a string with some template tags (<%= %>); in most cases, the tags surround a call to a property on the pkg property, such as pkg.title. However, you can also execute functions from inside those tags: the use of grunt.template.today() and _.pluck() shows us that.
concat / min / qunit / lint / watch
I’ve grouped the next five properties together because they’re very similar. They all set options for specific tasks, the tasks they’re named after. When configuring these tasks, it’s important to note that Grunt distinguished between two types of tasks: regular tasks, and multitasks. Basically, the difference is that regular tasks have only a single set of configuration options, whereas multitasks can have multiple sets of instructions (called targets). Of the five tasks I listed in the header of this section, the only one that isn’t a multitask is watch.
Notice that in our config object, the qunit and lint properties are both objects with the files property. files is a single target for this task. In both cases, they’re an array of files to be used when executing this task. Let’s say I want to be able to lint only the files in src sub-directory. I could add another target so that the lint property would look like this:
lint: {
files: ['grunt.js', 'src/**/*.js', 'test/**/*.js'],
src: ['src/**/*.js']
},
Now, to lint only the files in src, I run grunt lint:src: I pass the target name after a colon. If I run only grunt lint, both targets will be run.
In the case of the concat and min tasks, the targets are more complicated: they are objects with source (src) and destination (dest) properties. Of course, this tells Grunt where to get the files and where to put them when it is done processing them, respectively. If you add other files to your project, you’ll want to add them in the right place to make sure they are concatenated and minified correctly. So, if I added a src/utils.js file that my jQuery plugin depended on, I’d change concat.dist.src to this:
src: ['<banner:meta.banner>', 'src/utils.js', '<file_strip_banner:src/<%= pkg.name %>.js>'],
Looking at some of these tasks more closely, you’ll notice a few other directives: the most important is probably the directive. This allows you to access the properties of other tasks for reuse. You’ll notice that the configuration for the watch task uses , so that it operates on the same list of files that we gave to the lint task. You can learn more about the other directives in the Grunt docs.
Speaking of the watch task, what exactly does it do? Very simple: it runs the tasks in the tasks property when a file in that list of files is changed. By default, the lint and qunit tasks are run.
jshint
This property simply configures what “bad parts” JSHint looks for in your JavaScript. The complete list of options can be found on the JSHint website’s options pages.
At the very bottom of our grunt.js file, you’ll see this line:
grunt.registerTask('default', 'lint qunit concat min');
This is what creates our default task; you know, the one that runs when we run just grunt. It’s actually creating an alias task, and you can create as many alias tasks as you want:
grunt.registerTask('src', 'lint:src qunit:src concat:src min:src');
Assuming you created src targets for each of those tasks, you can now call grunt src and do exactly what you want.
While the tasks that come with Grunt will get your pretty far, you can probably think of other things that you’d love to be able to automate. Not to worry: Grunt comes with an API that allows anyone to create Grunt tasks and plugins. While we won’t be creating any Grunt tasks in this tutorial, if you’re interested in doing so, you should start with the Grunt plugin template (run grunt init:gruntplugin), and then read through the API docs. Once you’ve written your task, you can load it into a project by adding this line inside your project’s grunt.js file:
grunt.loadTasks(PATH_TO_TASKS_FOLDER);
Note that the parameter isn’t the path to the task file itself, it’s the path to the folder the task file is in.
However, other Grunt plugins are starting to appear, and some are available on NPM. After you install them via npm install, you’ll load them into your project with this line:
grunt.loadNpmTasks(PLUGIN_NAME);
Of course, you’ll want to check the plugin documentation to see what you should add to your configuration object.
What Grunt plugins are available? Well, since Grunt is so new (less than an month old as I write this), there aren’t too many yet. I’ve found two:
grunt-css: for linting and minifying CSSgrunt-jasmine-task: for running Jasmine specsIf you’ve found others, I’d love to hear about them; post ‘em in the comments!
ConclusionWhile Grunt is a very new project, it’s hardly incomplete; as we’ve seen, it comes with pretty much everything you’ll need to use it on a large project, and can be extended as much as you want.
I’m hoping Grunt will become a community standard, and that we’ll see lots of tasks, plugins, and init templates popping up in the near future. How do you feel about it?
We’re in need of a kick-ass, talented author to help out on Nettuts+ on a regular basis, a Bieber fan preferably.
We’re not sticklers but we’d optimally like to see weekly and bi-weekly submissions from you. Do you have what it takes? This is a paid gig, so read on!
What You NeedSo, assuming you submit an article a week, and one Premium tut/screencast per month, you can expect to make between $1100 – $1400 each month.
Excellent! Where Do I Sign Up?We’ll need the following information from you:
Please email this information to net[at]tutsplus.com.
We get a ton of single line emails. Don’t be that person. Emails without the requisite information will be discarded.
If you can’t manage a weekly/bi-weekly schedule, get in touch either way. I’m sure we can work something out!
Get cracking, ladies and gents! Looking forward to hearing from all of you.
The Truematter team was recently tasked with creating a fun, sexy web app that presents numerical data in an engaging way. After putting our heads together, we came up with a bar chart that responds interactively to user input without, heaven forbid, reloading any pages. We’re very pleased to be able to share that with the coding community. There are some CSS tricks involved, a dose of jQuery, and some graphic magic, but when we’re done with this tutorial, we’ll have a beautifully interactive chart that you can customize to your heart’s content.
Prepping the GraphicsThe graphic elements and the way those elements are chopped up and compiled is what really gives this chart some wow-factor. There are only three graphic elements needed for this project, but, since we’re essentially creating an illusion using CSS and images, those graphics need to be crafted with precision.
The chart itself is composed of three separate PNGs stacked on top of each other using CSS, two of which are stationary (the front and back “glassy” chart casing pieces), and one of which is animated using jQuery (the green bar piece in the middle).
Graphic Element 1: Back of the Chart
The background piece is comprised of the bottom of the chart (which we only see when the bar is empty at 0%), the back left and back right panels. On this bottommost layer, we include all of the faces of the graph casing that need to become “covered” when the graph is full or partially full. This is the layer that really gives the 3D illusion some depth.
Graphic Element 2: The Bar Graph
The animated center portion of this graph is simple to create. We suggest using very basic 3D extrude functions in Illustrator, or you can also just draw a three-panel shape in Photoshop and call it a day. Color your graph very brightly, oversaturating it as much as possible. Why? Because in the final chart, it will be shown beneath a semi-transparent casing, so the color will be muted by the layer that covers it.
It’s super important that the top and bottom edges of this piece remain transparent, so be sure to save it as a PNG.
Also just as important: make this piece exactly the same height as the graph casing pieces. In our pictures here, the middle layer looks shorter than the casing – when you create your central piece, make the central bar exactly as tall as the casing. If you make it too short, your graph will never be able to fill up to 100% properly. If you make it too tall, the graph can shoot out of the casing container when it’s animated.
Graphic Element 3: The Top Casing
Here’s where we get to the real visual trickery, which happens along the bottom edge of this third and final piece. While the top left and right edges retain transparency, the bottom left and right edges should be filled in solid with the same color as the background the graph will be sitting on.
For example, if your website background is black, the bottom two edges of this piece need to be filled in with solid black. In our example above, the site background is white, so we’ve filled in the bottom edges with white. This solid-color area effectively hides any overspill from the middle bar-chart piece (graphic element 2). Without this piece and its opaque bottom edge, we lose the 3D effect entirely. Here’s the graph without it:
The HTML
Okay! We’ve got all of our graphic pieces complete and we’re ready to jump right into the code. The HTML itself is pretty straightforward. You’re looking at a few basic form select fields and a few divs for the bar graph.
First, let’s look at the complete code, then we’ll take it piece by piece (no pun intended!). Note that the bar graph div elements are included within the form tag.
<noscript><h3>You must enable JavaScript to view this demo!</h3></noscript> <form id="form1" action="#" onsubmit="return false;"> <!-- container for the form elements --> <div id="formcontainer"> How much money do you want to contribute to savings each month? <select id="savings" onchange="calculate();"> <option selected="selected" value="0">- select an amount -</option> <option value="100">100 dollars</option> <option value="200">200 dollars</option> <option value="300">300 dollars</option> <option value="400">400 dollars</option> <option value="500">500 dollars</option> <option value="1000">1000 dollars</option> <option value="2000">2000 dollars</option> <option value="3000">3000 dollars</option> <option value="4000">4000 dollars</option> <option value="5000">5000 dollars</option> </select> What is your monthly income? <select id="income" onchange="calculate();"> <option selected="selected" value="0">- select an amount -</option> <option value="1000">1000 dollars</option> <option value="2000">2000 dollars</option> <option value="3000">3000 dollars</option> <option value="4000">4000 dollars</option> <option value="5000">5000 dollars</option> </select> </div> <div id="grapharea"> <h4>Percentage of Monthly Salary Saved</h4> <div id="p25">25%</div> <div id="p50">50%</div> <div id="p75">75%</div> <div id="p100">100%</div> <div id="graphcontainer"> <img id="graphbackground" src=" [d2o0t5hpnwv4c1.cloudfront.net] alt="" width="262" height="430" /> <img id="graphbar" src=" [d2o0t5hpnwv4c1.cloudfront.net] alt="" width="262" height="430" /> <img id="graphforeground" src=" [d2o0t5hpnwv4c1.cloudfront.net] alt="" width="262" height="430" /> </div> </div> </form>The Breakdown
Since this chart depends on javascript, we first check to make sure it’s enabled in the browser:
<noscript><h3>You must enable JavaScript to view this demo!</h3></noscript>
Now we begin the form. For the purposes of this demo, we’ve used two simple dropdowns, but you could just as easily modify this code to use text fields, radio buttons, or any other type of input. The select onchange attribute is given the function “calculate();”, which we’ll cover in the jQuery section below.
<div id="formcontainer"> How much money do you want to contribute to savings each month? <select id="savings" onchange="calculate();"> <option selected="selected" value="0">- select an amount -</option> <option value="100">100 dollars</option> <option value="200">200 dollars</option> <option value="300">300 dollars</option> <option value="400">400 dollars</option> <option value="500">500 dollars</option> <option value="1000">1000 dollars</option> <option value="2000">2000 dollars</option> <option value="3000">3000 dollars</option> <option value="4000">4000 dollars</option> <option value="5000">5000 dollars</option> </select> What is your monthly income? <select id="income" onchange="calculate();"> <option selected="selected" value="0">- select an amount -</option> <option value="1000">1000 dollars</option> <option value="2000">2000 dollars</option> <option value="3000">3000 dollars</option> <option value="4000">4000 dollars</option> <option value="5000">5000 dollars</option> </select> </div>
And finally, included within the form tag, the graphic elements:
<div id="grapharea"> <h4>Percentage of Monthly Salary Saved</h4> <div id="p25">25%</div> <div id="p50">50%</div> <div id="p75">75%</div> <div id="p100">100%</div> <div id="graphcontainer"> <img id="graphbackground" src=" [d2o0t5hpnwv4c1.cloudfront.net] alt="" width="262" height="430" /> <img id="graphbar" src=" [d2o0t5hpnwv4c1.cloudfront.net] alt="" width="262" height="430" /> <img id="graphforeground" src=" [d2o0t5hpnwv4c1.cloudfront.net] alt="" width="262" height="430" /> </div> </div>The CSS
#formcontainer { float: left; }
#grapharea { position: relative; float: left; margin-left: 4em; }
#graphcontainer {
position: relative;
width: 262px;
height: 430px;
overflow: hidden;
}
#graphbackground, #graphforeground { position: absolute; }
#graphbar { position: absolute; top: 430px; }
.graphlabel { position: absolute; left: 270px; }
#p25 { top: 365px; }
#p50 { top: 278px; }
#p75 { top: 191px; }
#p100 { top: 105px; }
Probably the most crucial thing that should catch your eye here is the #graphbar div. Notice that our #graphbar div starts out at 430px – the total height of the bar chart. This height declaration is the number that is manipulated via jQuery to make the graph animate. If you have created your own graphic elements which are taller or shorter than 430px, you’ll need to adjust that number to match your own graphics.
Likewise, you’ll also need to adjust the px of #p25, #p50, #p75, and #p100. These top declarations set the position of the 25%, 50%, 75% and 100% markers on the right side of the graph.
Also, take a close look at the #graphcontainer styles:
#graphcontainer {
position: relative;
width: 262px;
height: 430px;
overflow: hidden;
}
Putting a position:relative;declaration on this outer wrapper lets us use absolute positioning on the three divs within #graphcontainer, so we can stack them on top of each other using CSS offsets. Setting a height and a width are also vital here – the height and width of this div should perfectly match the height and width of graphic elements 1 and 3. And overflow: hidden; hides the animated graphic, element 2, when it extends outside the bounds of the wrapping div.
The CodeAh, now for the jQuery! It ties all pieces together and adds in the animation. A quick review of the full jQuery:
<script>
function calculate() {
var ZEROPOS = 355;
var salary = $("#income").val();
var savings = $("#savings").val();
var percent = savings / salary;
if (parseInt(savings) > 0 && parseInt(salary) > 0)
{
if (parseInt(savings) > parseInt(salary))
alert("You cannot save more than you earn - please change your selections accordingly.");
else
{
val = (percent == 0) ? ZEROPOS : ZEROPOS - (percent * ZEROPOS);
$("#graphbar").animate({ top: val + "px" }, 1500);
}
}
}
</script>
The Final Breakdown
First, create a constant to use in the calculations. ZEROPOS is the pixel value of where the central graphic element (graphic 2, the movable piece) should be positioned to represent 0%.
var ZEROPOS = 355;
Next, find the values selected by the user using the select boxes – salary and savings. Then calculate the percentage value – percent.
var salary = $("#income").val();
var savings = $("#savings").val();
var percent = savings / salary;
The next step is a validation check to make sure the user has actually selected values from both select boxes. If they have, we continue with the script. Otherwise, we do nothing.
if (parseInt(savings) > 0 && parseInt(salary) > 0)
If the user has entered the appropriate values, we then make sure that they have selected a salary that is higher than the amount they want to contribute to savings. If they have, we continue with the script. Otherwise we prompt the user to change their selections.
if (parseInt(savings) > parseInt(salary))
alert("You cannot save more than you earn - please change your selections accordingly.");
Now we are finally to the meat of the script – calculating the position to which we will move the graph bar and then firing off the jQuery animate functionality to make it happen.
val = (percent == 0) ? ZEROPOS : ZEROPOS - (percent * ZEROPOS);
$("#graphbar").animate({ top: val + "px" }, 1500);
So, in calculating the value for the graph bar position – val – first determine if the percentage is zero. If so, set value equal to the ZEROPOS constant. Otherwise, calculate the new bar position by subtracting the percentage value multiplied by the zero position value from the zero position value. This formula is used because we are animating the bar from the zero position at the bottom of the chart up towards the top.
Once the pixel value is calculated, we know where we’re heading, so simply call the animate method in jQuery on the graphbar object passing in the new CSS value for the top position with an animation rate of 1500 (the speed of the animation in milliseconds). Then jQuery does the magic for us and moves the bar within the chart accordingly. Violà!
Hope you enjoyed this tutorial!
I’m frequently asked about how I’m able to create new directory structures and files so quickly in Sublime Text 2. Well the answer is that this functionality is not offered natively; instead, I use a helpful plugin. I’ll demonstrate it in this video.
It’s worth mentioning that, toward the end of the video, I assign a custom key-binding: n-n. Please note that this binding assumes that you’re working in Vintage (Vim) mode. To trigger it, you’d exit to command mode – by pressing Esc – and then type n-n.
If you’re not a Vintage user, you can stick with the default key-binding that comes with the plugin.
Show Links
Admit it: every time you see a person using Vim, you consider the possibility that they know something you don’t. Why else would they be using an editor that, in your mind, is dated, open-source junk? Think what you wish, but there’s a reason why top developers swear by Vim.
February 2011Until you’ve spent at least a month working every day with the editor, you’ll undoubtedly hate it! This is specifically why the majority of newcomers will play around with Vim for a day, become disgusted, and never touch it again. What’s unfortunate is that, if these developers could get beyond the pitfalls, they’d be introduced to incredible speed and flexibility.
1. There’s Too Many ModesIt’s true: Vim is not your standard code editor (it’s better). Transitioning from, say, TextMate to Vim is going to be an entirely different process than switching from TextMate to Espresso. Always keep that in mind when you find yourself tearing our your hair because Vim seemingly refuses to enter text when you type.
Though there are multiple modes in Vim, we’ll focus on the three most important. But before we continue, note that each key serves a different function, dependent upon which mode you’re currently in. Even more confusing — at first — a capital letter triggers a different action than a lowercase.
In command mode, pressing the lowercase “i” will trigger “Insert Mode.” However, an uppercase “I” will move the cursor to the beginning of the line. This might sound confusing, but it provides you will incredible power!
V to switch to Visual mode, and then type 5j to select five lines down. Finally, you’d press d, for delete. I know this sounds incredibly confusing at first. You might think to yourself, “All that work just to delete five lines?” The truth is, though, this method is significantly faster.
2. Ancient EditorWhy would you turn your nose up at over three decades of development?
You might hear your friends say, “Isn’t Vim an ancient code editor?” Well, you could say that; it’s been around for over thirty years. Though honestly, why would you turn your nose up at over three decades of development? That’s longer than I’ve been alive! Better yet, even to this day, Vim is under active development. The most recent release, 7.3, was made available in August, 2010.
Secondly, it’s important to keep in mind that Vim is not Vi. If your only experience is with the latter, take some time to download the latest release and toy around with the new features. You’ll be pleasantly surprised!
3. I Love TextMate SnippetsIf Vim can’t natively do it, you can bet that there’s a plugin available somewhere!
You’ll quickly find that, if Vim can’t perform a particular task, then it’s likely that a plugin has developed to provide that functionality. For instance, consider TextMate’s excellent snippets feature. While Vim doesn’t support this natively, you can download the snipMate plugin, which should make the experience virtually identical to what you’re used to.
Learn More About Switching to Vim From TextMateThe less movement, the better.
Firstly, this isn’t true. It might have been the case with Vi, but you’re free to use how Vim in the way that feels most comfortable to you. So use the arrow keys to your heart’s content — though keep in mind that there’s a reason why most Vim users don’t.
The h,j,k,l keys mapping to left, down, up, and right, respectively, serve two purposes:
5. I’m a Designer, DudeWhen all is said and done, you are the person using the editor. If, at first, you feel more comfortable using the arrow keys, then by all means do!
That’s okay! Vim is not for everybody. As a person who at least attempts to do design work from time to time, I can fully attest that Vim may not be the best fit for designers.
Developers are not designers; it’s only natural that this fact will be reflected in their choice of editors.
If you find that the bulk of your days are spent working with HTML and CSS, then maybe Vim is not for you. Now, that’s not to say that you shouldn’t give it a shot; but it’s certainly understandable, should you decide to stick with a more designer-friendly editor, such as Coda.
6. Vim Offers Nothing My Current Editor Doesn’t Already DoPlain and simple, that’s rubbish. Certainly, every editor does have its strong points, but you’ll find that Vim is incredibly powerful, and, more importantly, flexible. There are hundreds upon hundreds of plugins available (for free) that will provide support for virtually any kind of functionality that you require.
Many newcomers often cite the built-in “Change Inner” command as a huge selling point. It certainly was for me! Let’s say that you have the following piece of code:
var joe = 'plumber';
Assuming that the cursor is at the beginning of that line, and you wish to change the value “plumber” to “black,” traditionally, you might use the arrow keys or the mouse to select and change the text. With Vim, the process is hugely simplified. Simply type: ci'. This stands for “Change Inner Quotes,” or, find the next set of single quotes, and change the value inside.
ul > li { My list item text. }. This was an initial gripe that I had with Vim, as well. When first launching, say, MacVim, you’re thrown into the wolf-pack! No code highlighting, no formatting, no smart indenting… no nothing! Particularly if you’re using a custom Vim editor, there should at least be a base vimrc file to get you started. It can be an intimidating experience trying to figure out how to apply your custom preferences.
For those unfamiliar with a vimrc file, it’s essentially a file that allows you to specify your editor preferences.
Use this as a starter (click the Expand button below):
" .vimrc File " Maintained by: Jeffrey Way " jeffrey@jeffrey-way.com " [net.tutsplus.com] " "Forget compatibility with Vi. Who cares. set nocompatible "Enable filetypes filetype on filetype plugin on filetype indent on syntax on "Write the old file out when switching between files. set autowrite "Display current cursor position in lower right corner. set ruler "Want a different map leader than \ "set mapleader = ","; "Ever notice a slight lag after typing the leader key + command? This lowers "the timeout. set timeoutlen=500 "Switch between buffers without saving set hidden "Set the color scheme. Change this to your preference. "Here's 100 to choose from: [www.vim.org] colorscheme twilight "Set font type and size. Depends on the resolution. Larger screens, prefer h20 set guifont=Menlo:h14 "Tab stuff set tabstop=3 set shiftwidth=3 set softtabstop=3 set expandtab "Show command in bottom right portion of the screen set showcmd "Show lines numbers set number "Prefer relative line numbering? "set relativenumber" "Indent stuff set smartindent set autoindent "Always show the status line set laststatus=2 "Prefer a slightly higher line height set linespace=3 "Better line wrapping set wrap set textwidth=79 set formatoptions=qrn1 "Set incremental searching" set incsearch "Highlight searching set hlsearch " case insensitive search set ignorecase set smartcase "Hide MacVim toolbar by default set go-=T "Hard-wrap paragraphs of text nnoremap <leader>q gqip "Enable code folding set foldenable "Hide mouse when typing set mousehide "Shortcut to fold tags with leader (usually \) + ft nnoremap <leader>ft Vatzf " Create dictionary for custom expansions set dictionary+=/Users/jeff_way/.vim/dict.txt "Opens a vertical split and switches over (\v) nnoremap <leader>v <C-w>v<C-w>l "Split windows below the current window. set splitbelow " session settings set sessionoptions=resize,winpos,winsize,buffers,tabpages,folds,curdir,help "Set up an HTML5 template for all new .html files "autocmd BufNewFile * silent! 0r $VIMHOME/templates/%:e.tpl "Load the current buffer in Firefox - Mac specific. abbrev ff :! open -a firefox.app %:p<cr> "Map a change directory to the desktop - Mac specific nmap <leader>d :cd ~/Desktop<cr>:e.<cr> "Shortcut for editing vimrc file in a new tab nmap <leader>ev :tabedit $MYVIMRC<cr> "Change zen coding plugin expansion key to shift + e let g:user_zen_expandabbr_key = '<C-e>' "Faster shortcut for commenting. Requires T-Comment plugin map <leader>c <c-_><c-_> "Saves time; maps the spacebar to colon nmap <space> : "Automatically change current directory to that of the file in the buffer autocmd BufEnter * cd %:p:h "Map code completion to , + tab imap <leader><tab> <C-x><C-o> " More useful command-line completion set wildmenu "Auto-completion menu set wildmode=list:longest "http://vim.wikia.com/wiki/Make_Vim_completion_popup_menu_work_just_like_in_an_IDE set completeopt=longest,menuone inoremap <expr> <CR> pumvisible() ? "\<C-y>" : "\<C-g>u\<CR>" inoremap <expr> <C-n> pumvisible() ? '<C-n>' : \ '<C-n><C-r>=pumvisible() ? "\<lt>Down>" : ""<CR>' inoremap <expr> <M-,> pumvisible() ? '<C-n>' : \ '<C-x><C-o><C-n><C-p><C-r>=pumvisible() ? "\<lt>Down>" : ""<CR>' "Map escape key to jj -- much faster imap jj <esc> "Delete all buffers (via Derek Wyatt) nmap <silent> ,da :exec "1," . bufnr('$') . "bd"<cr> "Bubble single lines (kicks butt) "http://vimcasts.org/episodes/bubbling-text/ nmap <C-Up> ddkP nmap <C-Down> ddp "Bubble multiple lines vmap <C-Up> xkP`[V`] vmap <C-Down> xp`[V`] " Source the vimrc file after saving it. This way, you don't have to reload Vim to see the changes. if has("autocmd") augroup myvimrchooks au! autocmd bufwritepost .vimrc source ~/.vimrc augroup END endif " easier window navigation nmap <C-h> <C-w>h nmap <C-j> <C-w>j nmap <C-k> <C-w>k nmap <C-l> <C-w>l "------------------------" "NERDTREE PLUGIN SETTINGS "------------------------" "Shortcut for NERDTreeToggle nmap <leader>nt :NERDTreeToggle <CR> "Show hidden files in NerdTree let NERDTreeShowHidden=1 "autopen NERDTree and focus cursor in new document autocmd VimEnter * NERDTree autocmd VimEnter * wincmd p "Helpeful abbreviations iab lorem Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. iab llorem Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. "Spelling corrects. Just for example. Add yours below. iab teh the iab Teh The " Get to home dir easier " <leader>hm is easier to type than :cd ~ nmap <leader>hm :cd ~/ <CR> " Alphabetically sort CSS properties in file with :SortCSS :command! SortCSS :g#\({\n\)\@<=#.,/}/sort " Shortcut to opening a virtual split to right of current pane " Makes more sense than opening to the left nmap <leader>bv :bel vsp " Saves file when Vim window loses focus au FocusLost * :wa " Backups set backupdir=~/.vim/tmp/backup// " backups set directory=~/.vim/tmp/swap// " swap files set backup " enable backup " No more stretching for navigating files "noremap h ; "noremap j h "noremap k gj "noremap l gk "noremap ; l set showmatch " show matching brackets " print empty <a> tag map! ;h <a href=""></a><ESC>5hi8. I Don’t Want to Use the Terminal
Me neither — at least not more than I have too — there are a variety of dedicated Vim editors which provide a more Windows/Mac GUI-like experience.
These editors will provide support for the sorts of keystrokes that are ingrained into your body, such as “Command + F” to search, or “Command + W” to close the current window.
Well I suppose that depends on if you’re more of a visual learner. However, if that’s the case — promo alert — you might consider checking out our “Venturing into Vim” 4-week course. What’s unique about this series is that I recorded each episode in real-time, as I was learning Vim for the first time. The advantage to this is that you’ll get a far more practical overview of Vim. What confused me at first will undoubtedly confuse you as well!
You can purchase it on the Tuts+ marketplace for $19.
10. I Can’t Edit Files on My ServerRemember how, in school, sometimes, the student sitting next to you was able to explain and help you understand some difficult concept more than the teacher? The reason is because, once you’re mastered a craft, it’s difficult to recall what personally gave you the most trouble. From September to October, I embarked on a four-week challenge, which I call “Venturing into Vim.” After hearing countless extremely talented developers praise this seemingly decade-old code editor, I decided that it was worth a month of my time to figure out why so many people consider Vim to be the best editor on the planet.
Of course you can, though, admittedly, it’s not quite as user-friendly as, say, Coda’s remote server feature. Speaking of Panic, if you’re a Transmit user, you might consider installing the Transmit FTP plugin.
“This script allows you to upload the current file via Transmit directly from Vim. For it to work, you need to be working on a file that’s tied to a Transmit connection, and this connection must have “DockSend” enabled.”
With this plugin, when editing a file that has a Transmit connection (open file via Transmit), you only need to press Control + U to push those updates back to your remote server. It’s a cinch!
Truthfully, though, you should try to adopt a better build/deployment process. This way, rather than using FTP, you can simply git push.
Sure – there are a handful of reasons not to use Vim. It has a steep learning curve, and requires a complete rethinking of how a code editor should function. That said, there are hundreds of reason why you should use Vim. The only question is: why aren’t you?
It’s a month of new beginnings over at our design and inspiration gallery Creattica. April has seen the site undergo a complete redesign with a whole new streamlined grid-style layout for its over 50 different categories. On top of that, Creattica now has better navigation, a new UI Elements section and a growing Freebies area of now over 700 items. All this and over 14,000 featured works from some 100,000 members. It’s quite the site!
But that’s not all, let’s first take a look at some of Creattica’s top UI work – and then stay tuned for some exciting news at the conclusion!
2. User Login
3. Simple Breadcrumb
4. WIP – Calendar App
5. Minimal Login User Interface
6. Switch
7. the One Club Video Player
8. the Creative Finder
9. Pagination
10. Pricing Tables
The Future of Creattica
When we started Creattica (originally Faveup) back in 2007, we wanted it to be one on the top inspiration galleries on the web. And as the site has grown and changed over the years, we find ourselves with what has become one of the biggest, broadest and most in-depth galleries of design out there. So I guess we can say – mission accomplished!
Over these past few months, Envato has really started working on focusing on our core products of Marketplaces and Tuts+ sites. So while we love Creattica, we find ourselves in the regrettable position of having to put the site up for sale soon. We really want it to go to an awesome home, so if anyone would like to express interest in the sale or get more details, please contact us here.
I’m certain we’ll find some new owners who can keep Creattica growing and evolving on into the future. And in the meantime I hope you enjoy the new design and all the gorgeous work as much as I do!
There are a number of ways to profile and monitor the performance of PHP code in a development environment, but once it’s been let loose on a production server its extremely difficult to know what’s going on when your app is getting accessed by your users.
In March 2009, Facebook released XHProf, a function level profiler that allows you to identify bottlenecks in your application. And it runs on a production server with little overhead.
XHProf will provide you with a hierarchical profile of the functions your application calls, allowing you to follow the chain of events that lead up to resource intensive code running. You’ll be able to see exactly how much wall time (the actual time elapsed), CPU time and memory your application uses.
This tutorial will help you get XHProf installed on your server, and integrated with your CodeIgniter application via hooks so that you can start profiling your application, in a production environment, straight away. Everything you see here was written for Ubuntu 10.04 – the latest LTS release at the time of writing.
InstallationInstallation of XHProf can be done via PECL – that said, I’ve never been able to get the PECL version installed on Ubuntu easily so its easier to install from source. Start off by downloading the latest revision from the XHProf GitHub account.
wget [https:] -O xhprof.tar.gz
Extract the tarball and switch into the extracted folder – this will change depending on the latest revision available from GitHub.
tar -xvf xhprof.tar.gz cd facebook-xhprof-bc8ef04/extension
Then install as normal.
phpize ./configure make sudo make install
Finally, we need to tell PHP to load the extension. Create a config file for it – I do this for cleanliness but you can always drop it at the end of your main php.ini.
sudo nano /etc/php5/conf.d/xhprof.ini
And paste in the following:
extension=xhprof.so xhprof.output_dir="/var/tmp/xhprof"
This tells XHProf to use the directory at /var/tmp/xhprof to save its run data.
You can check whether XHProf is installed correctly by entering php -m at the command line and checking that the module is available. Remember to restart Apache so that it gets picked up in your PHP web applications as well.
If you want to render the callgraph images you’ll also need the graphviz package. This can be obtained from apt.
sudo apt-get install graphvizIntegration With CodeIgniter
XHProf can be used on an ad-hoc basis to evaluate small pieces of code, but it’s at its most useful when you let it profile the full page. First of all, you’ll need to move the XHProf code into your web applications root so that it has access to the appropriate classes.
sudo mv ~/facebook-xhprof-bc8ef04 /var/www/xhprof
CodeIgniter has an excellent mechanism for injecting custom code into the execution of a page called ‘Hooks’. This is what we’ll use to integrate XHProf with your application. Enable hooks in your application/config/config.php file.
$config['enable_hooks'] = true;
Then specify your hooks in application/config/hooks.php
$hook['pre_controller'] = array( 'class' => 'XHProf', 'function' => 'XHProf_Start', 'filename' => 'xhprof.php', 'filepath' => 'hooks', 'params' => array() ); $hook['post_controller'] = array( 'class' => 'XHProf', 'function' => 'XHProf_End', 'filename' => 'xhprof.php', 'filepath' => 'hooks', 'params' => array() );
Then create the hook that will load XHProf into your application at application/hooks/xhprof.php. This class will provide the necessary minimum to get XHProf collecting data from your application.
class XHProf {
private $XHProfPath = 'xhprof/';
private $applicationName = 'my_application';
private $sampleSize = 1;
private static $enabled = false;
public function XHProf_Start() {
if (mt_rand(1, $this->sampleSize) == 1) {
include_once $this->XHProfPath . 'xhprof_lib/utils/xhprof_lib.php';
include_once $this->XHProfPath . 'xhprof_lib/utils/xhprof_runs.php';
xhprof_enable(XHPROF_FLAGS_NO_BUILTINS);
self::$enabled = true;
}
}
public function XHProf_End() {
if (self::$enabled) {
$XHProfData = xhprof_disable();
$XHProfRuns = new XHProfRuns_Default();
$XHProfRuns->save_run($XHProfData, $this->applicationName);
}
}
}
There’s a few thing to point out in this code sample.
$XHProfPath variable should point to the directory you installed XHProf in. In our example, we put it in the root of the web application, but you might be storing it somewhere central and symlinking it to multiple applications.$applicationName variable lets you specify the name of the application using XHProf. This could be especially important in an environment where you’re running multiple applications on the same server.$sampleSize variable lets you specify a factor for how often XHProf profiles a run. In a production environment where you’re receiving thousands of hits, it’s probably not worth storing every single run. You can increase this variable to get a random sample of runs. Changing it to 10, for example, will give you a report from one in every 10 visits to your application.The xhprof_enable() function can accept any combination of 3 constants.
XHPROF_FLAGS_NO_BUILTINS – skip all internal php functions. Adding this means you’ll only see time spent in functions that you have written (or are part of CodeIgniter)XHPROF_FLAGS_CPU – add additional CPU profiling informationXHPROF_FLAGS_MEMORY – add additional memory profiling informationCombine them using +. eg. xhprof_enable(XHPROF_FLAGS_NO_BUILTINS + XHPROF_FLAGS_MEMORY);
After running through your application a couple of times, point your browser at the XHProf application directory http://localhost/xhprof/xhprof_html/index.php – adjusting for the address of your development server – and you’ll see a list of your last application runs with the newest at the top. Select one of the runs to see it’s profiling information.
Storing all this profiling information is all very well and good but knowing how to evaluate it is the key to optimising your application.
The ‘Overall Summary’ is a good place to start – it’ll show you:
These number should give you an overall base to start from and provide headline numbers to compare against when you start optimisation.
Below the summary is the detailed run report The bottlenecks in your application should be fairly obvious – they’re the function calls at the top of the report.
The most resource intensive things you’re likely to come across will be those that make calls to external services or your database. By default, the results are sorted by ‘inclusive wall time’. This shows the functions that took the longest to run by the cumulative total of them and the functions they call. Ordering by ‘exclusive wall time’ allows you to see the specific functions that are using the most amount of resource.
Clicking a function allows you to drill down into more detail about that function and the functions it called. From there, it’ll be easier to see exactly what your long running function was doing and spot any issues.
When debugging your CodeIgniter application, one of the best places to start is the controller being called for your page – Welcome::index in the example. From there you can easily see the breakdown of where the controller spent most of it’s time.
Callgraph
XHProf can generate a call graph view that provides a visual clue to the main path of your application. The main bottlenecks are highlighted in red to show where most your resources are being spent.
Comparing Runs
XHProf provides a mechanism for comparing multiple runs – this will allow you to make a number of optimisations and then see how they’ve affected your application.
http://localhost/xhprof/xhprof_html/index.php?run1=run_id_1>&run2=run_id_2&source=applicationName
Note: run_id_1 and run_id_2 should be the ids from some of your previous runs and applicationName should be the name you specified when setting up the hook earlier.
This will provide the same information you see in a regular report but provide percentage statistics on any changes since the first run, allowing you to easily see if your optimisations are moving your performance in the right direction.
Aggregating RunsSometimes a single run might not be enough to evaluate exactly what’s going on. Pages my work slightly differently depending on the query string or user input or there might be differences in caching mechanisms.
Aggregating a number of runs allows you to combine a number of runs and receive an average of the resources consumed.
http://localhost/xhprof/xhprof_html/index.php?run=run_id,run_id,run_id&source=applicationName
Note: run_id should be the ids from some of your previous runs and applicationName should be the name you specified when setting up the hook earlier.
You should now have XHProf up and running in your CodeIgniter application and be able to see exactly where your code is spending most of it’s time. Optimisation can be a difficult task. Sometimes it’s not as simple as reimplementing a function, or adding a layer of caching. Thinking about why you’re doing something and what the exact effect that is having on your application is just as important.
So let me know if you have any questions in the comments below. Thank you so much for reading!
In this Tuts+ Premium screencast, we’ll review RequireJS, an asynchronous module loader and dependency manager, and AMD, which defines the mechanism and guidelines for declaring and importing modules.
Become a Premium member to view this screencast, as well as hundreds of other advanced tutorials and screencasts from the Tuts+ network.
You’ll Learn:
The recently re-launched Tuts+ Premium is a service that provides top-tier training in a variety of creative fields. Whether you prefer books, visual training, or in depth tutorials, we have you covered. While we unfortunately can’t afford to provide the service for free, it’s only $19 a month – less than you’d spend on dinner.
I hope you’ll consider checking it out! In addition to learning a huge variety of new skills, it’s also a fantastic way to say thank you to Nettuts+.
In this lesson, we’ll review the awesome RequireJS optimizer to handle the process of merging and compressing our stylesheets. While preprocessors continue to become increasingly popular, there are still plenty of folks who stick with regular CSS. In these cases, a solid build tool/process is vital.
Choose 720p for the clearest video. Closing ThoughtsIf you don’t like the idea of using Require.js, alternatively you might consider Grunt (along with the Grunt-CSS plugin), or Jake.
Taking a break from the relatively technical focus of the past few quizzes, we’re going to do something very offbeat today. Yes, Offbeat with a capital O. Be it front end or back end programming, I’m sure you’re working with HTML tags every day. But as the saying goes, can you ‘catch ‘em all?’ Or rather, can you figure out what these rarely used HTML tags do?
I can already hear you chuckling with mirth, grasshopper. I’ve added a few deprecated tags just to keep you on your toes. Get everything right and prove that your HTML-fu is strong!
$(document).ready(function(){ $('#quiz-container').jquizzy({ questions: init.questions, resultComments: init.resultComments, twitterStatus: 'Woo! I scored {score} on the Nettuts quiz on quirky HTML tags. Give it a shot!', twitterImage: 'http://d2o0t5hpnwv4c1.cloudfront.net/jquizzy-1.0/img/share.png', twitterUsername: 'envatowebdev', splashImage: 'http://d2o0t5hpnwv4c1.cloudfront.net/1152_quiz12/nettuts.png', }); });
HTML5 provides us with a whole crop of new possibilities, such as drawing with canvas, implementing multimedia with the audio and video APIs, and so on. One of these tools, which is still relatively new, is the File System API. It gives us access to a sandboxed section of the user’s local file system, thus filling the gap between desktop and web applications even further! In today’s tutorial, we’ll go through the basics of this new and exciting API, exploring the most common filesystem tasks. Let’s get started!
IntroductionNo longer do we need to download and install a given piece of software in order to use it. Simply a web browser and an internet connection gives us the ability to use any web application, anytime, anywhere, and on any platform.
In short, web apps are cool; but, compared to desktop apps, they still have one significant weakness: they don’t have a way to interact and organize data into a structured hierarchy of folders – a real filesystem. Fortunately, with the new Filesystem API, this can be changed. This API gives web applications controlled access to a private local filesystem “sandbox,” in which they can write and read files, create and list directories, and so on. Although at the time of this writing only Google’s Chrome browser supports the “full” implementation of the Filesystem API, it still deserves to be studied as a powerful and convenient form of local storage.
The Filesystem API comes in two different versions. The asynchronous API, which is useful for normal applications, and the synchronous API, reserved for use with web workers. For the purposes of this tutorial, we will exclusively explore the asynchronous version of the API.
Step 1 – Getting StartedYour first step is to obtain access to the HTML5 Filesystem by requesting a LocalFile System object, using the window.requestFileSystem() global method:
window.requestFileSystem(type, size, successCallback, opt_errorCallback)
There’s no way for a web application to “break out” beyond the local root directory.
As the first two parameters, you specify the lifetime and size of the filesystem you want. A PERSISTENT filesystem is suitable for web apps that want to store user data permanently. The browser won’t delete it, except at the user’s explicit request. A TEMPORARY filesystem is appropriate for web apps that want to cache data, but can still operate if the web browser deletes the filesystem. The size of the filesystem is specified in bytes and should be a reasonable upper bound on the amount of data you need to store.
The third parameter is a callback function that is triggered when the user agent successfully provides a filesystem. Its argument is a FileSystem object. And, lastly, we can add an optional callback function, which is called when an error occurs, or the request for a filesystem is denied. Its argument is a FileError object. Although this parameter is optional, it’s always a good idea to catch errors for users, as there are a number of places where things can go wrong.
The filesystem obtained with these functions depends on the origin of the containing document. All documents or web apps from the same origin (host, port, and protocol) share a filesystem. Two documents or applications from different origins have completely distinct and disjoint filesystems. A filesystem is restricted to a single application and cannot access another application’s stored data. It’s also isolated from the rest of the files on the user’s hard drive, which is a good thing: there’s no way for a web application to “break out” beyond the local root directory or otherwise access arbitrary files.
Let’s review an example:
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
window.requestFileSystem(window.TEMPORARY, 5*1024*1024, initFS, errorHandler);
function initFS(fs){
alert("Welcome to Filesystem! It's showtime :)"); // Just to check if everything is OK :)
// place the functions you will learn bellow here
}
function errorHandler(){
console.log('An error occured');
}
This creates a temporary filesystem with 5MB of storage. It then provides a success callback function, which we will use to operate our filesystem. And, of course, an error handler is also added – just in case something goes wrong. Here, the errorHandler() function is too generic. So if you want, you can create a slightly optimized version, which gives the reader a more descriptive error message:
function errorHandler(err){
var msg = 'An error occured: ';
switch (err.code) {
case FileError.NOT_FOUND_ERR:
msg += 'File or directory not found';
break;
case FileError.NOT_READABLE_ERR:
msg += 'File or directory not readable';
break;
case FileError.PATH_EXISTS_ERR:
msg += 'File or directory already exists';
break;
case FileError.TYPE_MISMATCH_ERR:
msg += 'Invalid filetype';
break;
default:
msg += 'Unknown Error';
break;
};
console.log(msg);
};
The filesystem object you obtain has a name (a unique name for the filesystem, assigned by the browser) and root property that refers to the root directory of the filesystem. This is a DirectoryEntry object, and it may have nested directories that are themselves represented by DirectoryEntry objects. Each directory in the file system may contain files, represented by FileEntry objects. The DirectoryEntry object defines methods for obtaining DirectoryEntry and FileEntry objects by pathname (they will optionally create new directories or files if you specify a name that doesn’t exist). DirectoryEntry also defines a createReader() factory method that returns a DirectoryReader object for listing the contents of a directory. The FileEntry class defines a method for obtaining the File object (a Blob) that represents the contents of a file. You can then use a FileReader object to read the file. FileEntry defines another method to return a FileWriter object that you can use to write content into a file.
Phhew…sounds complicated? Don’t worry. Everything will become clearer as we progress through the examples below.
Step 2 – Working With DirectoriesObviously, the first thing you need to create in a filesystem is some buckets, or directories. Although the root directory already exists, you don’t want to place all of your files there. Directories are created by the DirectoryEntry object. In the following example, we create a directory, called Documents, within the root directory:
fs.root.getDirectory('Documents', {create: true}, function(dirEntry) {
alert('You have just created the ' + dirEntry.name + ' directory.');
}, errorHandler);
The getDirectory() method is used both to read and create directories. As the first parameter, you can pass either a name or path as the directory to look up or create. We set the second argument to true, because we’re attempting to create a directory – not read an existing one. And at the end, we add an error callback.
So far, so good. We have a directory; let’s now add a subdirectory. The function is exactly the same with one difference: we change the first argument from ‘Documents’ to ‘Documents/Music’. Easy enough; but what if you want to create a subfolder, Sky, with two parent folders, Images and Nature, inside the Documents folder? If you type ‘Documents/Images/Nature/Sky‘ for the path argument, you will receive an error, because you can’t create a directory, when its immediate parent does not exist. A solution for this is to create each folder one by one: Images inside Documents, Nature inside Images, and then Sky inside Nature. But this is a very slow and inconvenient process. There is a better solution: to create a function which will create all necessary folders automatically.
function createDir(rootDir, folders) {
rootDir.getDirectory(folders[0], {create: true}, function(dirEntry) {
if (folders.length) {
createDir(dirEntry, folders.slice(1));
}
}, errorHandler);
};
createDir(fs.root, 'Documents/Images/Nature/Sky/'.split('/'));
With this little trick, all we need to do is provide a full path representing the folders which we want to create. Now, the Sky directory is successfully created, and you can create other files or directories within it.
Now it’s time to check what we have in our filesystem. We’ll create a DirectoryReader object, and use the readEntries() method to read the content of the directory.
fs.root.getDirectory('Documents', {}, function(dirEntry){<br>
var dirReader = dirEntry.createReader();
dirReader.readEntries(function(entries) {<br>
for(var i = 0; i < entries.length; i++) {
var entry = entries[i];
if (entry.isDirectory){
console.log('Directory: ' + entry.fullPath);
}
else if (entry.isFile){
console.log('File: ' + entry.fullPath);
}
}
}, errorHandler);
}, errorHandler);
In the code above, the isDirectory and isFile properties are used in order to obtain a different output for directories and files, respectively. Additionally, we use the fullPath property in order to get the full path of the entry, instead of its name only.
There are two ways to remove a DirectoryEntry from the filesystem: remove() and removeRecursively(). The first one removes a given directory only if it is empty. Otherwise, you’ll receive an error.
fs.root.getDirectory('Documents/Music', {}, function(dirEntry) {
dirEntry.remove(function(){
console.log('Directory successfully removed.');
}, errorHandler);
}, errorHandler);
If the Music folder has files within it, then you need to use the second method, which recursively deletes the directory and all of its contents.
fs.root.getDirectory('Documents/Music', {}, function(dirEntry) {
dirEntry.removeRecursively(function(){
console.log('Directory successufully removed.');
}, errorHandler);
}, errorHandler);
Step 3 – Working With Files
Now that we know how to create directories, it’s time to populate them with files!
The following example creates an empty test.txt in the root directory:
fs.root.getFile('test.txt', {create: true, exclusive: true}, function(fileEntry) {
alert('A file ' + fileEntry.name + ' was created successfully.');
}, errorHandler);
The first argument to getFile() can be an absolute or relative path, but it must be valid. For instance, it is an error to attempt to create a file, when its immediate parent does not exist. The second argument is an object literal, describing the function’s behavior if the file does not exist. In this example, create: true creates the file if it doesn’t exist and throws an error if it does (exclusive: true). Otherwise, if create: false, the file is simply fetched and returned.
Having an empty file is not very useful, though; so let’s add some content inside. We can use the FileWriter object for this.
fs.root.getFile('test.txt', {create: false}, function(fileEntry) {
fileEntry.createWriter(function(fileWriter) {
window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder;
var bb = new BlobBuilder();
bb.append('Filesystem API is awesome!');
fileWriter.write(bb.getBlob('text/plain'));
}, errorHandler);
}, errorHandler);
Above, we retrieve the test.txt file, and create a FileWriter object for it. We then append content to it by creating a new BlobBuilder object and using the write() method of FileWriter.
Calling getFile() only retrieves a FileEntry. It does not return the contents of the file. So, if we want to read the content of the file, we need to use the File object and the FileReader object.
fs.root.getFile('test.txt', {}, function(fileEntry) {
fileEntry.file(function(file) {
var reader = new FileReader();
reader.onloadend = function(e) {
alert(this.result);
};
reader.readAsText(file);
}, errorHandler);
}, errorHandler);
We have written some content to our file, but what if desire to add more at a later date? To append data to an existing file, the FileWriter is used once again. We can reposition the writer to the end of the file, using the seek() method. seek accepts a byte offset as an argument, and sets the file writer’s position to that offset.
fs.root.getFile('test.txt', {create: false}, function(fileEntry) {
fileEntry.createWriter(function(fileWriter) {
fileWriter.seek(fileWriter.length);
window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder;
var bb = new BlobBuilder();
bb.append('Yes, it is!');
fileWriter.write(bb.getBlob('text/plain'));
}, errorHandler);
}, errorHandler);
To remove a file from the filesystem, simply call entry.remove(). The first argument to this method is a zero-parameter callback function, which is called when the file is successfully deleted. The second is an optional error callback if any errors occur.
fs.root.getFile('test.txt', {create: false}, function(fileEntry) {
fileEntry.remove(function() {
console.log('File successufully removed.');
}, errorHandler);
}, errorHandler);
Step 4 – Manipulating Files and Directories
FileEntry and DirectoryEntry share the same API methods for copying, moving and renaming entries. There are two methods you can use for these operations: copyTo() and moveTo(). They both accept the exact same parameters:
copyTo(parentDirEntry, opt_newName, opt_successCallback, opt_errorCallback); moveTo(parentDirEntry, opt_newName, opt_successCallback, opt_errorCallback);
The first parameter is the parent folder to move/copy the entry into. The second is an optional new name to give the moved/copied entry, which is actually required when you copy an entry in the same folder; otherwise you will get an error. The third and fourth parameters were explained previously.
Let’s review some simple examples. In the following one, we copy the file test.txt from the root to the Documents directory.
function copy(currDir, srcEntry, destDir) {
currDir.getFile(srcEntry, {}, function(fileEntry) {
currDir.getDirectory(destDir, {}, function(dirEntry) {
fileEntry.copyTo(dirEntry);
}, errorHandler);
}, errorHandler);
}
copy(fs.root, 'test.txt', 'Documents/');
This next example moves test.txt to Documents, instead of copying it:
function move(currDir, srcEntry, dirName) {
currDir.getFile(srcEntry, {}, function(fileEntry) {
currDir.getDirectory(dirName, {}, function(dirEntry) {
fileEntry.moveTo(dirEntry);
}, errorHandler);
}, errorHandler);
}
move(fs.root, 'test.txt', 'Documents/');
The following example renames test.txt to text.txt:
function rename(currDir, srcEntry, newName) {
currDir.getFile(srcEntry, {}, function(fileEntry) {
fileEntry.moveTo(currDir, newName);
}, errorHandler);
}
rename(fs.root, 'test.txt', 'text.txt');
Learn More
In this introductory tutorial, we’ve only scratched the surface of the different filesystem interfaces. If you want to learn more and dig deeper into Filesystem API, you should refer to the W3C specifications specifications:
Now that you have a basic understanding of what the Filesystem API is, and how it can be used, it should be considerably easier to understand the API documentation, which can be a bit confusing at first sight.
ConclusionThe Filesystem API is a powerful and easy to use technology, which provides web developers with a whole crop of new possibilities when building web applications. Admittedly, it’s still quite new and not widely supported by all major browsers, but this will certainly change in the future. You might as well get a head start!
Over the past few weeks, we’ve been working hard behind the scenes to create a central place for our Tuts+ readers to call “home”. Somewhere that shows you the latest content from across all our sites, allows you to filter only those tutorials that interest you, and shares a few statistics about the Tuts+ network.
Today, we’re very excited to introduce the Tuts+ Hub — read on to find out more about it!
All Our Latest Tutorials in One PlaceThe Tuts+ Hub is a new way to see all the latest content across our whole network. Any new tutorials that have been published since your last visit will be given a “New” banner, and you can quickly filter out the sites that don’t interest you — just toggle the checkboxes at the top of the screen to show/hide content for each site.
We’re also showcasing all our new Tuts+ Premium tutorials — these are shown with a yellow border, so it’s worth keeping an eye out for these if you’re one of our awesome Tuts+ Premium members!
A Handful of StatisticsWe also thought it would be fun to share a few of our statistics with you. Did you know we’ve published over 11,500 tutorials? Or that we’re approaching half a million comments across the network? It’s a fun way to keep an eye on how Tuts+ is growing (and these stats are updated every day).
What Do You Think?
I’d love to hear your feedback on Tuts+ Hub, and any thoughts or comments on what works well/what you’d like to see improved. We’re really happy with how the project turned out and I think you’re going to love it!
Thanks for being one of our readers and, if you’ve somehow resisted all the way down to this point, head over and check out the Tuts+ Hub!
In this hands-on Premium screencast, from scratch, we’ll use test-driven development to build a jQuery plugin. Along the way, we’ll take advantage of Grunt, QUnit, and GitHub to build, test, and distribute our plugin, respectively. If the idea of testing in JavaScript is still foreign to you, you won’t find a better introduction!
Become a Tuts+ Premium member to watch this 45 minute screencast, as well as hundreds of other advanced videos, tutorials, and courses.
You’ll Learn How to:Of course, you’re likely to pick up a variety of other neat tips and tricks along the way!
Tuts+ Premium
The recently re-launched Tuts+ Premium is a service that provides top-tier training in a variety of creative fields. Whether you prefer books, visual training, or in depth tutorials, we have you covered. While we unfortunately can’t afford to provide the service for free, it’s only $19 a month – less than you’d spend on dinner.
I hope you’ll consider checking it out! In addition to learning a huge variety of new skills, it’s also a fantastic way to say thank you to Nettuts+.
Recently, I’ve been doing a considerable amount of CoffeeScript work. One problem I ran into early-on was testing: I didn’t want to have to manually convert my CoffeeScript to JavaScript before I could test it. Instead, I wanted to test from CoffeeScript directly. How’d I end up doing it? Read on to find out!
You’ll need to have Node.js and Node Package Manager installed.
Before we continue on, I’ll point out that you need to have a decent knowledge of CoffeeScript for this tutorial; I won’t be explaining the bits and pieces here. If you’re interested in CoffeeScript, you should check out the CoffeeScript tuts available here on Nettuts+, or the CoffeeScript documentation.
Additionally, you’ll need to have Node.js and the Node Package Manager (npm) installed for this tutorial. If you don’t have ‘em installed, no worries: head over to nodejs.org and download the installer for your platform; then, well, install it!
Meeting Mocha and ChaiWe’ll be building the beginnings of a todo list application (cliché, I know). These will be CoffeeScript classes. Then, we’ll write some tests with Mocha and Chai to test that functionality.
Why both Mocha and Chai? Well, Mocha is a testing framework, but it doesn’t include the actual assertions component. That might sound strange: after all, there isn’t much more to a testing library, is there? Well, there is, in Mocha’s case. The features that brought me to the library are two-fold: the ability to run tests from the command line (instead of having an HTML page to run them in the browser), and the ability to run test in CoffeeScripts, without having to convert that code to JavaScript (as least manually: Mocha does it behind the scenes). There are other features, too, that I won’t be talking about here, including:
And on, and on. See more at the Mocha home page. To install Mocha simply run npm install -g mocha, and you’re set.
As for Chai: it’s a great assertion library that offers interfaces for doing both BDD and TDD; you can use it both in the browser or on the command line via node, which is how we’ll use it today. Install it for Node, via npm install -g chai.
Now that we have our libraries installed, let’s start writing some code.
Setting Up Our ProjectLet’s begin by setting up a mini project. Create a project folder. Then, create two more folders in that one: src, and test. Our CoffeeScript code will go in the src folder, and our tests will go in, you guessed it, the tests folder. Mocha looks for a test folder by default, so by doing this, we’ll save ourselves some typing later.
Mocha looks for a
testfolder by default.
We’re going to create two CoffeeScript classes: Task, which will be a todo item, and TaskList, which will be a list of todo items (yes, it’s more than an array). We’ll put them both in the src/task.coffee file. Then, the tests for this will be in test/taskTest.coffee. Of course, we could split ‘em into their own files, but we’re just not going to do that today.
We have to start by importing the Chai library and enabling the BDD syntax. Here’s how:
chai = require 'chai' chai.should()
By calling the chai.should method, we’re actually adding a should property to Object.prototype. This allows us to write tests that read like this:
task.name.should.equal "some string"
If you prefer the TDD syntax, you can do this:
expect = chai.expect
… which allows you to write tests like this:
expect(task.name).to.equal "some string"
We’ll actually have to use both of these, as you’ll see; however, we’ll use the BDD syntax as much as possible.
Now we’ll need to import our Task and TaskList classes:
{TaskList, List} = require '../src/task'
If you aren’t familiar with this syntax, that’s CoffeeScript’s destructured assignment at work, as well as some of its object literal sugar. Basically, our require call returns an object with two properties, which are our classes. This line pulls them out of that object and gives us two variables named Task and TaskList, each of which points to the respective class.
Great! Now, how about a test? The beauty of the Mocha syntax is that its blocks (describe and it) are identical to Jasmine’s (both being very similar to RSpec). Here’s our first test:
describe 'Task instance', -> task1 = task2 = null it 'should have a name', -> task1 = new Task 'feed the cat' task1.name.should.equal 'feed the cat'
We start with a describe call: all these tests are for a Test instance. By setting test1 = test2 = null outside our individual tests, we can use those values for multiple tests.
Then, in our first test, we’re simply creating a task and checking to see that its name property has the correct value. Before writing the code for this, let’s add two more tests:
it 'should be initially incomplete', -> task1.status.should.equal 'incomplete' it 'should be able to be completed', -> task1.complete().should.be.true task1.status.should.equal 'complete'
Ok, let’s run these tests to make sure they’re failing. To do this, let’s open a command prompt and cd to your project folder. Then, run this command:
mocha --compilers coffee:coffee-script
Mocha doesn’t check for CoffeeScript by default, so we have to use the --compilers flag to tell Mocha what compiler to use if it finds a file with the coffee file extension. You should get errors that look like this:
If, instead of seeing that, you get the error Cannot find module '../src/task', it’s because your src/task.coffee file doesn’t exist yet. Make said file, and you should get said error.
Well, now that we have failing tests, it’s time to write the code, correct? Open that src/task.coffee file and let’s get cracking.
class Task constructor: (@name) ->
Just this is enough to get our first test passing. If you aren’t familiar with that parameter syntax, that just sets whatever value was passed to new Task to the @name (or this.name) property. However, let’s add another line to that constructor:
@status = 'incomplete'
That’s good. Now, head back to the terminal and re-run our tests. You’ll find that—wait a second, nothing’s changed! Why aren’t our first two tests passing?
A simple problem, actually. Because the CoffeeScript compiler wraps the code in each file in a IIFE (or, a self-invoking anonymous function), we need to “export” anything we want to be accessible from other files. In the browser, you’d do something like window.Whatever = Whatever. For Node, you can use either global or exports. We’ll be using exports, since 1) that’s considered best practice, and 2) that’s what we prepared for when setting up our tests (remember our require call?). Therefore, at the end of our task.coffee file, add this:
root = exports ? window root.Task = Task
With that in place, you should find that two of our three tests are now passing:
To get the last test to pass, we’ll have to add a complete method. Try this:
complete: -> @status = 'complete' true
Now, all tests pass:
Now’s a good time to mention that Mocha has a number of different reports: these are just different ways to output the test results. You can run mocha --reporters to see your options:
By default, Mocha uses the dot reporter. However, I prefer the spec reporter, so I tack -R spec on the end of command (-R is the reporter-setting flag).
Adding a Feature
Let’s add a feature to our Task class: we’ll let tasks be dependent on other tasks. If the “parent” task isn’t completed, the “child” task can’t be done. We’ll keep this feature simple and allow tasks to have only one sub-task. We also won’t check for recursiveness, so while it will be possible to set two tasks to be the parent and child of each other, it will render both tasks incomplete-able.
Tests first!
it 'should be able to be dependent on another task', -> task1 = new Task 'wash dishes' task2 = new Task 'dry dishes' task2.dependsOn task1 task2.status.should.equal 'dependent' task2.parent.should.equal task1 task1.child.should.equal task2 it 'should refuse completion it is dependent on an uncompleted task', -> (-> task2.complete()).should.throw "Dependent task 'wash dishes' is not completed."
Task instances are going to have a dependsOn method, which tasks the task that will become their parent. Tasks that have a parent task should have a status of “dependent.” Also, both tasks get either a parent or child property that points to the appropriate task instance.
In the second test there, we say that a task with an incomplete parent task should throw an error when its complete method is called. Notice how test syntax works: we need to call should off of a function, and not the result of the function: therefore, we wrap the function in parentheses. This way, the test library can call the function itself and check for the error.
Run those tests and you’ll see that both fail. Coding time!
dependsOn: (@parent) -> @parent.child = @ @status = 'dependent'
Again, very simple: we just set the task parameter to the parent task, and give it a child property which points to this task instance. Then, we set the status of this task to be “dependent.”
If you run this now, you’ll see that one of our tests is passing, but the second isn’t: that’s because our complete method doesn’t check for an uncompleted parent task. Let’s change that.
complete: ->
if @parent? and @parent.status isnt 'completed'
throw "Dependent task '#{@parent.name}' is not completed."
@status = 'complete'
true
Here’s the completed complete method: if there’s a parent task, and it isn’t completed, we throw an error. Otherwise, we complete the task. Now, all tests should pass.
Next, we’ll build the TaskList class. Again, we’ll start with a test:
describe 'TaskList', ->
taskList = null
it 'should start with no tasks', ->
taskList = new TaskList
taskList.tasks.length.should.equal 0
taskList.length.should.equal 0
This is old-hat to you by now: we’re creating a TaskList object and checking its tasks and length properties to makes sure their both at zero. As you might guess, tasks is an array that holds the tasks, while length is just a handy property that we’ll update when adding or removing tasks; it just saves us from having to write list.tasks.length.
To make this test pass, we’ll make this constructor:
class TaskList constructor: () -> @tasks = [] @length = 0
Good start, and that gets our test passing.
We’ll want to be able to add tasks to a task list, right? We’ll have an add method that can take either a Task instance, or a string which it will convert to a Task instance.
Our tests:
it 'should accept new tasks as tasks', ->
task = new Task 'buy milk'
taskList.add task
taskList.tasks[0].name.should.equal 'buy milk'
taskList.length.should.equal 1
it 'should accept new tasks as string', ->
taskList.add 'take out garbage'
taskList.tasks[1].name.should.equal 'take out garbage'
taskList.length.should.equal 2
First, we add an actual Task object, and check the taskList.tasks array to verify that it’s been added. Then, we add a string, and make sure that a Task object with the right name was added to the tasks array. In both cases, we check the length of taskList as well, to make sure that it’s being property updated.
And the function:
add: (task) ->
if typeof task is 'string'
@tasks.push new Task task
else
@tasks.push task
@length = @tasks.length
Pretty self-explanatory, I think. And now our tests pass:
Of course, we might want to remove tasks from our list, right?
it 'should remove tasks', -> i = taskList.length - 1 taskList.remove taskList.tasks[i] expect(taskList.tasks[i]).to.not.be.ok
First, we call the remove method (yet to be written, of course), passing it the last task currently in the list. Sure, we could just hardcode the index 1, but I’ve done it this way because that makes this test flexible: if we changed our previous tests or added more tests above this one, that might have to change. Of course, we have to remove the last one because otherwise, the task after it will take its place and there’ll be something at that index when we’re expecting there to be nothing.
And speaking of expecting, notice that we’re using the expect function and syntax here instead of our usual should. This is because taskList.tasks[i] will be undefined, which doesn’t inherit from Object.prototype, and therefore we can’t use should.
Oh, yeah, we still need to write that remove function:
remove: (task) ->
i = @tasks.indexOf task
@tasks = @tasks[0...i].concat @tasks[i+1..] if i > -1
@length = @tasks.length
Some fancy array footwork combined with CoffeeScript’s ranges and array splicing shorthand closes this deal for us. We’re simply splitting off all the items before the one to remove and all the items after it; the we concat those two arrays together. Of course, we’ll update @length accordingly. Can you say “passing tests”?
Let’s do one more thing. We want to print our a (relatively) nice-looking list of the current tasks. This will be our most complex (or at least, our longest) test yet:
it 'should print out the list', ->
taskList = new TaskList
task0 = new Task 'buy milk'
task1 = new Task 'go to store'
task2 = new Task 'another task'
task3 = new Task 'sub-task'
task4 = new Task 'sub-sub-task'
taskList.add task0
taskList.add task1
taskList.add task2
taskList.add task3
taskList.add task4
task0.dependsOn task1
task4.dependsOn task3
task3.dependsOn task2
task1.complete()
desiredOutput = """Tasks
- buy milk (depends on 'go to store')
- go to store (completed)
- another task
- sub-task (depends on 'another task')
- sub-sub-task (depends on 'sub-task')
"""
taskList.print().should.equal desiredOutput
What’s going on here? First, we’re creating a new TaskList object so that we start from scratch. Then, we create five tasks and add them to taskList. Next, we set up a few dependencies. Finally we complete one of our tasks.
We’re using CoffeeScript’s heredoc syntax to create a multi-line string. As you can see, we’re keeping it pretty simple. If a task has a parent task, it’s mentioned in parentheses after the task name. If a task is completed, we put that, too.
Ready to write the function?
print: ->
str = "Tasks\n\n"
for task in @tasks
str += "- #{task.name}"
str += " (depends on '#{task.parent.name}')" if task.parent?
str += ' (complete)' if task.status is 'complete'
str += "\n"
str
It’s actually pretty straightforward: we just look over the @tasks array and add ‘em to a string. If they have a parent, we add that, and if they’re complete, we add that too. Notice that we’re using the modifier form of the if statement, to tighten up our code. Then, we return the string.
Now, all our tests should pass:
Wrapping Up
Try adding a few features to get the hang of it all.
That’s the extent of our little project today. You can download the code from the top of this page; in fact, why don’t you try adding a few features to get the hang of it all? Here are a few ideas:
Task instances from being able to depend on each other (recursive dependencies).TaskList::add method throw an error if it receives something other than a string or a Task object.These days, I’m finding CoffeeScript more and more attractive, but the biggest downside to it is that it must be compiled to JavaScript before it’s useful. I’m grateful for anything that negates some of that workflow breaker, and Mocha definitely does that. Of course, it’s not perfect (since it’s compiling to JS before running the code, line numbers in errors don’t match up with your CoffeeScript line numbers), but it’s a step in the right direction for me!
How about you? If you’re using CoffeeScript, how have you been doing testing? Let me know in the comments.
Sure, we’re all familiar with borders. Is there anything new that could possibly be introduced? Well, I bet there’s quite a few things in this article that you never knew about!
Not only can CSS3 be used to create rounded corners, but plain-ole’ CSS can also be wrestled into displaying custom shapes. That’s right; in the past, before these techniques were discovered, we might have resorted to using absolutely positioned background images to display circles or arrows. Thankfully – as we gleefully take one more step away from Photoshop – this is no longer the case.
The BasicsYou’re likely familiar with the most basic use of borders.
border: 1px solid black;
The above code will apply a 1px border to an element. Plain and simple; but we can also modify the syntax a bit.
border-width: thick; border-style: solid; border-color: black;
In addition to passing a specific value to border-width, three keywords may alternatively be used: thin, medium, and thick.
While it might initially seem unnecessary to ever make use of the long-hand form, there are a handful of cases when it’s advantageous, such as when you need to update some aspect of a border when a designated event occurs.
Perhaps you need to change the color of a border when the user hovers over a specific element. Using the shorthand form would require that you repeat the pixel values.
.box {
border: 1px solid red;
}
.box:hover {
border: 1px solid green;
}
A more elegant and DRY approach would be to specifically update the border-color property.
.box {
border: 1px solid red;
}
.box:hover {
border-color: green;
}
Additionally, as you’ll find shortly, this long-hand technique is helpful when creating custom shapes with CSS.
Border-Radiusborder-radius is the golden child of CSS3 – the first new property to gain widespread use in the community. What this translates to is that, excluding Internet Explorer 8 and below, all browsers can display rounded corners.
Previously, it was necessary to use vendor prefixes for both Webkit and Mozilla, in order for the styling to be correctly applied.
-webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px;
These days, however, we can slice off the vendor versions without worry, and simply stick with the official form: border-radius.
As one might expect, we can also specify custom values for each side of a box.
border-top-left-radius: 20px; border-top-right-radius: 0; border-bottom-right-radius: 30px; border-bottom-left-radius: 0;
In the code above, setting border-top-right-radius and border-bottom-left-radius to zero would be superfluous, unless the element is inheriting values which need to be reset.
Much like margin or padding, these settings can be condensed into a single property, if necessary.
/* top left, top right, bottom right, bottom left */ border-radius: 20px 0 30px 0;
As an example (and as web designers do so often), the shape of a lemon can be reproduced with CSS and the border-radius property, like so:
.lemon {
width: 200px; height: 200px;
background: #F5F240;
border: 1px solid #F0D900;
border-radius: 10px 150px 30px 150px;
}
Beyond the Basics
Many designers happily stick with the knowledge outlined thus far in this chapter; however, there’s a few ways we can push it further!
Multiple BordersThere’s a variety of techniques that we can refer to, when tasked with applying multiple borders to an element.
Border-StyleWhile solid, dashed, and dotted are the most frequent values for the border-style property, there’s also a few others that we can make use of, including groove and ridge.
border: 20px groove #e3e3e3;
Or, with the long-hand form:
border-color: #e3e3e3; border-width: 20px; border-style: groove;
While this is certainly helpful, a ridge or groove effect isn’t really multiple borders.
OutlineThe most popular technique for creating two borders is to take advantage of the outline property.
.box {
border: 5px solid #292929;
outline: 5px solid #e3e3e3;
}
This method works wonderfully, however, it’s limited to two borders. Should you need to create a layered, gradient-esque, effect, a different approach will be necessary.
Pseudo ElementsWhen the outline technique doesn’t suffice, an alternate approach is to take advantage of the :before and :after pseudo elements, and apply any necessary additional borders to the generated content.
.box {
width: 200px; height: 200px;
background: #e3e3e3;
position: relative;
border: 10px solid green;
}
/* Create two boxes with the same width of the container */
.box:after, .box:before {
content: '';
position: absolute;
top: 0; left: 0; bottom: 0; right: 0;
}
.box:after {
border: 5px solid red;
outline: 5px solid yellow;
}
.box:before {
border: 10px solid blue;
}
This perhaps isn’t the most elegant approach, but it certainly gets the job. One caveat is that it’s easy to confuse the order in which the border colors will be applied. A certain level of “guess and check” is often required to apply the correct sequence.
Box-ShadowThe cool kids way to create an infinite number of borders is to take advantage of the spread parameter in the box-shadow CSS3 property.
.box {
border: 5px solid red;
box-shadow:
0 0 0 5px green,
0 0 0 10px yellow,
0 0 0 15px orange;
}
In this case, we’re being clever and are using box-shadow in a way that might not necessarily have been intended when the specification was originally written.
By setting the x, y, and blur components to 0, we can instead use the spread value to create solid borders at the desired locations. Because box-shadows can be stacked, through the use of a comma, the number of possible levels is infinite.
This technique gracefully degrades quite nicely. In older browsers, which do not recognize the box-shadow property, this will simply render the single red 5px border.
Modifying AnglesRemember: designs needn’t be identical in all browsers. Write your CSS for the most modern of browsers, and then provide suitable fallbacks, accordingly.
In addition to passing a single value to border-radius, we can alternatively provide two – separated by a / – to specify unique values for both the horizontal and vertical radii.
For example…
border-radius: 50px / 100px; /* horizontal radius, vertical radius */
…is equivalent to:
border-top-left-radius: 50px 100px; border-top-right-radius: 50px 100px; border-bottom-right-radius: 50px 100px; border-bottom-left-radius: 50px 100px;
This technique is particularly helpful when you need to mimic a subtle, lengthy curve, rather than a generic rounded corner. For instance, the following code allows us to slightly break away from a square shape, resulting in more of a curled, paper-like effect.
.box {
width: 200px; height: 200px;
background: #666;
border-top-left-radius: 15em 1em;
border-bottom-right-radius: 15em 1em;
}
CSS Shapes
Perhaps the neatest use of borders is when they’re cleverly applied to elements, which have a zero width and height. Confusing, huh? Let’s see a demonstration.
For the next several examples, assume the following markup…
<div class="box"></div>
…and the following base styling:
.box {
width: 200px;
height: 200px;
background: black;
}
The most frequently referenced example, when demonstrating how CSS shapes might be used in a project, is to create the obligatory arrow.
The key to understanding how an arrow might be formed with CSS is to set a unique border-color to each side, and then reduce both the width and height values for the container to 0.
Assuming a div with a class of arrow as the container:
.arrow {
width: 0; height: 0;
border-top: 100px solid red;
border-right: 100px solid green;
border-bottom: 100px solid blue;
border-left: 100px solid yellow;
}
As demonstrated at the beginning of this chapter, a cleaner syntax would be to not use the all-encompassing short-hand version:
.arrow {
width: 0; height: 0;
border: 100px solid;
border-top-color: red;
border-right-color: green;
border-bottom-color: blue;
border-left-color: yellow;
}
We can even reduce this further, by grouping the color values.
.arrow {
width: 0; height: 0;
border: 100px solid;
border-color: red green blue yellow;
}
Interesting, right? It makes perfect sense, though, when we take a step back. That’s the only possible way that the colors could align, assuming a width and height of zero for the container. Now, what if we set all of the border-colors to transparent, except for the blue side?
.arrow {
width: 0; height: 0;
border: 100px solid;
border-bottom-color: blue;
}
Excellent! But it doesn’t seem too semantic to create an .arrow div, all for the purpose of adding an arrow to the page. Instead, pseudo elements can be used to apply the arrow after or before the associated element.
To create a 100% CSS speech bubble, we begin with the markup.
<div class="speech-bubble">Hi there!</div>
Next, some base styling should be applied.
.speech-bubble {
position: relative;
background-color: #292929;
width: 200px;
height: 150px;
line-height: 150px; /* vertically center */
color: white;
text-align: center;
}
The arrow will be applied using the after psuedo-element.
.speech-bubble:after {
content: '';
}
The
:beforeand:afterpsuedo elements can be used to insert generated content either before or after an element’s content.
At this point, it’s simply a matter of reproducing the arrow, and positioning it in the proper location. We start by absolutely positioning the content, resetting the width and height, and applying the border colors.
.speech-bubble:after {
content: '';
position: absolute;
width: 0;
height: 0;
border: 10px solid;
border-color: red green blue yellow;
}
Because we know that we want the arrow to point downward, the image above demonstrates that all but the red (or top) border should either be omitted, or set to transparent.
.speech-bubble:after {
content: '';
position: absolute;
width: 0;
height: 0;
border: 10px solid;
border-top-color: red;
}
When creating CSS shapes, because we can’t use the width property to specify how wide the arrow should be, the border-width property should be used instead. In this case, the arrow should be slightly larger; so the border-width can be increased to 15px. We’ll also position the arrow at the bottom and center of the container, by using the top and left properties, respectively.
.speech-bubble:after {
content: '';
position: absolute;
width: 0;
height: 0;
border: 15px solid;
border-top-color: red;
top: 100%;
left: 50%;
}
Almost there; the final step is to update the color of the arrow to be the same as the container’s background. The positioning also needs to be modified to account for the width of the borders (15px). While we’re here, we’ll also apply a subtle border-radius to make the container appear to be more bubble-like.
.speech-bubble {
/* … other styles */
border-radius: 10px;
}
.speech-bubble:after {
content: '';
position: absolute;
width: 0;
height: 0;
border: 15px solid;
border-top-color: #292929;
top: 100%;
left: 50%;
margin-left: -15px; /* adjust for border width */
}
Not bad, ay? Abstract this code away to a few reusable classes, and you’re good to go for all future projects.
/*
Speech Bubbles
Usage: Apply a class of .speech-bubble and .speech-bubble-DIRECTION
<div class="speech-bubble speech-bubble-top">Hi there</div>
*/
.speech-bubble {
position: relative;
background-color: #292929;
width: 200px;
height: 150px;
line-height: 150px; /* vertically center */
color: white;
text-align: center;
border-radius: 10px;
font-family: sans-serif;
}
.speech-bubble:after {
content: '';
position: absolute;
width: 0;
height: 0;
border: 15px solid;
}
/* Position the Arrow */
.speech-bubble-top:after {
border-bottom-color: #292929;
left: 50%;
bottom: 100%;
margin-left: -15px;
}
.speech-bubble-right:after {
border-left-color: #292929;
left: 100%;
top: 50%;
margin-top: -15px;
}
.speech-bubble-bottom:after {
border-top-color: #292929;
top: 100%;
left: 50%;
margin-left: -15px;
}
.speech-bubble-left:after {
border-right-color: #292929;
top: 50%;
right: 100%;
margin-top: -15px;
}
Bonus: Better Vertical Centering
One downside to using line-height to vertically center text is that you’re limited to a single line. Should the text require two or more lines, each line height will be far too large. A clever solution is to set a display of table to the speech bubble, and a display of table-cell to the paragraph that wraps the text. This then allows us to align the text to the middle, accordingly.
<div class="speech-bubble speech-bubble-top">
<p>Text goes here.</p>
</div>
Next, the modified CSS.
.speech-bubble {
/* other styles */
display: table;
}
.speech-bubble p {
display: table-cell;
vertical-align: middle;
}
If references to
display: tablebring back terrible memories of old-fashioned, table-based layouts, don’t worry. These properties merely refer to the style in which an element should display.
We’re not limited to triangles; CSS is capable of producing all sorts of shapes – even hearts and biohazard signs!
.biohazard {
width: 0; height: 0;
border: 60px solid;
border-radius: 50%;
border-top-color: black;
border-bottom-color: black;
border-left-color: yellow;
border-right-color: yellow;
}
Summary
Though it’s true that the simple border: 1px solid black syntax goes a long way, if we’re clever, we can create a variety of helpful effects, icons, and shapes. Who would have thought that borders could be so powerful? The key is to remember that the styling for common shapes or speech bubbles should only be created once, and then abstracted away to utility classes for future usage.
I’m pleased to announce that Jeremy McPeak’s latest course, JavaScript Fundamentals, is now available on Tuts+ Premium! As the author of countless books, including JavaScript: 24 Hour Trainer and Professional AJAX, Jeremy is the perfect person to show you the ropes!
With over five hours of content, you’ll learn everything from the absolute basics, all the way up to event delegation. And, with Jeremy’s teaching style, nobody gets left behind.
Take the final course quiz to ensure that you’ve mastered the skills in each lesson.
Tuts+ Premium
The recently re-launched Tuts+ Premium is a service that provides top-tier training in a variety of creative fields. Whether you prefer books, visual training, or in depth tutorials, we have you covered.
While we unfortunately can’t afford to provide the service for free, it’s only $19 a month – less than you’d spend on dinner.
But the best part is that, every month, we’re adding as many as five new in depth courses on the skills that you want to learn. What’s on the near horizon? JavaScript Testing with Jasmine, Professional Screencasting, Cleaner Code With CoffeeScript, Test-Driven Development in Rails, and countless more.
I hope you’ll consider checking it out! In addition to learning a huge variety of new skills, it’s also a fantastic way to say thank you to Nettuts+.
Welcome back to the Building a content viewer with Backbone series. Over the first four parts, we looked at almost every major component that ships with the latest version of Backbone including models, controllers, views and routers.
In this part of the tutorial, we’re going to hook our application up to a web server so that we can store our contacts in a database. We won’t be looking at LocalStorage; this is a popular means of persisting the data that Backbone apps use, but the fact is there are already a number of excellent tutorials available on this subject.
Getting Started
We’ll need a webserver and a database for this part of the tutorial. I use Microsoft’s VWD as an editor, which ships with a built-in web server and works well with MSSQL server, so this is what we’ll be using. In truth, it doesn’t really matter which stack you decide to go with.
Installing and configuring either of these technologies (VWD and MSSQL server) is beyond the scope of this tutorial, but it’s relatively straight-forward to do and there are plenty of good guides out there.
Once installed, you’ll want to set up a new database containing a table to store the data in. The table columns should mirror the different properties our models use, so there should be a name column, an address column, etc. The table can be populated with the example data we’ve used throughout the series so far.
One column that should appear in our new table, but which we haven’t used in our local test data is an id, which should be unique to each row in the table. For ease of use, you probably want to set this to auto-increment when the data is added to the table.
In order to communicate with the server, Backbone gives us the Sync module; this is the only major module that we haven’t used yet and so understanding it will complete our knowledge of the fundamentals of the framework.
Calling the sync() method results in a request being made to the server; by default, it assumes either jQuery or Zepto is in use and delegates the request to whichever of them is present to actually perform. It also assumes a RESTful interface is awaiting on the back-end so by default makes use of POST, PUT, GET, DELETE HTTP methods. As we’ve seen, Backbone can be configured to fall back to old-school GET and POST methods with additional headers which specify the intended action.
As well as being able to call sync() directly, models and collections also have methods that can be used to communicate with the server; models have the destroy(), fetch(), parse() and save() methods, and collections have fetch() and parse(). The destroy() fetch() and sync() methods all defer to sync() whether being used with models or collections. The parse() method, called automatically whenever data is returned by the server, is by default a simple no-op which just returns the response from the server, but can be overridden if we wish to pre-process the response before consuming it.
The way model data is bootstrapped into the page will vary depending on the back-end technology being used.
The Backbone documentation for the fetch() method (of a collection) states that this method should not be used on the initial page load to request the required models from the server. It goes on to elaborate in the FAQ section that a page should have the required modules already available to the page on load to avoid the initial AJAX request.
This is a great idea and while we don’t explicitly have to follow the advice, doing so will make our application just a little bit snappier, and that can only be a good thing.
The way model data is bootstrapped into the page will vary depending on the back-end technology being used. We’re going to be using .net in this example, so one way of doing this would be to dynamically create a <script> element containing the required model data, and inject it into the page. To do this we’ll need to convert our index.html file to index.aspx instead (we’ll also need an index.aspx.cs code-behind or class file too). But doing this raises a new issue.
We can lift the ‘Mustache-style’ example straight off of the Underscore documentation page.
The problem with Underscore templates is that they use <%= to specify placeholders in the template that are replaced with actual data when the template is consumed. This is the same syntax that ASPX pages use to run dynamic .Net code within HTML tags. The Underscore templates that we’ve used in this example so far prevent the ASPX page from running correctly and instead it displays a server error.
Fortunately there are several ways around this problem, the simplest way being to change the syntax used to specify the placeholders used in the templates. Underscore exposes the templateSettings property for this very purpose, allowing us to easy specify a regular expression used to match the symbols we wish to use. We can lift the ‘Mustache-style’ example straight off of the Underscore documentation page in fact; at the start of our app.js file (within the very outer function) we can just add the following code:
_.templateSettings = {
interpolate: /\{\{(.+?)\}\}/g
};
All this does is supply a new regular expression to the interpolate method, which allows us to use the alternative syntax {{ property }} instead of <%= property %>. We should also at this point go through the templates and change all of the original template tags to use the new syntax.
Although this is not something we’ve used in our templates so far, there are also additional symbols that Underscore can use. We can evaluate JavaScript using <% and can escape data using <%-. If we wish to use these in our templates and have replaced the interpolate property, we should also configure the evaluate and escape Underscore properties as well.
We can now think about delivering the model data that is stored in a database to our page when the page is initially rendered. We can easily do this be adding a simple method to the class file for our ASPX page that reads the records from the database and creates a list of objects where each object represents a single contact. We can then serialise the list into a JavaScript array and inject it into the page. As long as the array has the same format as the dummy array we used in the first four parts of this tutorial, we won’t have to change our front-end code.
As a placeholder for the array, we can just add a new <script> element to the body of the page, directly before the reference to app.js, which calls the method in the code-behind:
<script>
var contacts = <%= getData() %>
</script>
The actual logic in the code-behind that performs the database read and list serialisation could vary wildly depending on the implementation, and is somewhat beyond the scope of this tutorial – we’re more interested in getting that initial payload on the page than we are about how we actually get it. Feel free to check out the class file in the accompanying code download for probably the quickest and easiest, but by no means the best, way to do it.
At this point, we should be able to remove the contacts array that held our dummy data from app.js, run the page (through the built-in WVD webserver, or IIS) and see exactly the same page, with almost the same functionality, as we saw at the end of part 4. Yay!
In this example, I’ve used a .net 4.0 ASMX file to handle the requests from the front-end. In order for the back-end to see the data sent to it, we should configure the emulateHTTP and emulateJSON Backbone properties. Add the following lines of code directly after where we changed Underscore’s template syntax:
Backbone.emulateHTTP = true; Backbone.emulateJSON = true;
Whether or not you’ll need to configure these properties when building a Backbone app for real depends entirely on the back-end technology you choose to work with.
So, our application could modify the data in several ways; it could change the attributes of a contact that already exists, it could add an entirely new contact, or it could delete a contact that already exists.
The logic to do all of these things on the front-end already exists, but now that a server is involved, the behaviour of the page has already changed. Although the page will render as it did before, if we try to delete a contact, Backbone will throw an error complaining that a url has not been defined. The reason for this is because we used the destroy() method in the deleteContact() method of our ContactView class.
Let’s look at how to restore the delete functionality. The first thing we should do then is define a url attribute for our models. Add the property to the Contact class that defines an individual model:
url: function () {
return "/ContactManager.asmx/ManageContact?id=" + this.get("id");
}
We specify a function as the value of the url property, which returns the URL that should be used to make the requests to. In this example, we can use an asmx web service file to handle the requests. We also add the name of our web method (ManageContact) and add the id of the model as a query string parameter.
Now if we delete one of the contacts when we run the page a POST request is made to the web service. An X [HTTP-Method-Override] header is added to the request which specifies that the intended HTTP method was DELETE. We can use this in our web service logic to determine what action to take on the database.
Next we can update the saveEdits() method of the ContactView class so that it notifies the web service when a contact is edited; change the line of code that uses the set() method so that it appears like this:
this.model.set(formData).save();
All we do is chain the save() method on to the set() method. The save() method delegates to the sync() method which makes a POST request to the server. As before the id of the model is sent as a query string and an X [HTTP-Method-Override] is used to specify the intended PUT method. This time however, the Content-Type header is set to application/x-www-form-urlencoded (if we didn’t configure the emulateJSON property it would be application/json) and the model data is sent as a form data, which we can use to make whatever changes are necessary.
All that is left to do on the front-end is to update the addContact() method of the DirectoryView class. Previously in this method we had an if statement that checked the type of the model being added to see if the select menu needed to be updated. We should now change that if statement so that it appears as follows:
if (_.indexOf(this.getTypes(), formData.type) === -1) {
this.$el.find("#filter").find("select").remove().end().append(this.createSelect());
}
this.collection.create(formData);
We’ve trimmed the if statement down to remove the else condition, making the code a bit tidier. We’ve also removed the add() method and added the create() method in its place. The create() method will actually add the new model to the collection automatically without us manually creating a new instance of our model’s class, and it will also make a request to the server, once again delegating to sync().
This time the X [HTTP-Method-Override] header does not need to be set, because POST is the method that we would use were the request being made to a RESTful interface anyway. As with the save() method, the model data passed to the create() method is delivered to the server as form data.
As with the server-side code used at the start of this part of the tutorial to bootstrap the initial model data into our app, the code used to process and handle the requests made by Backbone is beyond the scope of the tutorial. We’re interested only in the front-end here. As before, the Web service used for this demo is included in the code archive and is fully commented, so check it out if you’re interested. I’ve also included a database backup, which you should be able to restore in order to get going with the demo data.
SummaryIn this part of the tutorial, we looked at some of the methods we can use which delegate to Backbone’s sync() method in order to communicate with a back-end that can persist the changes made using the front-end of the application.
We saw how Backbone by default makes RESTful requests to a specified URL and how we can configure it in order to work with legacy servers that do not operate on REST principles. We also looked at some of the methods that delegate to sync() in order to communicate with the server. Specifically, we covered the remove(), save() and create() methods and looked at what is sent to the server and how.
We also looked at how easy it is to change the symbols that Underscore uses in order to interpolate data into a template. This now concludes the Contact Manager tutorial; while there are many more features we could add to the application, we have now covered the basics of what it takes to build a fully-functional application using the excellent Backbone.js. Thanks for reading.
Each month, we bring together a selection of the best tutorials and articles from across the whole Tuts+ network. Whether you’d like to read the top posts from your favourite site, or would like to start learning something completely new, this is the best place to start!
Psdtuts+ — Photoshop Tutorials
Our First Look at Photoshop CS6 – Now Available in Beta
Today, Adobe announced some big news. Photoshop CS6 is now available for download in Beta. For the last several months, we have been speculating about what features we can expect to see in Photoshop CS6. This speculation was fueled by the Photoshop team’s release of several sneak peek videos that revealed some exciting new features. Now that the speculation is over and Photoshop CS6 Beta is available for us to download and try, we have been able to make a careful examination of some of its new features.
Quick Tip: Create a Royal Gold Text Effect in Photoshop Using Layer Styles
In this quick tutorial we will show you how to create a royal gold text effect using layer styles in Photoshop. Let’s get started!
Create a Clean Twitter App Interface in Photoshop
In this tutorial we will create a clean Twitter app interface in Photoshop using layer styles and basic vector shapes. Let’s get started!
Sass vs. LESS vs. Stylus: Preprocessor Shootout
Wielding the true power of a CSS preprocessor is an adventure. There are countless languages, syntaxes, and features, all ready for use right now.
Sexy Code Snippet Management With Gists
I’ve always struggled to find the perfect code snippet management tool…until now (and it’s free). I’ll show you my preferred workflow, using GitHub Gists and Sublime Text 2.
Create Instagram Filters With PHP
In this tutorial, I’ll demonstrate how to create vintage (just like Instagram does) photos with PHP and ImageMagick. Wait? What? Yes, you can do this very thing with PHP and ImageMagick, and that’s just scratching the surface!
Create a Psychedelic, Funky Line Art Portrait
In today’s Illustrator tutorial, I’m going to show you how I created a retro, psychedelic, funky, 70s inspired portrait using link art, bold colors and the handy Swirl Tool.
Quick Tip: Brainstorming Head Design, a Focus on the Eye Line
Varying the horizontal axis line (the eyeline) and adjusting the spacing of the eyes are two excellent methods used to brainstorm new character designs. In this tutorial, we look at a few simple, but effective, techniques you can employ to give your characters personality while making them both fresh and visually interesting.
InDesign Tutorials for Magazine and Layout Design
Do you want to learn to how to design a magazine in InDesign? Discover industry standard workflows for print design professionals, magazine layout design tips, and follow how to design a magazine cover layout that attracts attention. These are just a few of the topics you’ll find in this roundup of best InDesign magazine and layout tutorials.
The Benefits and Pitfalls of Gamification
Gamification is becoming a hot commodity around the web, but what is it? Is it being used correctly? Let’s have a look at various aspects of gamification and how they can be used and misused.
Building a Responsive Layout With Skeleton: Starting Out
Dave Gamache’s Skeleton Boilerplate provides the perfect foundations upon which to build responsive websites rapidly and reliably. We’re going to use Skeleton and build a responsive page based on the Magazine design featured on Webdesigntuts+ recently. We’ll be looking at everything from multiple background images, through to media queries, flexible media and mobile-friendly navigation. Let’s get started!
Quick Tip: Stitch up Your Layout
In this Quick Tip we’re going to create a cool stitching effect in Photoshop. We won’t be using brushes, we won’t be relying on patterns – we’re going to use a really simple and fast alternative.
How to Photograph Birds of Prey in White
The first thing people say when they see my Birds of Prey Portrait Sessions is “nice Photoshop cutout work”. In fact, it’s not a cutout, the birds are photographed against a white background. Let me show you how.
Camera Exposure Modes Explained
If you’re just getting started with photography, the letters on your camera’s dial might feel like hieroglyphics. With a variety of modes, it can be hard to pick what’s right for the current shooting situation. Today, we’re going to explain common camera modes so that you can pick the right one for any situation and grow out of using auto mode.
A Photographer’s Guide to Visiting Nepal
Nepal is a country wrapping in mystique for many. The home of the highest mountain in the world and a civilization known for living in the harsh conditions of the Himalayas, Nepal has much to offer the photographic traveler. Lush jungles cover areas of Southern Nepal which is the location of the birth place of Buddha, founder of one of the oldest religions in the world still practiced today. To the East travel becomes more rugged as compared to the more arid regions of the West.
In this tutorial from James Whiffin you’ll learn how to create a truly awesome invisibility effect using Maya and After Effects. James will cover creating a fractal material in Maya to use as a transition for the cloaking effect, and how to composite displacement maps inside of After Effects, using the luma values of ambient occlusion and depth map layers to achieve this popular effect in our shot.
In this new two part tutorial from Frederik Steinmetz, you’ll learn how to create a truly unique fish swarm animation using Blender. Frederik will guide you through the entire process of Modeling, Texturing, Rigging and Animating a realistic school of mackerels. You’ll then learn how to create a spiraling, tornado like effect for the swarm using particles.
So UVMapping… you hate it, I hate it. But unfortunately it’s a necessary step in the process of completing most cg projects. In this tutorial we’ll look at creating uvs using the ‘Unwrap UVW’ modifier in 3D Studio Max, and discuss what uv mapping is, why it’s necessary and some ways to approach it.
Third of the series: “Naked Truth About…” In this video I am showing issues with rotations in 3D space and ways to deal with them. Have you ever wondered what is the difference between Rotation and Orientation? We set two keyframes for rotations and the layer rotates strangely. Has it ever happened to you? Those problems are NOT After Effects specific. This video explains the way rotations in 3D space work. Once you understand the concept – it will be easier for you to tame rotations not only in After Effects, but also in other applications. Have fun.
When working with video files on a daily basis, alot of time is spent converting videos into different formats, whether for web, archive, or editing purposes. Add onto that the interruption of opening and closing programs to convert your files and you’ll end up spending less time being creative and more on technical chores. In this quick tip tutorial, we look at two simple ways to save you time by automating the process of conversion using droplets and watch folders.
Helpful Tips For Improving Promotional Documentary Video
Promotional video content for the web is a powerful way to communicate a message and it is used by many companies and organizations. It allows communication in a variety of forms including spoken word and visual imagery but it also facilitates a connection with the audience on a more subconscious level. A promotional video containing a product, message or guidance must look professional – but before you spend your annual marketing budget employing a production company, ask yourself if you can achieve appropriate results by applying the hints and tips below.
A Guide to Producing an Epic Orchestral Track
Although I’m not a huge fan of epic productions, they are definitely the thing that producers/directors/game developers search for nowadays. Now, as a whole, epic orchestral tracks can be heard on movies, TV, commercials, video games, trailers. They always support tension, battle, chase – all of these extremely dynamic moments that always catch the attention of the customer. This tutorial is a guide that I do hope will help you create your own epic orchestral track. Let’s get started!
How to Minimize Noise In Your Mixes
Home recordings are infamous for having noise. Background noise from outside and indoor noises from the air conditioning and people shuffling around in the next room. Home recording studios are usually never sound isolated enough. Even though you might have a great sounding room, with acoustic treatment carefully placed all over, you’re still going to run into sound isolation issues.
The biggest issue is noise, and in the following tutorial I’ll run through a couple of ways you can minimize the amount of annoying hiss and noise from your home recordings.
Making An Electro/Dubstep Bass Patch In FM8
If you fancy a bit of departure form virtual analog, subtractive synths you could try dabbling in FM (Frequency Modulation) synthesis. Native Instruments FM8 is the perfect way to get involved with this edgy sound creation method. Lets take a look at how to create a typical roughed up bass patch that you could use for Electro house or Dubstep.
Build a Stage3D Shoot-’Em-Up: Sprite Test
In this tutorial series (part free, part Premium) we’ll create a high-performance 2D shoot-em-up using the new hardware-accelerated Stage3D rendering engine. We will be taking advantage of several hardcore optimization techniques to achieve great 2D sprite rendering performance. In this part, we’ll build a high-performance demo that draws hundreds of moving sprites on-screen at once.
oCanvas: A jQuery- and Flash-Style Library for HTML5 Canvas
With the advent of tools like Adobe Edge and libraries like EaselJS, more resources are becoming available for developers looking to create interactive HTML5 content. Many of these tools are being geared specifically for Flash developers to make the transition from ActionScript to HTML5 canvas a smooth one. This article will overview oCanvas, an HTML5 library that developers might not only find invaluable but also very easy to use.
How to Build a Python Bot That Can Play Web Games
In this tutorial we’ll explore the ins and outs of building a Computer Vision-based game bot in Python, which will be able to to play the popular Flash game Sushi Go Round. You can use the techniques taught in this tutorial to create bots for automatically testing your own web games.
Using Custom Image Sizes in Your Theme and Resizing Existing Images to the New Sizes
In this tutorial you will learn how to generate custom sized images for you to use in your WordPress theme. Why use custom image sizes? So you won’t have to edit every image you upload to the Media Library. This way every image uploaded will get all the custom defined image sizes generated automatically. It can be inserted into the post or page using the Media Gallery or from the loop. Continue reading to find out how.
The Complete Guide To The WordPress Settings API, Part 4: On Theme Options
In the last article, we took a deep dive into the various types of menus that are supported by the WordPress API. Although they aren’t necessarily part of the Settings API, they play a key part in development especially when working on more advanced plugins and themes.
The header.php – What Needs to Go in It and What Doesn’t
In this tutorial, let’s talk about the header.php, an essential file for any WordPress theme. I’ll show you a nice header file example and give tips about what needs to go in it and what doesn’t.
D Game Development with ShiVa3D Suite: Project Overview
Curious about 3D game development? Now is the time to learn! This five-part tutorial series will demonstrate how to build a simple game with ShiVa3D Suite, a cross-platform 3D game engine and development tool. This series was originally intended for Tuts+ Premium members only, but will instead be given away to the community for free with each portion published back-to-back over the next 5 days. Read on to begin your journey into 3D programming!
Android Essentials: Creating Android-Compliant Libraries
Android libraries can be packaged and reused in other Android applications using the new Android Library feature of the Android SDK. In this quick tutorial, you will learn how to create simple Android library files that can be reused in multiple projects and increase developer efficiency.
Enabling Google Analytics to Gather App Statistics
Google Analytics is a service provided by Google that makes it easy to track what users do. The Google Analytics team released an Analytics SDKs for mobile platforms including Android, iOS (Apple), and mobile websites. In this tutorial, learn how to include and enable this technology within your Android projects to gather important information about how users are using your applications.
In this three part tutorial, we’ll be diving deep into creating a to do list management app in Node.js and Geddy. This is the last entry in the series, where we’ll be persisting our todo items to MongoDB.
As a quick refresher, last time, we created our todo resource and made a working to do list application, but the data only existed in memory. In this tutorial, we’ll fix that!
MongoDB is a NoSQL document store database created by the folks over at 10gen. It’s a great database for Node apps because it stores its data in a JSON-like format already, and its queries are written in JavaScript. We’ll be using it for our app, so let’s get it set up.
Installing MongoDBGo to [www.mongodb.org] and download the latest version for your OS. Follow the instructions in the readme from there. Make sure that you can start mongod (and go ahead and leave it running for the duration of this tutorial)
It’s worth noting that you’ll need to have mongo running any time you want your app running. Most people set this up to start up with their server using an upstart script or something like it.
Done? alright, let’s move on.
MongoDB-WrapperFor our app, we’ll be using a module that wraps the mongodb-native database driver. This greatly simplifies the code that we’ll be producing, so let’s get it installed. cd into your app and run this command:
npm install mongodb-wrapper
If all goes well you should have a mongodb-wrapper directory in your node_modules directory now.
Mongo is a really easy DB to work with; you don’t have to worry about setting up tables, columns, or databases. Simply by connecting to a database, you create one! And just by adding to a collection, you make one. So let’s set this up for our app.
Editing your init.js fileWe’re going to need access to our DB app-wide, so let’s setup our code in config/init.js. Open it up; it should look like this:
// Add uncaught-exception handler in prod-like environments
if (geddy.config.environment != 'development') {
process.addListener('uncaughtException', function (err) {
geddy.log.error(JSON.stringify(err));
});
}
geddy.todos = [];
geddy.model.adapter = {};
geddy.model.adapter.Todo = require(process.cwd() + '/lib/model_adapters/todo').Todo;
Let’s add in our db code at the very top (and remove the geddy.todos array while we’re at it):
var mongo = require('mongodb-wrapper');
geddy.db = mongo.db('localhost', 27017, 'todo');
geddy.db.collection('todos');
// Add uncaught-exception handler in prod-like environments
if (geddy.config.environment != 'development') {
process.addListener('uncaughtException', function (err) {
geddy.log.error(JSON.stringify(err));
});
}
geddy.model.adapter = {};
geddy.model.adapter.Todo = require(process.cwd() + '/lib/model_adapters/todo').Todo;
First, we require the mongodb-wrapper module. Then, we set up our database, and add a collection to it. Hardly any set up at all.
Geddy doesn’t really care what data backend you use, as long as you’ve got a model-adapter written for it. This means that the only code that you’ll have to change in your app to get your todos into a database is in the model-adapter. That said, this will be a complete rewrite of the adapter, so if you want to keep your old in-memory app around, you’ll want to copy the code to another directory.
Open up your model-adapter (lib/model_adapters/todo.js) and find the save method. It should look something like this:
this.save = function (todo, opts, callback) {
if (typeof callback != 'function') {
callback = function(){};
}
var todoErrors = null;
for (var i in geddy.todos) {
// if it's already there, save it
if (geddy.todos[i].id == todo.id) {
geddy.todos[i] = todo;
todoErrors = geddy.model.Todo.create(todo).errors;
return callback(todoErrors, todo);
}
}
todo.saved = true;
geddy.todos.push(todo);
return callback(null, todo);
}
Make it look like this:
this.save = function (todo, opts, callback) {
// sometimes we won't need to pass a callback
if (typeof callback != 'function') {
callback = function(){};
}
// Mongo doesn't like it when you send functions to it
// so let's make sure we're only using the properties
cleanTodo = {
id: todo.id
, saved: todo.saved
, title: todo.title
, status: todo.status
};
// Double check to see if this thing is valid
todo = geddy.model.Todo.create(cleanTodo);
if (!todo.isValid()) {
return callback(todo.errors, null);
}
// Check to see if we have this to do item already
geddy.db.todos.findOne({id: todo.id}, function(err, doc){
if (err) {
return callback(err, null);
}
// if we already have the to do item, update it with the new values
if (doc) {
geddy.db.todos.update({id: todo.id}, cleanTodo, function(err, docs){
return callback(todo.errors, todo);
});
}
// if we don't already have the to do item, save a new one
else {
todo.saved = true;
geddy.db.todos.save(todo, function(err, docs){
return callback(err, docs);
});
}
});
}
Don’t be too daunted by this one; we started with the most complex one first. Remember that our save method has to account for both new todos and updating old todos. So let’s walk through this code step by step.
We use the same callback code as we did before – if we don’t have a callback passed to us, just use an empty function.
Then we sanitize our todo item. We have to do this because our todo object has JavaScript methods on it (like save), and Mongo doesn’t like it when you pass it objects with methods on them. So we just create a new object with just the properties that we care about on it.
Then, we check to see if the todo is valid. If it’s not, we call the callback with the validation errors. If it is, we continue on.
In case we already have this todo item in the db, we check the db to see if a todo exists. This is where we start to use the mongodb-wrapper module. It gives us a clean API to work with our db. Here we’re using the db.todos.findOne() method to find a single document that statisfies our query. Our query is a simple js object – we’re looking for a document whose id is the same as our todos id. If we find one and there isn’t an error, we use the db.todos.update() method to update the document with the new data. If we don’t find one, we use the db.todos.save() method to save a new document with the todo item’s data.
In all cases, we call a callback when we’re done, with any errors that we got and the docs that the db returned to us being passed to it.
Editing the all methodTake a look at the all method, it should look like this:
this.all = function (callback) {
callback(null, geddy.todos);
}
Let’s make it look like this:
this.all = function (callback) {
var todos = [];
geddy.db.todos.find().sort({status: -1, title: 1}).toArray(function(err, docs){
// if there's an error, return early
if (err) {
return callback(err, null);
}
// iterate through the docs and create models out of them
for (var i in docs) {
todos.push( geddy.model.Todo.create(docs[i]) )
}
return callback(null, todos);
});
}
Much simpler than the save method, don’t you think? We use the db.todos.find() method to get all the items in the todos collection. We’re using monogdb-wrapper’s api to sort the results by status (in decending alphabetical order) and by title (in ascending alphabetical order). Then we send that to an array, which triggers the query to start. Once we get our data back, we check to see if there are any errors, if there are, we call the callback with the error. If there aren’t any errors we continue on.
Then, we loop through all the docs (the documents that mongo gave back to us), create new todo model instances for each of them, and push them to a todos array. When we’re done there, we call the callback, passing in the todos.
Take a look at the ‘load’ method, it should look something like this:
this.load = function (id, callback) {
for (var i in geddy.todos) {
if (geddy.todos[i].id == id) {
return callback(null, geddy.todos[i]);
}
}
callback({message: "To Do not found"}, null);
};
Let’s make it look like this:
this.load = function (id, callback) {
var todo;
// find a todo in the db
geddy.db.todos.findOne({id: id}, function(err, doc){
// if there's an error, return early
if (err) {
return callback(err, null);
}
// if there's a doc, create a model out of it
if (doc) {
todo = geddy.model.Todo.create(doc);
}
return callback(null, todo);
});
};
This one is even simpler. We use the db.todos.findOne() method again. This time, that’s all we have to use though. If we have an error, we call the callback with it, if not, we continue on (seeing a pattern here yet?). If we have a doc, we create a new instance of the todo model and call the callback with it. That’s it for that one.
Take a look at the remove method now, it should look like this:
this.remove = function(id, callback) {
if (typeof callback != 'function') {
callback = function(){};
}
for (var i in geddy.todos) {
if (geddy.todos[i].id == id) {
geddy.todos.splice(i, 1);
return callback(null);
}
}
return callback({message: "To Do not found"});
};
Let’s make it look like this:
this.remove = function(id, callback) {
if (typeof callback != 'function') {
callback = function(){};
}
geddy.db.todos.remove({id: id}, function(err, res){
callback(err);
});
}
The remove method is even shorter than it used to be. We use the db.todos.remove() method to remove any documents with the passed in id and call the callback with an error (if any).
Let’s go test our app: cd into your project’s directory and start up the server with geddy. Create a new todo. Try editing it, have it fail some validations, and try removing it. It all works!
I hope you’ve enjoyed learning about Node.js, MongoDB and especially Geddy. I’m sure by now you’ve got a million ideas for what you could build with it, and I’d love to hear about them. As always, if you have any questions, leave a comment here or open up an issue on github.