Quick Tip: What Exactly is Web Hosting?
19/5/2012 external link
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
Conclusion
If 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!
Ticksy: A Support Story
18/5/2012 external link
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.
Most importantly, it’s doing a great job handling my support requests — the reason I built it in the first place!
Designing Ticksy
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 All
Two 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.
As a designer, it was supremely validating. And it was a reminder to keep working.
More to Come
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 Up
Ticksy 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!
Diving Into CSS Regions
17/5/2012 external link
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 Support
At 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 )
Navigate to chrome://flags/ (or about:flags)
Find “Enable CSS Regions” and enable it.
Restart your browser and verify that the demo is working.
A Simple Example
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
The flow-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.
The 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 @region rule 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 API
At 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 name
A 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 node
The 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 region
We 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://developers.google.com/speed/docs/best-practices/rendering#UseEfficientCSSSelectors
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’
overflow: The content does not fit in the specified region and will overflow to the next region if it’s available
fit: The region has successfully contained the content and no overflow is necessary.
empty: There is no content inside of the selected region and therefore it is empty (as will normally be the case with a region whose previous region returns ‘fit’ for regionOverflow)
undefined: The element is not a valid region.
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.
Browser implementation for CSS Regions isn’t great right now and even when it does improve browser implementation may vary. For example Internet Explorer only allows flow-into to be set on an iframe.
CSS Regions can let developers and designers offer a more improved experience on the web, especially on mobile/tablet devices.
Modernizr can detect support for CSS Regions, it’s what the demo uses.
Adobe have taken a great initiative on CSS Regions. Be sure to check out their demos!
Chris Coyier of CSS Tricks has come up with a cool and practical use for CSS Regions.
Meet Crockford’s JSCheck
16/5/2012 external link
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 Test
Of 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 Claim
Now 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 Claim
Before 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.
Adding a Classifier
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 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 Thoughts
So, 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!
How to Super-Scale Magento in the Cloud
15/5/2012 external link
This tutorial will help you prepare a Magento install for high traffic, better load times, and simpler ongoing site management. Ready?
Requirements
You can download the finished code for this tutorial, or launch the “magento-basic” Quickstart from your Pagoda Box account to test a working site.
A Pagoda Box Account (free)
A functional local Magento Install
Local Development Software (MAMP or WAMP)
Git Installed (Can use SFTP)
The Pagoda Terminal Client Installed
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.
Step 1: Set Up Git Locally (SFTP will work as an alternate)
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 repository
git commit -m "some message about what you've done" – Commits your changes
git 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.
Mac – Ruby and RubyGems come pre-installed on Mac OSX. As long as you are running v10.5 or later, you should be good to go.
Windows – There are a couple of different ways to install Ruby in Windows. We recommend this auto-installer. If it doesn’t work for your set-up, a Google search will give you a pretty good list of installation walk-throughs.
Linux – Use your preferred package manager to download the Ruby package. For Ubuntu users, the gem is available through getdeb.net.
Install and Verify Terminal Client
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.)
Step 3: Install Magento Locally
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.
Shared Writable Directories
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 Extensions
The 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 .gitignore file to ignore the writable directories specified in your Boxfile. Identifying these directories inside the .gitignore file helps reduce the size of your repo, and your deploy time. In addition to the writable directories, you can also add the downloader directory to the .gitignore file, 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 Account
If 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 Box
Once 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 Option
You 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 Repository
git add . – Add all files from the project to the repo
git 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 changes
git 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 dashboard
git 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.
SFTP Option
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 Boxfile
The 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 --all
Alternate: 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 Production
Your 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.
Create a local.xml for Production
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 --all
Step 9: Migrate the Database
With 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.
Export Your Local DB
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 Connection
Now 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 DB
Next, 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 in the Boxfile
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 http://magento.pagodabox.com/cron.php"
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 http://magento.pagodabox.com/cron.php"
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 http://magento.pagodabox.com/cron.php
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!
Lots Of New Premium Content!
14/5/2012 external link
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+ Premium
Nothing above pique your interest? That’s okay; we have plenty of amazing new content on the near horizon, including the following courses:
PHP Fundamentals
What’s New in PHP 5.4?
Cleaner Code With CoffeeScript
Backbone.js Fundamentals
The Sublime Text 2 Guide
Test-Driven Development in Ruby
Already a member? What requests do you have? Haven’t signed up yet? Well now’s the perfect time!
How to Convert a Widget into a Joomla Module
11/5/2012 external link
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.
Template Files
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.
Language Files
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.
Final File Structure
After following each step, you should have the following file structure
language/en-GB.mod_mixcloud_widget.ini
language/en-GB.mod_mixcloud_widget.sys.ini
tmpl/default.php
tmpl/index.html
index.html
mod_mixcloud_widget.php
mod_mixcloud_widget.xml
Step 2: Setting Up Our XML File
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:
type: Tells Joomla what type of extension is being installed, in this case module.
version: Instructs the installer what version of Joomla we are creating the module for.
method: There are two options: 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.
client: Instructs whether the module is a front-end or back-end module.
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 Files
As 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>
Instead 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.
Language Files
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:
type: Type of form field like text, textarea, checkboxes, radio and calendar.
name: The name element of the form field to displayed.
default: Default value of the field.
label: Text displayed at the beginning of our form field.
description: Description that will be shown in a tooltip when hovering on our form field
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>
You 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.
Finished XML File
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:
Copyright Notice and Restricted Access Script
<?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' );
It is recommended that you add the Copyright Notice and Restricted Access line to your PHP files.
Adding our HTML
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="http://www.mixcloud.com/media/swf/player/mixcloudLoader.swf?feed=http%3A%2F%2Fwww.mixcloud.com%2FMaryAnneHobbs%2Fthom-yorke-moneyback-mix-xfm-music-response-150911%2F&embed_uuid=5d6c18a4-a837-409e-8cec-23ab44372842&stylecolor=&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="http://www.mixcloud.com/media/swf/player/mixcloudLoader.swf?feed=http%3A%2F%2Fwww.mixcloud.com%2FMaryAnneHobbs%2Fthom-yorke-moneyback-mix-xfm-music-response-150911%2F&embed_uuid=5d6c18a4-a837-409e-8cec-23ab44372842&stylecolor=&embed_type=widget_standard" 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="http://www.mixcloud.com/media/swf/player/mixcloudLoader.swf?feed=<?php 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="http://www.mixcloud.com/media/swf/player/mixcloudLoader.swf?feed=<?php 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>
Take note of how we are just calling the variables from our controller file.
Step 5: Creating Language Files
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 Module
After 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.
Add index.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.
Packaging
After 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.
Conclusion
Now 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!
Aspect-Oriented Programming in PHP
10/5/2012 external link
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 Weaving
Our 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 Cache
Every 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 Flow3
Alright, 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’re on MAC and FLOW3 complains “index.php not found “, open the .htaccess in the webdir and add a # before the line Rewrite Base /“–
If you’re on Windows and the command line tool does not work, open Settings.yaml.example in FLOW3′s configuration directory, uncomment and adjust the phpBinaryPathAndFilename variable to the correct path (e.g. C:\xampp\php\php.exe) and save the file as Settings.yaml
On some machines, if FLOW3 is very slow, you have to increase the memory limit of PHP in the php.ini
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 http://localhost/flow3/Web! 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:
Classes: The home of your PHP Code, a first Controller is already created in the Classes/Controller Folder: the StandardController!
Resources/Private: The home of all templates that are fired into Fluid, FLOW3′s powerful Templating Engine. It hosts some other stuff like localization files as well.
Resources/Public: This has not been created yet, but every file in the Resources/Public folder will be directly accessible from the web. This is a good place for images, CSS, Javascript etc.
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 Aspect
Take 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!
Conclusion
The 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?
Which Tuts+ Site Should We Launch Next?
9/5/2012 external link
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 Say
Nettuts+ Readers: Which Tuts+ Site Should We Launch Next?
We’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 Membership
Our 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!
Prototypes in JavaScript
9/5/2012 external link
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); // true
The 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.
How to Use Prototype
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.
Updating Native Objects Prototypes
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.
As 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.
Conclusion
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.




