1: void testUseCategory() {
2: String testString = "break.this.string.up.into.words"
3: println "Test StringUtils.split"
4: StringUtils.split(testString, ".").each {
5: println it
6: }
7:
8: println "Test use(StringUtils)"
9: use(StringUtils) {
10: testString.split(".").each {
11: println it
12: }
13: }
14: }
15:
...
xml.contacts do
xml.contact(:type => "work") do
xml.name("Joe Smith")
...
dynamic xmlBuilder = new XmlBuilder();
var result = xmlBuilder.Contacts(
xmlBuilder.Contact("type", "work",
xmlBuilder.Name("Joe Smith"),
xmlBuilder.Phone("type", "work", "(123) 456-7890"),
xmlBuilder.Address("type", "home",
xmlBuilder.Street("123 Main St."),
xmlBuilder.City("SpringField"))));
Console.WriteLine("XML: \n" + result.ToString());
class XmlBuilder : DynamicObject
{
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
//parse arguments
//Create XML Document
//Set Result
//Return true (handled method)
}
}
private dynamic ParseArguments(object[] arguments)
{
var documents = new List();
var strings = new List();
var attributes = new List();
string text = null;
foreach (var arg in arguments)
{
XDocument xDocArgument;
string stringArgument;
if (CastAs(arg, out xDocArgument))
documents.Add(xDocArgument);
else if (CastAs(arg, out stringArgument))
strings.Add(stringArgument);
}
if (strings.Count % 2 == 1)
{
text = strings.Last();
strings.RemoveAt(strings.Count - 1);
}
for (int i = 0; i <>return new { Documents = documents, Attributes = attributes, Text = text }; } private bool CastAs (object value, out T castedValue) { if (value is T) { castedValue = (T)value; return true; } castedValue = default(T); return false; }
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
var arguments = ParseArguments(args);
XElement rootElement = new XElement(binder.Name, arguments.Text, arguments.Attributes);
foreach (XDocument xDocument in arguments.Documents)
rootElement.Add(xDocument.FirstNode);
result = new XDocument(rootElement);
return true;
}
private static string[] primaryColors = new string[] { "Blue", "Red" };
private static string[] secondaryColors = new string[] { "Black", "Silver" };
static void Main(string[] args)
{
foreach (var result in GetColorList())
{
Console.WriteLine("Color Selection {0} and {1}", result.PrimaryColor, result.SecondaryColor);
}
}
public static IEnumerable GetColorList()
{
return from primaryColor in primaryColors
from secondaryColor in secondaryColors
select new { PrimaryColor = primaryColor, SecondaryColor = secondaryColor };
}
Dynamic languages like Groovy and Ruby are all about productivity. Not only do they eliminate the need to write tons of boilerplate code, they also contain all sorts of “syntactic sugar” – language features that let you accomplish a tremendous amount of work with very little code.
Most of these features – closures, ultra-convenient collection-oriented operators like the spread-dot (*.), etc., are well-documented. Some, however, are a bit more arcane, and not realizing they exist can actually cost you a lot of time if you stumble into them inadvertently.
The feature I have in mind at the moment is a very convenient but somewhat surprising bit of collection behavior in Groovy. I’ve been working with the language for quite a while, and have dutifully pored over books and language reference documents to become familiar with all of the groovy goodness at my disposal, but somehow I missed this one until someone on my project team found it – quite by accident.
Here’s the magic find. In Groovy, if you have a list of objects, you can access, on the list itself, properties that belong to objects within the list. The result, somewhat intuitively (but somehow quizzical at the same time), is a list of values of that property across all the members in the list. Here’s an example to illustrate what I mean – just copy this and run it as a Groovy script.
class Player {
def name
def teams
public String toString() {
"${name} - played for ${teams}"
}
}
def players =
[
new Player(name: "Greg Maddux", teams: ["Cubs", "Braves"]),
new Player(name: "Ron Santo", teams: ["Cubs", "White Sox"]),
new Player(name: "Billy Williams", teams: ["Cubs", "Athletics"]),
new Player(name: "Bruce Sutter", teams: ["Cubs", "Cardinals"]),
new Player(name: "Lou Brock", teams: ["Cubs", "Cardinals"]),
new Player(name: "Gary Gaetti", teams: ["Twins", "Cubs"]),
new Player(name: "Ron Cey", teams: ["Dodgers", "Cubs"]),
new Player(name: "Ryne Sandberg", teams: ["Cubs"]),
new Player(name: "Mark Grace", teams: ["Cubs", "Diamondbacks"]),
new Player(name: "Goose Gossage", teams: ["Athletics", "Yankees", "Cubs"]),
new Player(name: "Sammy Sosa", teams: ["Rangers", "White Sox", "Cubs"]),
new Player(name: "Larry Bowa", teams: ["Phillies", "Cubs"]),
new Player(name: "Rick Monday", teams: ["Dodgers", "Cubs"]),
new Player(name: "Dave Kingman", teams: ["Rangers", "Mets", "Cubs"]),
new Player(name: "Joe Girardi", teams: ["Cubs", "Cardinals", "Yankees"])
]
def playerNames = players.name
println "Here, amazingly enough, is a list containing the names of all those players:\n $(playerNames)"
Once you know this behavior exists, it’s pretty easy to find situations where it’s an insanely convenient feature. If you don’t know it exists, you can get some pretty confusing results. In our case, we were running a GORM query to retrieve a domain object using an alternate ID (expecting a single result), and incorrectly used the list() method (we should have used get(), which returns the domain object, whereas list() always returns a list).
In our code, after querying for the domain object, we accessed properties of the object, and were puzzled when instead of getting Strings and Integers and Dates, we were getting single-member lists of Strings and Integers and Dates. Each list contained the value we expected, inexplicably embedded in a List instance. It took a little head-scratching before we figured out that the root cause was that our query was returning a list containing our domain object, and our code that accessed the properties, which would have failed miserably in Java if attempting to treat a List as though it were a domain object, was happily drilling through the wall of that list and getting at its contents.
Syntactic sugar? Definitely. Violation of the principle of least surprise? Maybe. Will I use it, now that I know it’s a tool at my disposal? Absolutely.
Stay Groovy, my friends…
def addDebugSaveToDomainObjects() {
grailsApplication.domainClasses.each {
it.metaClass.debugSave = {
def obj = delegate.save()
if( obj == null ) {
println "Error saving " + delegate.getClass().name
delegate.errors.allErrors.each {
println it
}
}
return obj
}
}
}
Test-driven development and the resulting test coverage it affords are some of the most important benefits of modern development practices. When it comes time to refactor code or make a significant functional change, there’s nothing like the peace of mind afforded by a comprehensive set of well-written unit tests all emitting that wonderful emerald green.
But such peace of mind comes at a cost. I’ve heard estimates from various teams I’ve talked to stating that the cost of writing unit tests for adequate code coverage (usually 50% to 80% on Java projects) can range from equivalent to the production code itself, to five times that. The problem with these estimates, of course, is subjectivity. For example, if you have found a bug using a test and work to fix the test, are you mentally counting that time as test-writing or code-writing?
Estimating factors aside, the plain fact is that testing does amplify the cost of software development – at least in the short term. Which means that a software engineer who really cares about his customer’s bottom line should always be on the lookout for ways to avoid the increased cost of testing.
This line of thinking arose the other day on the project I’m currently working on. It’s a Groovy/Grails web application with some public, customer-facing components and some internal-use components. We’re using Grails scaffolding as much as is practical for the relatively infrequently-used internal-use features to save time and money. In most cases, we have generated and tweaked the UI components (GSP pages) to smooth over some of the uglier bits of scaffolded pages, but we have tried not to generate any of the controller code, instead relying on Grails’ generate-at-runtime approach by including def scaffold = true in these controllers.
Why not generate the source? Because generated code is deceptively expensive! The minute a feature springs into existence in your code base, it needs to be tested – even if that code was generated by a framework like Grails. And Grails controller closures – insert and update, as the case in point here, do a significant amount work for you. Data binding. Optimistic locking exception handling. Routing of validation errors. All things that “just work” in scaffolded code, but which might be broken by someone once the code actually becomes part of your code base.
So when the need arose to customize part of the logic in insert and update closures (to do some custom interpretation of a series of check boxes on a form that couldn’t be made to conform to the default binding mechanism), we began looking for ways to override binding logic without generating and modifying the closures themselves.
As usual, Grails has a solution at the ready – controller interceptors. All we had to do was declare an interceptor that fires before insert and update closures, and in that closure, inspect our list of check boxes and set a params value that would then be assigned to our domain object through the normal binding process.
By using an interceptor, we completely avoided the need to generate (and assume custodianship of) the insert and update closures in this controller – which means we didn’t have to spend valuable time writing unit tests to cover these Grails-supplied features that could be broken once the code actually exists.
I guess I can summarize this little victory this way: Having tested code for an application feature is far better than having untested code. Having no code at all is better than either.
Stay Groovy, my friends.
pointcut="execution(* com.redpointtech.flex..*Service..*(..))"
beans = {
xmlns aop:"http://www.springframework.org/schema/aop"
securityAspect(SecurityAspect)
aop {
config("proxy-target-class":true) {
aspect(id:'theSecurityAspectDef', ref:'securityAspect') {
around method: "invoke", pointcut:"execution(* com.redpointtech.flex..*Service..*(..))"
}
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!--
we are defining the AOP beans in the xml file because of this bug which will
not be fixed until 1.2.2 and we are using 1.2.1
http://jira.codehaus.org/browse/GRAILS-5932
aspect are called twice because of the above bug. See
http://n4.nabble.com/Advice-called-2-or-more-times-for-aspect-with-execution-pointcut-Bug-td1588831.html
toward the bottom Graeme says that 1.2.2 will have the fixes, and 1.3 M1 already does have the
fix. For now we have to live with the interceptor called twice.
-->
<bean id="myAroundAspect" class="com.redpointtech.aop.MyAroundAspect" />
<aop:config proxy-target-class="true">
<aop:aspect id="theAspect" ref="myAroundAspect">
<aop:around method="invoke" pointcut="target(com.redpointtech.aop.AOPMarkerInterface)" />
</aop:aspect>
</aop:config>
</beans>
class MyAroundAspect {
public Object invoke(ProceedingJoinPoint joinPoint) throws Throwable {
Object returnVal = null
println "MyAroundAspect called...."
returnVal = joinPoint.proceed()
return returnVal
}
}
class TeslaService {
boolean transactional = true
// secureService is a custom property that is looked for in the BootStrap.groovy file and if
// found in a ServiceClass, then it modifies the invokeMethod to add some code before and
// after the call.
boolean secureService = true
def serviceMethod() {
println 'Tesla Service Method called'
}
}
class BootStrap {
def grailsApplication
def init = { servletContext ->
grailsApplication.serviceClasses.each {
def isSecured = it.getPropertyValue('secureService')
if( isSecured ) {
// example of how to 'intercept' service classes.
it.metaClass.invokeMethod = { name, args ->
println '''SECURE before $name'''
def res = delegate.metaClass.getMetaMethod(name,args).invoke( delegate, args)
println '''SECURE after $name. res=$res'''
res
}
}
}
}
def destroy = {
}
}
class TeslaServiceIntegrationTests extends GrailsUnitTestCase {
def teslaService
protected void setUp() {
super.setUp()
}
protected void tearDown() {
super.tearDown()
}
void testServiceMethod() {
teslaService.serviceMethod()
}
}
The framework knows when it needs to see the content of your view so you don’t have to worry about it… the system is very efficient at this process to minimize the work that has to done and the it even caches the results so drawRect doesn’t have be called multiple times (this is especially good for things like animation). If you feel like you are at a state in your controller where you want call drawRect… you should instead call setNeedsDisplay on the view and the system will take care of calling drawRect when the time is right. In fact… if you do call drawRect… nothing is going to change on the screen so just don’t do it (your app might even crash!). This fits in with one the main themes in iPhone developement… laziness is good. This lazy way of updating the view lets the framework optimize the hard-work that comes along with rendering to the screen.
It has been a little while since my last post… I have been spending my time picking up Ruby on Rails so that I could create a web-service for my upcoming iPhone app… I’ll post about that shortly. But first a quick comment about views.
I have seen a lot of people who are starting out with iPhone dev get confused about how the process or creating and loading views. Take a look at this code from this code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic -- create and push a new view controller
if(childController == nil)
childController = [[BookDetailViewController alloc] initWithNibName:@"BookDetailView" bundle:[NSBundle mainBundle]];
Book *selectedBook = [appDelegate.books objectAtIndex:indexPath.row];
childController .book = selectedBook ;
[self.navigationController pushViewController:childController animated:YES];
}
If you want to do something like set the title of the childController with the title of the Book… do not do it in the viewDidLoad or awakeFromNib methods. These are called only 1 time in this instance because the child controller is only created once. Those methods are typically for setting up the controller’s “general state.” What I mean by “general” is the data that is not going to change from different book to book (like the background color and font and everything). Do not use this method to set up specific state for each book. A much better implementation would be to override the setBook method and do any extra initialization there. That way, everytime you have a new book, you will update the other values:
-(void) setBook(Book*)newBook {
if(book != newBook){
[book release];
book = [newBook retain];
self.title = book.title;
}
}
Hope this Helps!
private function adjustAxes(pLengthDict:Dictionary, vPosDict:Dictionary, vNegDict:Dictionary, clearance:Number):void {
var axis:LinearAxis
var axisLength:Number
var compChartMax:Number;
var key:Object;
var maxNegValue:Number;
var maxPosValue:Number;
var obj:Object
var percentPositive:Number;
var refChartMax:Number;
var referenceAxis:LinearAxis;
var referencePercentPositive:Number;
for (key in vPosDict){
axis = key as LinearAxis;
axisLength = pLengthDict[key];
maxPosValue = vPosDict[key];
maxNegValue = vNegDict[key];
obj = calculateMaxMin(maxPosValue, maxNegValue, axisLength, clearance);
if(maxPosValue > 0){
axis.maximum = obj.max;
}
else {
axis.maximum = 0;
}
if(maxNegValue > 0){
axis.minimum = obj.min;
}
else {
axis.minimum = 0;
}
percentPositive = axis.maximum / (axis.maximum - axis.minimum);
if(isNaN(referencePercentPositive)){
referenceAxis = axis;
referencePercentPositive = percentPositive;
}
else if(Math.abs(percentPositive - 0.5) < Math.abs(referencePercentPositive - 0.5)){
referenceAxis = axis;
referencePercentPositive = percentPositive;
}
}
//If there is more than one axis, Flex doesn't line up the zero
//value of each axis, so the data looks weird. This forces the
//zero values to line up.
for (key in vPosDict){
axis = key as LinearAxis;
axisLength = pLengthDict[key];
if(axis == referenceAxis){
continue;
}
if(axis.baseAtZero){
//we need to match the positive/negative ratio of the
//reference chart so that the zero values will line up
percentPositive = axis.maximum / (axis.maximum - axis.minimum);
if(Math.abs(percentPositive - referencePercentPositive) >= 0.5){
//The difference in axes is great. Make them both 50/50
//positive/negative.
compChartMax = Math.max(axis.maximum, Math.abs(axis.minimum));
obj = calculateMaxMin(compChartMax, compChartMax, axisLength, clearance);
axis.maximum = obj.max;
axis.minimum = obj.min;
refChartMax = Math.max(referenceAxis.maximum, Math.abs(referenceAxis.minimum));
obj = calculateMaxMin(refChartMax, refChartMax, pLengthDict[referenceAxis], clearance);
referenceAxis.maximum = obj.max;
referenceAxis.minimum = obj.min;
referencePercentPositive = 0.5;
}
else if(percentPositive < referencePercentPositive){
axis.maximum = -axis.minimum * referencePercentPositive / (1 - referencePercentPositive);
}
else if(percentPositive > referencePercentPositive){
axis.minimum = axis.maximum - (axis.maximum / referencePercentPositive);
}
}//end if(axis.baseAtZero)
}//end iteration over axes
}
private function calculateMaxMin(maxPosValue:Number, maxNegValue:Number,
axisLength:Number, clearance:Number):Object {
var useableAxisLength:Number = axisLength - clearance * 2;
var unitsPerPixel:Number = Math.ceil((maxPosValue + maxNegValue)/useableAxisLength);
var retObj:Object = new Object();
retObj.max = clearance * unitsPerPixel + maxPosValue;
retObj.min = -1 * (clearance * unitsPerPixel + maxNegValue);
return retObj;
}
public function padAxes():void {
if(theChart.series == null){
return;
}
var clearance:Number;
var i:int;
var items:Array;
var j:int;
var label:Label
var labelContainer:Sprite;
// key for all Dictionary objects is an IAxis object, referencing a
//particular axis ( primary or secondary )
//holds maximum positive value (NOT pixels) of data associated with a
//particular axis
var vPosDict:Dictionary = new Dictionary();
//holds the absolute value of the minimum negative value (NOT pixels) of
//data associated with a particular axis
var vNegDict:Dictionary = new Dictionary();
//holds the length, in pixels, of a particular axis
var pLengthDict:Dictionary = new Dictionary();
if(theChart is BarChart){
var labelWidth:Number = 0;
for(i = 0; i < theChart.series.length; i++){
if(! theChart.series is BarSeries){
continue;
}
for(j = 0; j < theChart.horizontalAxisRenderers.length; j++){
if(theChart.horizontalAxisRenderers[j].axis == theChart.series[i].horizontalAxis){
pLengthDict[theChart.series[i].horizontalAxis] = theChart.horizontalAxisRenderers[j].length;
break;
}
}
labelContainer = theChart.series[i].labelContainer;
items = theChart.series[i].items;
if(items == null || labelContainer == null || items.length != labelContainer.numChildren){
continue;
}
if(isNaN(vPosDict[theChart.series[i].horizontalAxis])){
vPosDict[theChart.series[i].horizontalAxis] = 0;
}
if(isNaN(vNegDict[theChart.series[i].horizontalAxis])){
vNegDict[theChart.series[i].horizontalAxis] = 0;
}
var bsi:BarSeriesItem;
var tmpLabel:Label = new Label();
for(j = 0; j < items.length; j++){
bsi = items[j];
label = labelContainer.getChildAt(j) as Label;
//we use a textField to get an immediate measure of the width
var textField:TextField = new TextField();
textField.text = label.text;
labelWidth = Math.max(labelWidth, textField.textWidth);
var xValue:Number = new Number(bsi.xValue);
if(xValue < 0){
vNegDict[theChart.series[i].horizontalAxis] = Math.max(vNegDict[theChart.series[i].horizontalAxis], Math.abs(xValue));
}
else {
vPosDict[theChart.series[i].horizontalAxis] = Math.max(vPosDict[theChart.series[i].horizontalAxis], xValue);
}
}//loop through bar series items
}//loop through bar series
clearance = theChart.computedGutters.left + theChart.getStyle("paddingLeft") + labelWidth;
adjustAxes(pLengthDict, vPosDict, vNegDict, clearance);
//Flex draws a new line for zero, but still keeps the old baseline.
//This will remove the old baseline.
theChart.backgroundElements[0].setStyle("verticalShowOrigin", false);
}
else if(theChart is ColumnChart){
var labelHeight:Number = 0;
for(i = 0; i < theChart.series.length; i++){
if(! theChart.series is ColumnSeries){
continue;
}
for(j = 0; j < theChart.verticalAxisRenderers.length; j++){
if(theChart.verticalAxisRenderers[j].axis == theChart.series[i].verticalAxis){
pLengthDict[theChart.series[i].verticalAxis] = theChart.verticalAxisRenderers[j].length;
break;
}
}
labelContainer = theChart.series[i].labelContainer;
items = theChart.series[i].items;
if(items == null || labelContainer == null || items.length != labelContainer.numChildren){
continue;
}
if(isNaN(vPosDict[theChart.series[i].verticalAxis])){
vPosDict[theChart.series[i].verticalAxis] = 0;
}
if(isNaN(vNegDict[theChart.series[i].verticalAxis])){
vNegDict[theChart.series[i].verticalAxis] = 0;
}
var csi:ColumnSeriesItem;
for(j = 0; j < items.length; j++){
csi = items[j];
label = labelContainer.getChildAt(j) as Label;
labelHeight = Math.max(labelHeight, label.height);
var yValue:Number = new Number(csi.yValue);
if(yValue < 0){
vNegDict[theChart.series[i].verticalAxis] = Math.max(vNegDict[theChart.series[i].verticalAxis], Math.abs(yValue));
}
else {
vPosDict[theChart.series[i].verticalAxis] = Math.max(vPosDict[theChart.series[i].verticalAxis], yValue);
}
}//loop through column series items
}//loop through column series
clearance = theChart.computedGutters.top + theChart.getStyle("paddingTop") + labelHeight;
adjustAxes(pLengthDict, vPosDict, vNegDict, clearance);
//Flex draws a new line for zero, but still keeps the old baseline.
//This will remove the old baseline.
theChart.backgroundElements[0].setStyle("horizontalShowOrigin", false);
}//end if ColumnChart
}
I recently purchased an Apple bluetooth keyboard and Magic Mouse for my Mac Mini (which I bought with the intention of using as a home theater media hub, but which gets almost as much use for ‘normal’ computer tasks).
When I first started test driving the Magic Mouse, I happened to be in iTunes, and happened to need to adjust the volume – which is a horizontal slider in the UI. I thought to myself, “This mouse has the ability to do scrolling in any direction – I wonder what happens if I hover over the volume control and swipe right?” Bam. Of course it worked. It’s Apple, and they think of *everything.*
I was truly impressed. And, being an engineer and naturally inquisitive, I took notice that I was impressed – particularly by such a “little thing.” I then realized that I was impressed not despite the fact that this was a little thing, but because it was a little thing. The sort of thing that other user interface and hardware designers might well have missed.
Which brings me to the moral of this story. I work in a consulting firm, and our goal is not merely to meet our customers’ expectations, but to surpass them. In short, to impress them. I think that sometimes we find this hard to do. We need to remind ourselves that sometimes it’s just the little things we do – things that others might have missed – that will make the biggest impression.
I have been working on my BusTracker application and have recently added the ability to view upcoming bus arrival times for a specific stop. The way it works is that a user presses the “Routes” tab and is presented with a list all Bus Routes in the system (notice I used the word “presses” and not “clicked”… there is no mouse on the iPhone!!!). The user can select a route, then select a direction, then select the bus stop they are at… and then they are presented with a list of upcoming bus arrival times for that specific stop. Immediately, I had a design in my head that consisted of an abstract controller and simple controllers for each page. Each page’s controller would inherit from the abstract controller and build off of that basic functionality (they all look the same… they just present different data). This is how I would do it if I were doing something in Spring MVC… so it should be okay here was well!
It turns out that after doing some more reading on Objective C that it doesn’t even have the reserved word “abstract”… here’s what Apple says about it:
Abstract Classes
Some classes are designed only or primarily so that other classes can inherit from them. These abstract classes group methods and instance variables that can be used by a number of different subclasses into a common definition. The abstract class is typically incomplete by itself, but contains useful code that reduces the implementation burden of its subclasses. (Because abstract classes must have subclasses to be useful, they’re sometimes also called abstract superclasses.)
Unlike some other languages, Objective-C does not have syntax to mark classes as abstract, nor does it prevent you from creating an instance of an abstract class.
The NSObject class is the canonical example of an abstract class in Cocoa. You never use instances of the NSObject class in an application—it wouldn’t be good for anything; it would be a generic object with the ability to do nothing in particular.
The NSView class, on the other hand, provides an example of an abstract class instances of which you might occasionally use directly.
Abstract classes often contain code that helps define the structure of an application. When you create subclasses of these classes, instances of your new classes fit effortlessly into the application structure and work automatically with other objects.
The language is much more tailored to delegation and composition rather than inheritance. Instead of having an abstract controller with other controllers inheriting from that… I chose to go with 1 simple concrete controller that calls a delegate when it needs a specific implementation. So, each page will use a different instance of the same controller that delegates to an object that knows how to present info for that page. This approach is also more easily tested because I don’t have to set up all of the data needed to test an entire controller… I only have to setup what the delegate needs to handle its specific task. I’ll post some sample code later on and link it to this post.
if(v.labelIsHorizontal){
if(v.y < (isNaN(v.min) ? base : v.min))
{
v.labelY = v.y - v.labelHeight;
if(v.labelY < this.dataRegion.top)
v.labelY = v.y;
}
else
{
v.labelY = v.y;
if(v.labelY > this.dataRegion.bottom)
v.labelY = v.y - v.labelHeight;
}
v.labelX = v.x - columnSeries.seriesRenderData.renderedHalfWidth +columnSeries.seriesRenderData.renderedXOffset;
}
if(padAxes){callLater(padAxesMaximums);
}
private function padAxesMaximums():void {
if(!_padAxesRequestPending){
return;
}
_padAxesRequestPending = false;
var axisLength:Number;
var axisMax:Number;
var clearance:Number;
var i:int;
var j:int;
var k:int;
var items:Array;
var label:Label;
var labelContainer:Sprite;
var pad:Number;
var tmpNum:Number;
// Map
// key = IAxis object - references a particular axis ( primary or secondary )
// value = maximum axis value ( NOT pixels )
var maxAxisSuggestion:Dictionary = new Dictionary();
if(theChart.series == null){
return;
}
if(theChart is BarChart){
for(i = 0; i < theChart.series.length; i++){
axisLength = 0;
for(k = 0; k < theChart.horizontalAxisRenderers.length; k++){
if(theChart.horizontalAxisRenderers[k].axis == theChart.series[i].horizontalAxis){
axisLength = theChart.horizontalAxisRenderers[k].length;
break;
}
}
if(axisLength == 0){
return;
}
labelContainer = theChart.series[i].labelContainer;
pad = theChart.getStyle("paddingRight");
clearance = theChart.computedGutters.right + pad;
items = theChart.series[i].items;
if(items == null || labelContainer == null || items.length != labelContainer.numChildren){
continue;
}
axisMax = 0;
for(j = 0; j < items.length; j++){
var bsi:BarSeriesItem = items[j];
label = labelContainer.getChildAt(j) as Label;
if((bsi.x + label.textWidth + clearance) > axisLength){
//we have a label stuck inside, yearning to be outside
tmpNum = Math.ceil(theChart.series[i].horizontalAxis.computedMaximum * axisLength / (axisLength - clearance - label.textWidth));
// take the maximum of the current running value, or the newly calcualted value.
axisMax = Math.max(tmpNum, axisMax);
}
// after looping through all of the labels and seeing
// what the max would be - then set the max
if( isNaN( maxAxisSuggestion[theChart.series[i].horizontalAxis] ) ) {
maxAxisSuggestion[theChart.series[i].horizontalAxis] = 0;
}
maxAxisSuggestion[theChart.series[i].horizontalAxis] = Math.max(maxAxisSuggestion[theChart.series[i].horizontalAxis], axisMax);
}//end iteration over BarSeriesItems
}//end iteration over BarSeries
}
else if(theChart is ColumnChart){
for(i = 0; i < theChart.series.length; i++){
labelContainer = theChart.series[i].labelContainer;
pad = theChart.getStyle("paddingTop");
clearance = theChart.computedGutters.top + pad;
items = theChart.series[i].items;
if(items == null || labelContainer == null || items.length != labelContainer.numChildren){
continue;
}
axisMax = 0;
for(j = 0; j < items.length; j++){
var csi:ColumnSeriesItem = items[j];
label = labelContainer.getChildAt(j) as Label;
if(csi.y < (clearance + label.height)){
//we have a label stuck inside, yearning to be outside
tmpNum = Math.ceil(theChart.series[i].verticalAxis.computedMaximum * csi.min / (csi.min - clearance - label.height));
// take the maximum of the current running value, or the newly calcualted value.
axisMax = Math.max(tmpNum, axisMax);
}
}
// after looping through all of the labels and seeing
// what the max would be - then set the max
if( isNaN( maxAxisSuggestion[theChart.series[i].verticalAxis] ) ) {
maxAxisSuggestion[theChart.series[i].verticalAxis] = 0;
}
maxAxisSuggestion[theChart.series[i].verticalAxis] = Math.max(maxAxisSuggestion[theChart.series[i].verticalAxis], axisMax);
}
}
for ( var key:Object in maxAxisSuggestion ) {
var keyAxis:LinearAxis = key as LinearAxis;
if( keyAxis != null ) {
var value:Number = maxAxisSuggestion[key] as Number;
keyAxis.maximum = value;
}
}
}
for each (var value:* in object){
trace(value);
}
for (var key:String in object){
trace(key + ": " + object[key]); // object[key] is value
}
public override function formatForScreen(value:Object):String {
if(value == null){
return "";
}
return value.toString();
}


In a previous post I went over the process of creating a favorites view similar to the Phone app on the iPhone. While the concepts discussed in that post are important if you want to do iPhone development, I found a short-cut for creating the edit button while I was digging through the UIViewController api.
In the last post, when the user clicked on the “Edit” button the “editClicked” method was called:
-(IBAction) editClicked:(id)sender {
[self.myTableView setEditing:YES animated:YES];
UIBarButtonItem *doneButton =
[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self
action:@selector(doneEditingTable)];
navItem.leftBarButtonItem = doneButton;
navItem.rightBarButtonItem = nil;
[doneButton release];
}
As you can see… first we set the editing mode, then created a new “Done” button to replace the the “Edit” button. I can get rid of this code if I set the “Edit” button to the editButtonItem object of my controller. This built-in button item will toggle its title and associated state between Edit and Done when it is pressed. Plus, the default button action invokes the setEditing:animated: method. That takes care of most of the code in the editClicked method. All I need to do to is implement the setEditing:animated:, then show/hide the “+” button based on the editing mode… simple and easy!
Last week I wrote about an error I got when I added a property to a Managed Object. I posted the solution, but was unable to dig into the docs and understand what was really happening.
It turns out that Apple (as usual) has provided a great resource for understanding the data migration process. It goes over simple and advanced migrations… but for my purposes, the simple-way was all the I needed. Core Data can perform automatic “lightweight migration” which means it infers differences between the original and updated managed object models. You don’t have create a new version of the mapping model! This is especially good while you are in development because you don’t have to reload all of your test data each time you change the data model each time.
The only catch is that lightweight migration will only work if your changes meet certain criteria:
Anything that doesn’t fit this criteria will have to be done manually… which is also in the docs!
I got this error last night after going through the Core Data Tutorial on the Apple dev site. I completed the tutorial and everything worked fine… then I added a few more things to the UI and a new field to the Event Managed Object. When I tried to re-build and run, I got that error above (“NSInternalInconsistencyException: This NSPersistentStoreCoordinator has no persistent stores. It cannot perform a save operation”) when trying to save an Event.
After doing some research, it turns out that I need to explicitly tell NSPersistentStoreCoordinator to automatically migrate newer versions of the data objects. I had to set these 2 values and pass them into the NSPersistentStoreCoordinator as options when it is created:
NSDictionary *options =
[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES],
NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES],
NSInferMappingModelAutomaticallyOption, nil];
persistentStoreCoordinator =
[[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:[self managedObjectModel]];
if (![persistentStoreCoordinator
addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil URL:storeUrl options:options error:&error]) {
// Handle error if failure
}
I’ll update this post as I understand more about what is going on…
This will be the first simple tutorial covering some of the new things I am tackling as I build my first iPhone app. Today I want to go over creating a table view that will show a list of favorites that the user has already selected; this will basically mimic the “Favorites” Tab in the Phone app. I am going to keep this one simple, so there will be no adding of Favorites this time… that can be for another time.
Here is what we want to accomplish(with Editing mode on the right):


On to the code… lets start out by looking at the FavoritesViewController.h:
@interface FavoritesViewController : UIViewController <UITableViewDataSource> {
NSMutableArray *favorites;
UITableView *myTableView;
UINavigationItem *navItem;
UIBarButtonItem *editButton;
UIBarButtonItem *addButton;
}
@property(nonatomic,retain) IBOutlet UIBarButtonItem *addButton;
@property(nonatomic,retain) IBOutlet UIBarButtonItem *editButton;
@property(nonatomic,retain) IBOutlet UINavigationItem *navItem;
@property(nonatomic,retain) IBOutlet UITableView *myTableView;
@property(nonatomic,retain) NSMutableArray *favorites;
-(IBAction) addClicked:(id)sender;
-(IBAction) editClicked:(id)sender;
@end
The ‘favorites’ array is the DataSource for the myTableView object. The navItem is the bar at the top of the screen that shows our “Favorites” label and holds the editButton and addButton; I made these IBOutlets so I could set them in Interface Builder. The 2 IBActions will fire when their respective buttons are pressed… lets focus on the editClicked method. When the user presses the Edit button, we want to tell the TableView to go into editing-mode and then update the buttons to show their correct state.
-(IBAction) editClicked:(id)sender {
[self.myTableView setEditing:YES animated:YES];
UIBarButtonItem *doneButton =
[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self
action:@selector(doneEditingTable)];
navItem.leftBarButtonItem = doneButton;
navItem.rightBarButtonItem = nil;
[doneButton release];
}
Pretty simple stuff right!… When the User presses the newly created “Done” button, the doneEditingTable method will fire so we can deal with the clean up. Before that happens though, the user will probably want to actually delete a row. The framework will handle all of the animation that happens when the user presses the little circle to the left of the text. UPDATE: I found an easier implementation for the Edit/Done button… check out my short post.
After the “Delete” button is hit, the commitEditingStyle method is called so you will need to override it:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
UserDataService *dataService = [(BusTrackerAppDelegate*)[UIApplication sharedApplication].delegate userDataService];
FavoriteStop *favorite = [favorites objectAtIndex:indexPath.row];
[dataService deleteFavorite:favorite];
// Update the array and table view.
[favorites removeObjectAtIndex:indexPath.row];
[self.myTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
}
}
As you can see, first I need to delete the FavoriteStop object from my db via my UserDataService. After the service method successfully finishes the deletion, the TableView needs to be updated to reflect the new list of favorites. Instead of reloading all of the data for the list, you can just delete the corresponding row from the table’s data source and TableView. When the user is ready to finish deleting, they hit the “Done” button which fires an action to the doneEditingTable method. The only thing left to do in order to get back to the “Normal” state (like when the view first loaded) is to reset the buttons.
- (void) doneEditingTable {
[self.myTableView setEditing:NO animated:YES];
navItem.leftBarButtonItem = editButton;
navItem.rightBarButtonItem = addButton;
}
I had to keep a reference to the original buttons on top because they were removed earlier, so I made sure to connect them in Interface Builder (see the interface editButton/addButton outlets). After that method finishes, the user is back to the original view… you are done!
Finally, if you want to add sorting to the edit view you only need to overload a few more methods:
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
// if you want to prevent a specific row from moving
if (indexPath.row == 0) {
return NO;
}
return YES;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {
FavoriteStop *favorite = [[self.myTableView objectAtIndex:sourceIndexPath.row]];
// update sort order in db
// ...........
[self.myTableView removeObjectAtIndex:sourceIndexPath.row];
[self.myTableView insertObject:favorite atIndex:destinationIndexPath.row];
}
There are more advanced ways to do this stuff, so check out the great doc on the apple dev site.
Nib files (.xib) are Interface Builder User Interface Resources files used to describe the View portion of a Cocoa application (os x, iphone). It isn’t too difficult to learn how to use Interface Builder (IB), but there are a few important things to know about Nibs before jumping into the mess. At design time in IB you can layout user interface controls, add controller objects, and define connections between those controllers and the UI. When you save your Nib, IB will archive those objects. At runtime, the objects are unarchived with same values as you set within IB.
One simple but important thing to remember is to make sure you break up your UI into multiple Nib files. You CAN put everything you want into 1 Nib file… TableViewController1 -> TableViewController2 -> TableViewController3 -> DetailsController… but even if you CAN do something, it doesn’t mean you SHOULD. If you have a Nib file that contains a lot of objects it is going to cause your application to load ALL of those objects when the Nib is loaded. For instance… in the example I had before, lets say you only wanted to load the TableViewController1 and show its data (when the user selects a row in Table1, you then want to load Table2). If you have everything in 1 Nib file then your app is going to call [TableViewController1 viewDidLoad], [TableViewController2 viewDidLoad], [TableViewController3 viewDidLoad], and [DetailsController viewDidLoad]… all because the framework is loading each object into memory.
If you are developing for the iphone, you probably don’t want to use up that memory when you have no idea whether or not the user will even click through to the DetailsController. If you separate your that big Nib file into multiple files you will be able to preserve the lazy loading functionality of the framework and save yourself headaches in the future.
I have been wanting to dive into developing and deploying a real iPhone application but have had a hard time deciding on what type of app to make. I recently came across an article talking about the CTA developing a web services API that will allow developers the ability to access all GPS data and arrival times for the buses in the system. This seemed like it would be a great candidate to use in an iPhone app because it would consist of getting data from web services, saving user data locally, using the maps api, displaying different views and controls, and parsing xml into objects… plus… it is an app I can actually use since I ride the bus a ton during the winter. In the next few days I’ll be posting my experience as I build this app… I’ll try and throw in some tutorials as I go along.
if(chart && !(ColumnChart(chart).showLabelVertically))
1: package flexUnitTests
2: {
3: import org.flexunit.runners.Suite;
4: [Suite]
5: [RunWith("org.flexunit.runners.Suite")]
6: public class FlexUnit4Suite
7: {
8: public var testCase1:MyTestCase;
9: }
10: }
1: package flexUnitTests
2: {
3: import flexunit.framework.Assert;
4: public class MyTestCase
5: {
6: public function MyTestCase()
7: {
8: }
9: [BeforeClass]
10: public static function runBeforeClass():void {
11: trace("runBeforeClass");
12: }
13: [AfterClass]
14: public static function runAfterClass():void {
15: trace("runAfterClass");
16: }
17: [Before]
18: public function runBeforeEveryTest():void
19: {
20: trace("runBeforeEveryTest");
21: }
22: [After]
23: public function runAfterEveryTest():void
24: {
25: trace("runAfterEveryTest");
26: }
27: [Test]
28: public function testTrue():void
29: {
30: trace("checkMethod");
31: Assert.assertTrue( true );
32: }
33: [Ignore("Not Ready to Run")]
34: [Test]
35: public function testNotReady():void
36: {
37: Assert.assertFalse( true );
38: }
39: }
40: }
1: <?xml version="1.0" encoding="utf-8"?>
2: <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
3: xmlns:flexunitrunning="http://www.adobe.com/2009/flexUnitUIRunner"
4: minWidth="1024" minHeight="768" creationComplete="onCreationComplete()">
5: <mx:Script>
6: <![CDATA[
7: import flexUnitTests.FlexUnit4Suite;
8: import org.flexunit.listeners.UIListener;
9: import org.flexunit.runner.FlexUnitCore;
10: private var flexUnitCore:FlexUnitCore;
11: private function onCreationComplete():void {
12: flexUnitCore = new FlexUnitCore();
13: flexUnitCore.addListener(new UIListener( testRunner));
14: flexUnitCore.run(FlexUnit4Suite);
15: }
16: ]]>
17: </mx:Script>
18: <flexunitrunning:TestRunnerBase id="testRunner" width="100%" height="100%" />
19: </mx:Application>
1: runBeforeClass
2: runBeforeEveryTest
3: checkMethod
4: runAfterEveryTest
5: runAfterClass
package com.redpointtech.domain
{
import mx.collections.ArrayCollection;
import mx.collections.IList;
[Bindable]
[Table( name="PROJECT")]
public class Project
{
// each project can contain a number of child or
// sub projects.
private var _subProjects:IList = new ArrayCollection();
private var _timeEntries:IList = new ArrayCollection();
[Id]
public var id:int;
[Column( name="proj_name")]
public var name:String;
[Column( name="proj_desc")]
public var desc:String;
[Column(name="color")]
public var color:Number;
[ManyToOne(name="parent_id", inverse="true")]
public var parent:Project;
[OneToMany(type="com.redpointtech.domain.Project", fkColumn="parent_id", lazy="false", cascade="save-update", indexed="true")]
public function set subProjects(value:IList):void {
_subProjects = value;
}
public function get subProjects():IList {
return _subProjects;
}
public function addSubProject(value:Project):void {
value.parent = this;
_subProjects.addItem(value);
}
[OneToMany(type="com.redpointtech.domain.TimeEntry", fkColumn="project_id", lazy="true", cascade="all", indexed="true")]
public function set timeEntries(value:IList):void {
_timeEntries = value;
}
public function get timeEntries():IList {
return _timeEntries;
}
public function addTimeEntry(value:TimeEntry):void {
value.project = this;
_timeEntries.addItem(value);
}
public function Project()
{
}
}
}
package com.redpointtech.domain
{
import com.redpointtech.util.Constants;
[Bindable]
[Table( name="TIMEENTRY")]
public class TimeEntry
{
[Id]
public var id:int;
[Column( name="full_year")]
public var fullYear:int;
[Column( name="month")]
public var month:int;
[Column( name="day_of_month")]
public var dayOfMonth:int; // Date.date equivalent
[Column( name="start_hour")]
public var startHour:int;
[Column( name="start_min")]
public var startMin:int;
[Column( name="end_hour")]
public var endHour:int;
[Column( name="end_min")]
public var endMin:int;
[Column( name="notes")]
public var notes:String="";
[Column( name="summary")]
public var summary:String="";
[ManyToOne(name="project_id", inverse="true")]
public var project:Project;
public function TimeEntry()
{
}
public function setDate(date:Date):void {
fullYear = date.fullYear;
month = date.month;
dayOfMonth = date.date;
}
public function setStartTime( hour:int, min:int):void {
startHour = hour;
startMin = min;
}
public function getStartDate():Date {
var startDate:Date = new Date(fullYear, month,dayOfMonth,startHour,startMin);
return startDate;
}
public function setEndTime(hour:int, min:int):void {
endHour = hour;
endMin = min;
}
public function getEndDate():Date {
var endDate:Date = new Date(fullYear,month,dayOfMonth,endHour,endMin);
return endDate;
}
private function _getTimeDiffInMillis():Number {
var startDate:Date = getStartDate();
var endDate:Date = getEndDate();
var diff:Number = endDate.getTime() - startDate.getTime();
return diff;
}
[Transient]
public function get elapsedHours():Number {
var diff:Number = _getTimeDiffInMillis();
var hours:Number = diff / Constants.millisecondsPerHour;
var floorHours:Number = Math.floor(hours);
return floorHours;
}
[Transient]
public function get elapsedMinutes():Number {
var hours:Number = elapsedHours;
var milliHours:Number = hours * Constants.millisecondsPerHour;
var diff:Number = _getTimeDiffInMillis();
var minuteDiff:Number = diff - ( milliHours );
var minutes:Number = minuteDiff / Constants.millisecondsPerMinute;
var roundedMinutes:Number = Math.min(Math.round(minutes),59);
return roundedMinutes;
}
}
}
[Test]
public function testCRUDNoHier():void {
trace("----------------testCRUDNoHier----------------");
var em:EntityManager = EntityManager.instance;
var p:Project = new Project();
p.name="P1";
p.desc = "proj from unit test";
p.color=0xFF001E;
em.save(p);
Assert.assertEquals("P1",p.name);
var p2:Project = new Project();
p2.name="P2";
p2.desc = "proj2 from unit test";
p2.color=0xAAAAAA;
em.save(p2);
Assert.assertEquals("P2",p2.name);
Assert.assertEquals("P1",p.name);
var p1:Project = em.loadItem(Project, p.id) as Project;
Assert.assertEquals("P1",p1.name);
var ps:ArrayCollection = em.findAll(Project);
Assert.assertEquals(2,ps.length);
p1.desc = "New P1 desc";
em.save(p1);
var p3:Project = em.loadItem(Project,p1.id) as Project;
Assert.assertEquals("New P1 desc", p3.desc);
em.remove(p2);
ps = em.findAll(Project);
Assert.assertEquals(1,ps.length);
}
public static function findAllOn(fullYear:Number,month:Number=-1,dayOfMonth:Number=-1):ArrayCollection {
var teCriteria:Criteria = entityManager.createCriteria(TimeEntry);
teCriteria.addEqualsCondition("fullYear", fullYear);
if( month > -1 ) teCriteria.addEqualsCondition("month",month);
if( dayOfMonth > -1 ) teCriteria.addEqualsCondition("dayOfMonth",dayOfMonth);
teCriteria.addSort("startHour");
return entityManager.fetchCriteria(teCriteria);
}
<mx:HBox width="100%" horizontalAlign="center" >
<mx:Legend dataProvider="{theChart}" direction="horizontal" />
</mx:HBox>
override protected function measure():void {
super.measure();
measuredWidth = Math.ceil(measuredWidth);
}












Redpoint is a technology services firm that solves complex business problems using
leading-edge technologies and agile best practices.
As a leader in the mid-west market, Redpoint is well known for its agile adoptions services
(readiness assessments, coaching, project pilots, and presentations & seminars),
ability to develop and deliver complex solutions using rich internet application UIs
(including AJAX, Flex, and Silverlight) and thought leadership in the Java and .NET development areas.
This is an automatic aggregator which pulls feeds and comments from many blogs of Redpoint contributors. The contents of these blog entries do not necessarily reflect Redpoint's position.