Intro to Functions
When you are just writing quick scripts, you can use Ruby all you want and be happy. However, there comes a point where you have to do the same thing in a bunch of places. Functions are a way to organize the logic in your code so that you can re-use it in more than one place. I’ll introduce how to do math at the beginning, but functions aren’t only for numbers as we will show later.
Doing Some Math
As long as you are working with numbers, you will have to remember some symbols. In your math text books you will see symbols that just don’t exist on keyboards and requires different key combination to make them show up. The good news is that the conventions for replacing mathematical symbols in code is pretty standard across languages. You only have to learn them once, which helps.
- + addition
- - subtraction
- * multiplication
- / division
- % modulus
- ^ exponent
- () group expressions
Math expressions are performed in algebraic order. In short, that means that expressions are evaluated in the reverse order from what I listed. Parentheses first, exponents next, then multiplication, division and modulus, finally addition and subtraction. Just to make it clear, look at the following code:
puts 4 + 5 * 6 # 34 puts (4 + 5) * 6 # 54 puts 4 + (5 * 6) # 34
It’s a good habit to use parentheses to make things clearer. There’s a few more symbols that allow you to do bit manipulation, but then I have to explain the math behind it. Let’s focus on this level of math for now. Let’s say we want to do a little trigonometry and calculate the area of a circle. The mathematical formula for the area of a circle is πr2. So how do we get a hold of the value of π? There is a Ruby module called Math that has the value of π and other more advanced functions.
Ok, so how does the expression look like in Ruby?
radius = 5 puts Math::PI * (radius ^ 2) # 15.707963267949
I added the parentheses to make it clearer that the exponent (raising to the power of two) comes first. So what if we wanted to reuse this function anywhere? We would have to create a function to do it. It’s pretty easy, and you will use the same construct in another post when we talk about creating our own methods. Let’s create our function:
def area radius
Math::PI * (radius ^ 2)
end
So what’s going on here? The word def is a Ruby keyword that tells Ruby that you are creating a function. After that, is the name of the function. Finally we have the list of parameters. A parameter is a name we give to a value that you pass to the function. Basically, the function is going to do something with that value—even though it doesn’t know what the value is first. The next line bears some explaining.
Functions can return a value, which is usually their whole point. However, we don’t see any words that say “return this”. It’s probably the most unintuitive thing you’ll run into with Ruby, but the last expression in a function is the value that’s returned. It’s a carryover from Smalltalk, and once you understand that it becomes a little more understandable. If we had one more line that just had the number 2 on it, then the function would always return the number 2—which is wrong for what we want. What some people do to make things a bit clearer is to use the keyword return . That keyword is designed for letting you leave a method early for some cases, but it works just as well. It’s probably not a bad habit as other languages require you to use it. The method would now look like this:
def area radius
return Math::PI * (radius ^ 2)
end
The keyword end is something we saw already when we were doing loops in the last lesson. This keyword is used to end any block, so you will use it a lot.
Not All Functions Are for Math
I introduced functions with math because that’s where the idea came from. But most problems don’t require the use of heavy math. Ruby isn’t designed to be a math engine anyway. Just for fun, let’s create a function that will turn a number into words—Japanese words to be exact. It’s only fitting as Ruby came from Japan after all. Just to save us some work, we’ll limit ourselves to the range from 0 to 99. To do that we need to use an if statement. The if statement let’s us do something if it is true, but skips the code inside if it is not true. We also want to raise an issue so that the calling code knows that they asked something we can’t deliver. The keyword is raise , which is rather convenient. You can “raise” any object, but we will just use a string. The code looks like this:
if not (0..99).include? number
raise "We can only translate numbers between 0 and 99"
end
I’ll include the solution below, and just expound on things in comments. Your job is to expand the method to do up to 999, or to change it to another language. I’m using Japanese partly because it’s easy to do with code. Other languages have more exceptions.
def to_japanese(number)
#
# Protect our method from trying to work on numbers
# it doesn't support
#
if not (0..99).include? number
raise "We can only translate numbers between 0 and 99"
end
#
# Keep it simple, use the variations of four and
# nine that work in the tens column as well as
# the ones column. These are the numbers from
# zero to nine.
#
numbers = ['rei', 'ichi', 'ni', 'san', 'yon', 'go',
'roku', 'nana', 'hachi', 'kyu']
#
# Modulus gives the remainder.
# 12 divided by 10 is 1 with a remainder of 2.
# It's a good way to get just the ones column.
# Then we use regular division to get just the tens column
#
ones = number % 10
tens = number / 10
case tens
# When we are doing 10 - 19
when 1
japanese = (0 == ones) ? 'ju' : 'ju ' + numbers[ones]
# When we are doing 20 - 99
when 2..9
japanese = numbers[tens] + ' ju'
if (ones > 0)
japanese = [japanese, numbers[ones]].join(' ')
end
# Otherwise we are doing 0-9
else
japanese = numbers[ones]
end
return japanese
end
So there are a couple things I need to explain above. First is the case, when, else construct. The case statement tells Ruby that we are going to use the following expression (in this example the expression is a variable) with a bunch of comparisons. It’s a little nicer than doing a bunch of if/else statements. The first match is what gets run. Each case that we are checking is marked with the when statement. To translate it to English, it’s like saying “when tens is 1 do this”, “when tens is in the range 2..9 do that”, “otherwise do this”.
The next thing I have to explain is the (something) ? true : false construct. It’s a shorthand for an if/else statement. Essentially, we are saying that if the ones column is 0, just return ‘ju’ otherwise return ‘ju ’ plus the translation of the ones column. Have fun!
Java Actionpack: How to protect actions
A very common issue dealing with web applications is how to protect a set of URLs so that only people who have the proper permissions can have access. With Java Actionpack, you would accomplish this through the use of filters. Filters can be called before or after actions, and you can be picky on which actions are supposed to be affected by those filters. Java Actionpack uses annotations to mark which methods on a controller are filters. All other public methods are actions. First, let’s look at the annotation we use:
@Filter(position = {Position}, rules = {Rules}, actions = [])
A closer look at the parameters:
positionPosition.BEFORE- The filter is called before the actions.
Position.AFTER- The filter is called after the actions.
rulesRules.ALL- The filter is called for all actions in the controller, no matter what the
actionslist parameter says. Rules.INCLUDE- The filter is called for only the actions listed in the
actionslist parameter. Rules.EXCLUDE- The filter is called for all actions not listed in the
actionslist parameter.
actions- The list of action names (method names in the controller) that this filter may affect depending on the rules. The default value is an empty list, so it can be omitted when you use
Rules.ALL
The first thing we need to worry about is how many of the actions need to be protected. Is the controller for an administration section where we would need to protect everything? Is the controller for general browsing, but we only need to protect just a couple of methods? Or possibly the controller is for the user self-management so we would need to protect everything except for the login and self registration actions. The only difference is in how we set up the filter. Just to keep things simple, let’s assume the controller is for an administration page.
public class AdminController extends Controller {
public static final String ADMIN_ROLE = "ADMINISTRATOR"
@Filter(position = Position.BEFORE, rules = Rules.ALL)
public void ensureUserIsAdmin() {
if ( ! request.isUserInRole(ADMIN_ROLE) ) {
sendMessage(
"You don't have permissions to access the administration pages.");
if ( null == request.getRemoteUser() ) {
request.setAttribute(ActionPack.CONTINUE, getThisUrl());
redirectToAction("security", "login");
} else {
redirectToAction("home", "index");
}
}
}
}
There’s a lot going on here. First, we have to check if the user is in the administration role. Once we know that, we can decide what to do. First, we send a message (this message is identified by a request attribute that Actionpack will forward through redirects). Next we determine whether the user is logged in or not. If the user is logged in we redirect them to the the action for HomeController.index. If the user is not logged in, we redirect them to the login page, with another special forwarding attribute so that the login section knows to come back to the original request. Don’t worry, the credentials will be checked again after a successful login.
So what’s the deal with request.isUserInRole() ? It’s a standard method on the HttpServletRequest object, and even if you don’t use container authentication, you can still use it in your application. Java Actionpack provides an interface that you use so that it knows what to do with your User object. The interface extends the java.security.Principle interface so that your User object is compatible with the request.getRemoteUser() and request.getUserPrinciple() methods. The user interface also extends java.io.Serializable so that it is compatible with session attributes in servlet engines that are compatible with the newest standards. Lastly, it provides one more method: isInRole(String) . The method is used so that the request wrapper can determine if the user object is in a the requested role.
So where do you put this object we created? The RequestWrapper looks in a session attribute identified by ActionPack.USER, which is the fully qualified class name for the User interface. So your login code will need to create an object that implements actionpack’s User interface, and place it in the session attribute ActionPack.USER and everything works as expected.
Filters are Inherited
When you set up a filter on a controller, you can extend the controller and any filters in the base class also apply to the derived class. For example, if you set up some filters in an abstract ApplicationController used as a base class for all the controllers in your application, those filters are now applied to all the controllers in the system—obeying the rules set up in the filter declaration of course. One possible use for this include sending a “Testing” disclaimer message that only applies when the application is set up on a test server. That message would show up on every page, along with any error messages that use the sendMessage system. I’ve also used it to manage a toolbox that is populated depending on what roles a user has, and possibly which controller the user is accessing (i.e. some tools are only accessible some of the time while others are there all the time). Use your imagination with applying them.How to get started with Java ActionPack
I’ve left hints about my Java Actionpack, but there really isn’t much out there about it for the general world. So how about I give you a quick primer about how to use it? First off, you will have to check it out with Subversion. There’s a slight conflict with the web site being pulled from SVN and browsing the repository, so you’ll have to use the SVN client instead of mounting the directory or browsing it online. I’ll have to work with my ISP on that.
svn co http://d-haven.org/svn/open/trunk/actionpack
It’s world readable, but only authorized people can write. For now, that’s just me but I’ve not really solicited any help at this point. Since that’s not the point right now, let’s worry about how to get it installed and running. Once you’ve checked it out, you’ll need to build it. Go to the directory where you checked it out and build it with ANT . Yes we kept it simple and only used ANT.
ant jar
The ant script builds two jars, “java-actionpack.jar” and “java-actionapack-test.jar”. The first file is what you will include in your webapp to make the magic happen. The second jar provides the helpers and mocks to help you test the controllers in your application. I’m assuming at this point you know how to set up a web application, how to include your code, and configure a web.xml file. So I’m only going to talk about what is specific to Java Actionpack. Obviously, java-actionpack.jar goes in your WEB-INF/lib folder. You’ll need to add the following to your web.xml file to use Java Actionpack:
<filter>
<filter-name>ActionPack</filter-name>
<filter-class>org.dhaven.actionpack.Dispatcher</filter-class>
<init-param>
<param-name>routes.file</param-name>
<param-value>WEB-INF/app.routes</param-value>
</init-param>
<init-param>
<param-name>controller.package</param-name>
<param-value>com.mycom.app.controllers</param-value>
</init-param>
<init-param>
<param-name>environment</param-name>
<param-value>development</param-value>
<!-- <param-value>test</param-value> -->
<!-- <param-value>production</param-value> -->
</init-param>
</filter>
<filter-mapping>
<filter-name>ActionPack</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
The only thing that is critical for you to add in is the “controller.package” parameter. This tells Actionpack where to look for your controllers. By default it is simply “controllers”, but that doesn’t really make sense for most people. The default value for “environment” is development, and this really is a way to help you to have logic depending on what environment you are in. The framework will log a message each time a controller is called with an action that doesn’t exist. The message won’t be logged in testing or production. I’ve used it to provide a message about the testing data that our client requires but shouldn’t be there in production. You can access it in your code by looking at the static variable Controller.environment, and the value is from the Environment enum. Finally the “routes.file” parameter allows you to override the default location for the routes file. The default value is what you see in the configuration snippet above.
So what happens if you don’t have a routes file at all? Java Actionpack will create some wiring for you. It will provide the following mappings by default:
/{controller}/{action}/{id}
/{controller}/{action}
That will provide the familiar mapping of what controller you want, which action on that controller, and the option of passing an ID. The way the routes file works is that the curly brackets refer to a request attribute . So why attributes instead of parameters? Mainly so that the application isn’t so easily manipulated by anyone who knows how to type URL parameters. The request attribute controller specifies which controller you are calling. The controller is inside the controller package you set up, and it transforms the name so that it capitalizes the first letter and tacks on Controller to the end. For example if the request is /message/view/34 and you use the configuration above, the class that Actionpack finds is com.mycom.app.controllers.MessageController . The action attribute specifies the method that will be called. With the example URL, we will call MessageController.view() . It’s up to the view() method to read the id attribute and do something with it. Let’s implement this example method:
public class MessageController extends Controller {
public void view() {
String id = (String)request.getAttribute("id");
request.setAttribute("message", Messages.findById(id));
}
}
You might wonder why I chose to place the message object into the request attribute. It’s really up to you where you put it (the RequestWrapper allows you to override request parameters as well), but this works well if you use the Expression Language and JSPs. You would just refer to the object in the parameter marker like this: ${ message.body } . This calls the method getBody() on the message object you passed in to the request attribute. You’ll also notice that the request object is available as a class attribute, as well as the context object and the response object. They follow the same format as you would expect for a JSP class to keep things familiar. I.e. the names are “request”, “context”, and “response” respectively.
So what about the view? Since Java Actionpack is supposed to be as non-intrusive as possible (yet still opinionated), Java Actionpack will look in the WebApp context for the file called by the controller/action name. So for our example URL from above, we would look at “message/view.jsp”. The progression is this: it looks for a JSP file (”.jsp”), then an HTML file (”.html”), and then finally is just passes the url “message/view” to the system to process. If you want your own supercool view technology as a Servlet, then you just have to set it up to react to the URL scheme we just outlined. Java Actionpack is a filter, but it uses the request dispatcher to pass that URL on. We favored the JSP technology partly because more people would be familiar with it, and partly because it finally grew up enough to be nice to use.
So what happens if Actionpack can’t do anything with the URL? Let’s say the request comes in for “index.html” or “site.css”, or even “fantastic/example.pdf”. If the “controller” attribute doesn’t match an existing controller, Java Actionpack gets out of the way, and lets your application deal with it as it normally would. If the controller is matched, but there is no method for the action attribute, Actionpack will process any filters set up in the controller and process the action calling the action_missing(String actionName) callback (which by default will log what happened in DEVELOPMENT mode). Finally it will dispatch to the view as it normally would. It comes in useful when you have to protect a whole bunch of actions that need a user to have a certain role, but some of the pages are static. Why should you have to create a method if there is nothing to do in it?
These are the basics, and there’s more to it. But for now, I just wanted to share how to get it downloaded so you can play around with it. It should feel natural to a Java developer, and natural enough to someone who has experience with Ruby on Rails. I’ll have to do a few more focused tutorials with it.
