Tải bản đầy đủ - 0 (trang)
Chapter 9. Separate Configuration Data from Code

Chapter 9. Separate Configuration Data from Code

Tải bản đầy đủ - 0trang

development progresses, due to architectural decisions. The third is the CSS class name

“selected.” This class name is used three times, meaning that a class name change

requires changes in three different places, increasing the likelihood that one will be

missed.

These are all considered configuration data, because they are hardcoded values that

may change in the future. The following are all examples of configuration data:













URLs

Strings that are displayed in the UI

Repeated unique values

Settings (i.e., items per page)

Any value that may change



The key point to remember about configuration data is that it changes, and you don’t

want to be modifying your JavaScript source code because someone changed his mind

about a message to display on the home page.



Externalizing Configuration Data

The first step in separating configuration data from code is to externalize the configuration data, which means getting the data out of the middle of your JavaScript code.

Here’s the previous example with the configuration data externalized:

// Configuration data externalized

var config = {

MSG_INVALID_VALUE: "Invalid value",

URL_INVALID:

"/errors/invalid.php",

CSS_SELECTED:

"selected"

};

function validate(value) {

if (!value) {

alert(config.MSG_INVALID_VALUE);

location.href = config.URL_INVALID;

}

}

function toggleSelected(element) {

if (hasClass(element, config.CSS_SELECTED)) {

removeClass(element, config.CSS_SELECTED);

} else {

addClass(element, config.CSS_SELECTED);

}

}



This example stores all of the configuration data in the config object. Each property of

config holds a single piece of data, and each property name has a prefix indicating the

type of data (MSG for a UI message, URL for a URL, and CSS for a class name). The naming



92 | Chapter 9: Separate Configuration Data from Code



www.it-ebooks.info



convention is, of course, a matter of preference. The important part of this code is that

all of the configuration data has been removed from the functions and replaced with

placeholders from the config object.

Externalizing the configuration data means that anyone can go in and make a change

without introducing an error in the application logic. It also means that the entire

config object can be moved into its own file, so edits are made far away from the code

that uses the data.



Storing Configuration Data

Configuration data is best stored in a separate file to create a clean separation between

it and application logic. A good starting point is to have a separate JavaScript file for

configuration data. Once the configuration data is in a separate file, it opens up more

possibilities for managing that data. A worthwhile option is moving your configuration

data into a non-JavaScript file.

Even though you’re writing a JavaScript application, JavaScript isn’t a great way to store

configuration data. That’s because the syntax is still that of a programming language,

so you need to be sure you haven't introduced syntax errors. If you end up concatenating

JavaScript files together, a syntax error in a single line breaks the overall application.

Configuration data truly belong in files that are hard to format incorrectly, and once

you have that file, it is trivial to convert the configuration data into a JavaScript format

automatically.

One of my favorite formats for configuration data is a Java properties file. Java properties files are simple name-value pairs in which each pair takes a single line (unless you

put in a multiple-line sequence) in the form name=value. It doesn’t matter if there are

spaces around the equals sign, so even that syntax isn’t hard to get right. Comments

are indicated by preceding the line with a # character. Here’s an example:

# UI Strings

MSG_INVALID_VALUE = Invalid value

# URLs

URL_INVALID = /errors/invalid.php

# CSS Classes

CSS_SELECTED = selected



This properties file contains the same properties as the config object from the previous

example. Notice how much simpler the file layout is. There are no quoted strings, which

means that you don’t have to worry about proper escaping or forgetting to close a string.

There are also no semicolons or commas to worry about. You can simply put in your

data and not worry about JavaScript syntax at all.

The next step is to convert this file into something that’s usable by JavaScript. There

are generally three formats in which you want your configuration data. The first is



Storing Configuration Data | 93



www.it-ebooks.info



JSON, which is useful when embedding your data into another file or setting up data

for retrieval from the server. For instance:

{"MSG_INVALID_VALUE":"Invalid value","URL_INVALID":"/errors/invalid.php",

"CSS_SELECTED":"selected"}



The second is JSONP (JSON with padding), which returns the JSON structure wrapped

in a function:

myfunc({"MSG_INVALID_VALUE":"Invalid value","URL_INVALID":"/errors/invalid.php",

"CSS_SELECTED":"selected"});



Because JSONP is valid JavaScript, you can concatenate this code into other files to

give them access to the data.

The last option is plain JavaScript, in which you assign the JSON object to a variable

to use later, as in:

var config={"MSG_INVALID_VALUE":"Invalid value","URL_INVALID":"/errors/invalid.php",

"CSS_SELECTED":"selected"};



As with JSONP, the plain JavaScript version can be combined with other JavaScript

files easily once produced.

For these common use cases, I have created a tool called Props2Js that reads Java properties files and outputs the data into one of these three formats. Props2Js is free and

open source, available at https://github.com/nzakas/props2js/. It works like this:

java -jar props2js-0.1.0.jar --to jsonp --name myfunc

--output result.js source.properties



The --to option specifies the output format, either “js,” “json,” or “jsonp.” The -name option specifies either the variable name (for “js”) or the function name (for

“jsonp”); this option is ignored for “json.” The --output option specifies the file to write

the data into. So this line takes the Java properties file named source.properties and

outputs JSONP with a callback function of myfunc to a file named result.js.

Using a tool like Props2Js allows you to keep configuration data in a simpler file format

and then easily convert your configuration data into a format that is usable by JavaScript

later.



94 | Chapter 9: Separate Configuration Data from Code



www.it-ebooks.info



CHAPTER 10



Throw Your Own Errors



When I was younger, the most befuddling part of programming languages was the

ability to create errors. My first reaction to the throw operator in Java was, “Well, that’s

stupid; why would you ever want to cause an error?” Errors were my enemy—something I sought to avoid—so the ability to cause an error seemed like a useless and

dangerous aspect of the language. I thought it was dumb to include the same operator

in JavaScript, a language that people just didn’t understand in the first place. Now,

with a great deal of experience under my belt, I’m a big fan of throwing my own errors.

Throwing errors in JavaScript is an art. It takes time to feel out where the appropriate

parts of your code should throw errors. Once you figure this out, however, you’ll find

that your debugging time will decrease and your satisfaction with the code will increase.



The Nature of Errors

An error occurs in programming when something unexpected happens. Maybe the

incorrect value was passed into a function or a mathematical operation had an invalid

operand. Programming languages define a base set of rules that when deviated from,

result in errors so that the developer can fix the code. Debugging would be nearly

impossible if errors weren’t thrown and reported back to you. If everything failed silently, it would take you a long time to notice that there was an issue in the first place,

let alone isolate and fix it. Errors are the friends of developers, not enemies.

The problem with errors is that they tend to pop up in unexpected places and at unexpected times. To make matters worse, the default error messages are usually too terse

to really explain what went wrong. JavaScript error messages are notoriously uninformative and cryptic (especially in old versions of Internet Explorer), which only compounds the problem. Imagine if an error popped up with a message that said, “This

function failed because this happened.” Instantly, your debugging task becomes easier.

This ease is the advantage of throwing your own errors.

It helps to think of errors as built-in failure cases. It’s always easier to plan for a failure

at a particular point in code than it is to anticipate failure everywhere. This is a very

95



www.it-ebooks.info



common practice in product design, not just in code. Cars are built with crumple zones,

areas of the frame that are designed to collapse in a predictable way when impacted.

Knowing how the frame will react in a crash—specifically, which parts will fail—allows

the manufacturers to ensure passenger safety. Your code can be constructed in the same

way.



Throwing Errors in JavaScript

Throwing errors in your JavaScript is arguably more valuable than in any other language

due to the complexities involved in web debugging. You can throw an error by using

the throw operator and providing an object to throw. Any type of object can be thrown;

however, an Error object is the most typical to use:

throw new Error("Something bad happened.")



The built-in Error type is available in all JavaScript implementations, and the constructor takes a single argument, which is the error message. When you throw an error

in this way, and the error isn’t caught via a try-catch statement, the browser displays

the value of message in the browser’s typical way. Most browsers now have a console

to which error information is output whenever an error occurs. In other words, any

error you throw is treated the same way as an error that you didn’t throw.

Inexperienced developers sometimes throw errors by just providing a string, such as:

// Bad

throw "message";



Doing so will cause an error to be thrown, but not all browsers respond the way you’d

expect. Firefox, Opera, and Chrome each display an “uncaught exception” message

and then include the message string. Safari and Internet Explorer simply throw an

“uncaught exception” error and don’t provide the message string at all, which isn’t very

useful for debugging purposes.

Of course, you can throw any type of data that you’d like. There are no rules prohibiting

specific data types:

throw

throw

throw

throw



{ name: "Nicholas" };

true;

12345;

new Date();



The only thing to remember is that throwing any value will result in an error if it’s not

caught via a try-catch statement. Firefox, Opera, and Chrome all call String() on the

value that was thrown to display something logical as the error message; Safari and

Internet Explorer do not. The only surefire way to have all browsers display your custom

error message is to use an Error object.



96 | Chapter 10: Throw Your Own Errors



www.it-ebooks.info



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Chapter 9. Separate Configuration Data from Code

Tải bản đầy đủ ngay(0 tr)

×