|
I will think of something for here later
-
Until recently, I have been a T-Mobile customer since 2001. Over the past 8 years, I have bought a new phone almost every year. This past June, it was time again for a new phone, so my wife and I got two shiny new Blackberry Curves. We decided these phones offered hotspot access at home through your internet connection without using your plan minutes for only $10! We were thrilled. We get the new phones, everything seemed great. We could call anywhere from our home via the hotspot where previously we had no signal. Then the problems started. My wife would run out to run some errands, I would call her and get an error “The phone number you have dialed is not in service”. Happened every single time. So we’ve had the phones for maybe a week, we called T-Mobile. We were on the phone with them for at least an hour before they decided they needed to send it to a higher level engineer. Four or five days go by and we haven’t heard anything. We called again and said we just wanted to return the phones while we are in our return window. The representative assured us on the phone that as long as we are troubleshooting with T-Mobile, we will be able to return the phones if they do not come up with a resolution. That sounded fine… we will give them a chance to fix the issue. Once again, she said she was escalating it to higher support and they would call back within a week. We waited two weeks, heard nothing. We called back again. This time, they were sending someone out in our area to check the towers, they will call back in a week… Months later, after going through this every other week with T-Mobile, we told them since they cannot fix the issue and it has been months, we would like to return our phones. That conversation went like this, almost verbatim. Me: “We have had this issue since we first got these phones and would like to return them.” T-Mobile: “I’m sorry, you are out of your 15 day window to return the phones.” Me: “The representative we first talked to months ago said if we would troubleshoot the issue with you and you cannot come up with a resolution, we could return the phones later.” T-Mobile: “You only have 15 days to return the phone. You should not have listened to us.” Are you serious? This pissed me off so badly I was done with T-Mobile. She transferred me to their “loyalty” department so I could cancel my plan. I explained this entire story to the “loyalty” department person, they couldn’t care less. She told me I would have to pay both contract cancellations ($400). I finally got her to let us only pay one cancellation ($200). Now I was off to transfer my number to a new service which would cancel my service with T-Mobile. I decided to get a new number, so called back in to cancel. Guess what! They cannot offer me the $200 cancellation, I have to pay the full $400! That’s lovely. Hung up on them, called back got someone else, same thing, $400. Hung up on them, called back, got my $200 again. Meanwhile, my wife and I found the problem with the phones right before we closed our accounts. When someone was on the hotspot router at home and someone was on the edge network away from home, you could not call them. I could switch to edge at home (going outside) and call just fine. Turns out the hotspot is garbage. This is the worst customer experience I have ever had in my life. I would discourage anyone from choosing T-Mobile. Jayme
|
-
Good days, bad days, all of us want to relax. We've worked all week and yet still google 'relaxation tips'. I have searched all over for ways to relax ... everything from deep breathing, yoga, you name it. These techniques do help, but to me are not the key to relaxation. How to relax is simple - work your ass off.
Have you ever had a bad day and just want to relax? I hate to burst your bubble, but it's probably not going to happen. There is no magic bullet to relaxation other than feeling good about what you accomplished for the given day or week. If you want to relax, skip the deep breathing exercises. Get out, mow the grass, run some errands, clean your garage, work on a project. Do this whole-heartedly and relaxation will follow.
Skip the google search to relax. Accomplish something. That is how to relax.
Jayme
|
-
Some cool changes have come along for RestLess. Instead of blabbering on, I'll dive right into the changes.
Small naming changes
Based on Dave Donaldson's recommendation, IRESTReadable, RESTProperty, etc have their case changed. They are now IRestReadable, RestProperty, etc. Just do a rename all on REST to Rest if you already have implemented the framework.
No More Default.aspx, pretty urls!
A problem with the first version, was default.aspx was specified in the Url. This is ugly and not very RESTful. There is a new key you can add to web.config appSettings:
<add key="RestLess::GeneratePlaceholderDirs" value="true" />
When this value is set to true, RestLess will generate placeholder folders with an empty default.aspx document. This way you can specify the url as http://mysite.com/api/products/ (notice no default.aspx). Your web application directory will look something like below:

IRestWritable
IRestReadable, in the first version, allowed you to send an xml document down to a user submitting a GET request at the resource url. IRestWritable exposes one method void GetRestPostedObject(object obj). When a POST is submitted to the url/resource, this method will be called. The best thing about GetPostedObject(object obj) is that it will populate the object from querystring for you based on your RestProperty's. If the attribute is specified as IOType.Input or IOType.InputOutput, RestLess will look in the url for you to see if these values were passed by the user, then send you a populated object.
For example (copied the example object from the first version for clarity, new additions in orange)
// decorate the class as a RestResource, the first parameter is the url of the resource appended to
// the root url (defined in web.config). In this case /api/products/
// the second parameter specifies if this resource is discoverable (more on this later)
[RestResource("products", true)]
public class Product : IRestReadable, IRestWritable
{
private string _name;
private string _description;
private double _amount;
// render this property as xmlelement "productname"
// IOType.InputOutput specifies that this property will be rendered
// to the outputted xml, and can also be inputted via querystring
[RestProperty("productname", IOType.InputOutput)]
public string Name
{
get { return _name; }
set { _name = value; }
}
// render this property as xmlelement "productdescription"
// IOType.Output specifies this property is rendered to the user,
// but it is not allowed in querystring for input
[RestProperty("productdescription", IOType.Output)]
public string Description
{
get { return _description; }
set { _description = value; }
}
// render this property as xmlelement "amount" (output only again)
[RestProperty("amount", IOType.InputOutput)
public object GetRESTObject()
{
// this is where you could go access your data access layer or whatever other method you choose.
// this is also where you could request an API key via querystring for security etc
// since productname is specified inputoutput, you should check for
// Request.QueryString["productname"] to get any data the user sends
// for the purposes of this demo, lets just make an object and pass it back
Product p = new Product();
p.Name = "Super Fly Ink Pen";
p.Description = "This is the most super fly ink pen ever";
p.Amount = 10.00;
return p;
}
#endregion
#region IRestWritable Members
public void GetRestPostedObject(object obj)
{
Product product = obj as Product;
// product will be populated here based on anything provided in QueryString, you do not
// need to retrieve it manually. for example, if the POSTed url is
// http://mysite.com/api/products/?productname=InkPen&amount=2.50
product.Name // will equal InkPen
product.Amount // will equal 2.50
}
#endregion
}
Right now, IRestWritable only supports getting values from querystring. That will change later to support xml documents being posted, etc.
Grab the new dll here. (as of now, the plan is to open-source the framework when it's a little further along)
Enjoy!
Jayme
|
-
I have been disappointed with the amount of legwork involved with setting up a simple REST API, so I decided to write a small attribute-based framework. The goal was to make it as simple as possible and be discoverable. Right now, the framework only supports GETting objects from your api, but that will change very soon.
Setting up RestLess
- Add a reference to RestLess.dll
- In web.config under <system.web>, add
<httpModules>
<add type="RestLess.RESTHttpModule, RestLess" name="RestLess" />
</httpModules>
- To tell RestLess where your API root directory is, in web.config under the <configuration> section add this key (you can make this whatever path you want)
<appSettings>
<add key="RestLess::BaseUrl" value="api/" />
</appSettings>
That's it!
Exposing an object via the api
You may have a Product object in your application with the properties Name, Description, and Amount. If you want to expose this via the api, you would simply decorate this class as follows. (RestLess code in blue, Comments in green)
// decorate the class as a RESTResource, the first parameter is the url of the resource appended to
// the root url (defined in web.config). In this case /api/products/
// the second parameter specifies if this resource is discoverable (more on this later)
[RESTResource("products", true)]
public class Product : IRESTReadable
{
private string _name;
private string _description;
private double _amount;
// render this property as xmlelement "productname"
// IOType.InputOutput specifies that this property will be rendered
// to the outputted xml, and can also be inputted via querystring
[RESTProperty("productname", IOType.InputOutput)]
public string Name
{
get { return _name; }
set { _name = value; }
}
// render this property as xmlelement "productdescription"
// IOType.Output specifies this property is rendered to the user,
// but it is not allowed in querystring for input
[RESTProperty("productdescription", IOType.Output)]
public string Description
{
get { return _description; }
set { _description = value; }
}
// render this property as xmlelement "amount" (output only again)
[RESTProperty("amount", IOType.Output)]
public double Amount
{
get { return _amount; }
set { _amount = value; }
}
#region IRESTReadable Members
public object GetRESTObject()
{
// this is where you could go access your data access layer or whatever other method you choose.
// this is also where you could request an API key via querystring for security etc
// since productname is specified inputoutput, you should check for
// Request.QueryString["productname"] to get any data the user sends
// for the purposes of this demo, lets just make an object and pass it back
Product p = new Product();
p.Name = "Super Fly Ink Pen";
p.Description = "This is the most super fly ink pen ever";
p.Amount = 10.00;
return p;
}
#endregion
}
Done - that's all you have to do! You can add these attributes to as many of your classes as you want to expose through your api.
If you request the url http://yoursite.com/api/products/default.aspx, the output will look like below

Discovering The API
As I mentioned earlier, the second parameter of the RESTResource attribute specifies if the resource is discoverable. If this parameter is set to true, you can visit http://yoursite.com/api/discover.aspx to see what the available resources are. The output of http://yoursite.com/api/discover.aspx will look something like below (notice another Customer object - I did not include this class for brevity).

I plan on making lots of updates and changes to the framework as needed (I am VERY open to suggestions!!). This is a rough-rough draft, but seems to work well.
... now go and add an API to your .NET application in a few minutes. :)
Jayme
|
-
The more sites I've tried in the past few years, the more I've noticed who cares about usability and who uses it in their marketing slogans. Some of the biggest players on the web define usability as doing what everyone else does. That is NOT usability.
I will pick on my own application to demonstrate:
Let's say you are creating an invoice for your customer in Meiraware Business. You fill out all of the relevant fields, then it's time to put in the invoice date.

Not bad! Pretty easy to use. Standard calendar picker.... can't get much better. This is what everyone does, so it's usable!
or so I thought....
Now lets looks at Remember The Milk's date picker for their calendar
(that right button is not a calendar control)
At first impression, I was thinking "No calendar? What kind of shit is that?" ... then I realized you can type damn near anything in this box. You could type Next Tuesday, May 15 (assumes this year), Tomorrow, Next Thursday. Take a look at a brief example from their site that you can type in this box:

That is attention to detail that ALL OF US could use more of. They took the concept of "choosing a date" and truly made it 'usable'.
In my opinion, there should be a calendar added to Remember The Milk's control, some people do prefer to choose from a calendar. But besides that, it's brilliant.
Jayme
(p.s. I'm sure there are other companies with methods such as these, this is just the first i've seen)
|
-
Today, I launched a one-click live demo of Meiraware Business (a simple and easy invoicing application). Why a live demo? In my opinion, screencasts and/or screenshots is a distant second to actually using an application. Screencasts - I don't want to be guided through by someone who knows what they're doing. I want to get in there and click where I want to click. If I truly believe my application is easy, why do I need to guide you through it? Screenshots - make the page look nice, but never show what the application is really about.
Here are the steps to check out Meiraware Business:
- Go to http://meiraware.com
- Click try a demo account
That's it. No email address, no personal information. When you are finished, close your browser.
In some situations, it may be difficult or impossible (not web-based) to provide a live demo. However, if you have the option to offer a one-click live demo, do so. People will hang around longer to see what your application is about.
Jayme
|
-
I've been asked by a few people the easiest way to store your data inside Graffiti. Most people start the question with "How do I add tables to the database ....". You could do that, but it would be complicated and annoying to deploy to other users. Simply use the ObjectManager and all your code will move to any Graffiti installation with ease. Here it is, in less than 2 minutes....
add a reference to Graffiti.Core, make an object... mark it serializable
[Serializable]
public class MyObject
{
private string _name;
private string _description;
public string Name
{
get { return _name; }
set { _name = value; }
}
public string Description
{
get { return _description; }
set { _description = value; }
}
}
instantiate and call Save..
MyObject obj = new MyObject();
ObjectManager.Save(obj, "MyObjectSettings");
retrieve it later..
ObjectManager.Get<MyObject>("MyObjectSettings");
That's it!
Jayme
|
-
Nathaniel Talbott had a great presentation this evening at the Agile RTP meeting. His Lightning Talk (4 - 5 minutes), Jim Cramer style, was about spending more energy on your application's user interface. One line from his slides really hit home for me:
The user interface IS THE APPLICATION
I couldn't agree with this more. I have seen so many applications that are unusable and look horrible, but the test coverage is over 90! The architecture is scalable! When someone wants to buy your product, they check out your "screenshots"... not your "codeshots". Having a good architecture is important, but throwing the user interface to the side is foolish.
Jayme
|
-
|
I just finished the first run of a Graffiti plug-in I'm calling SkinnyWeb. This plug-in allows you to compress the source output of your graffiti website to the user using GZIP, DEFLATE, or a custom WhiteSpace module. For now, it only compresses the...
|
-
|
A month or so ago my VM completely hard locked. I was working on Graffiti and had just created a category called 'test'. I had no choice but to force the vm closed and reopen. When I booted back up the system ran chkdsk. It fixed a lot of stuff...
|
-
|
Leo of Zen Habits (one of my favorite blogs) recently posted asking readers for tips on working from home. I left the following comments on his blog: Bad TV. Yep, that’s the secret. If you watch good TV, you will turn around from your desk and watch...
|
-
|
I'm glad to announce that the Graffiti migrator will be included with Graffiti beta 2. Thanks to everyone for all of your help and feedback. Beta 2 should be out sometime this week, worst case early next week. The first round will support CS2007 DB...
|
-
|
My recent post gathered a lot of attention in the comments for the Graffiti migrator utility I've been working on. I've received a lot of great feedback and have a new version in need of testing. What's different 1) This new version supports...
|
-
|
After lots of work, we have finally released the 'private' beta of Graffiti. If you don't know already, Graffiti is a simple CMS (content made simple) that is unlike any other. It is extremely easy to use and installs in minutes (copy/paste...
|
-
|
Oh Javascript... you can't be intuitive can you? I want to see if a function exists and then call it - if(decrementComments) decrementComments(); Wrong. if(typeof decrementComments == 'function') decrementComments(); Right. Graffiti users...
|
More Posts Next page »
|
|
|