miércoles, 28 de marzo de 2012

Creating a Todo List App With Node.js and Geddy

In this three part tutorial, we’ll be diving deep into creating a to do list management app in Node.js and Geddy. This is the second part in the series, where we’ll be creating a simple to do list management app.


Recap

As a quick refresher, last time we installed Node and Geddy, generated a new app, and learned how to start up the server. In this tutorial we’ll build upon what we did last time, so make sure you’ve completed that one before continuing.


Generating the Todo Resource

Geddy has a built in resource generator; this will allow us to automatically generate a model, controller, views, and routes for a specific resource. Our to do list app will only have one resource: todo. To generate it, just cd into your app’s directory (cd path/to/your/todo_app) and run:

 geddy resource todo 

You should now have these files added to your app:

  • app/models/todo.js
  • app/controllers/todos.js
  • app/views/todos/
    • index.html.ejs
    • show.html.ejs
    • edit.html.ejs
    • add.html.ejs

Your config/router.js should also have this appended to it:

 router.resource('todos'); 

What it all does

If you’re new to MVC this all might seem a little daunting to you. Don’t worry though, it’s really simple once you figure it out.

models/todo.js: This file is where we’ll define our todo model. We’ll define a number of properties that all todo’s have. We’ll also write some data validations here.

controllers/todos.js: This file is where all the /todos/ routes end up. Each action in this controller has a corresponding route:

 GET      /todos/            => index POST     /todos/            => create GET      /todos/:id         => show PUT      /todos/:id         => update DELETE   /todos/:id         => remove GET      /todos/:id/add     => add GET      /todos/:id/edit    => edit 

views/todos/: Each file in here corresponds to one of the GET routes that we showed you above. These are the templates that we use to generate the front end of the app. Geddy uses EJS (embedded JavaScript) as it’s templating language. It should look familiar if you’ve ever used PHP or ERB. Basically, you can use any JavaScript that you’d like in your templates.

Getting a feel for the routes

Now that we’ve generated a bunch of code, let’s verify that we’ve got all the routes that we need. Start the app again (geddy), and point your browser to http://localhost:4000/todos. You should see something like this

Go ahead and try that for the other GET routes too:

  • http://localhost:4000/todos/something
  • http://localhost:4000/todos/add
  • http://localhost:4000/todos/something/edit

All good? Alright, let’s continue.


Creating the Todo Model

In Geddy (and most other MVC frameworks), you use models to define the kind of data that your app will work with. We just generated a model for our todos, so let’s see what that gave us:

 var Todo = function () {   // Some commented out code };  // Some more commented out code  Todo = geddy.model.register('Todo', Todo); 

Models are pretty simple in Geddy. We’re just creating a new constructor function for our todos and registering it as a model in geddy. Let’s define some properties for our todos. Delete all the commented out code and add this to the contructor function:

 var Todo = function () {   this.defineProperties({     title: {type: 'string', required: true}   , id: {type: 'string', required: true}   , status: {type: 'string', required: true}   }); }; 

Our todos will have a title, an id, and a status, and all three will be required. Now let’s set some validations for our todos.

 var Todo = function () {    this.defineProperties({     title: {type: 'string', required: true}   , id: {type: 'string', required: true}   , status: {type: 'string', required: true}   });    this.validatesPresent('title');   this.validatesLength('title', {min: 5});    this.validatesWithFunction('status', function (status) {     return status == 'open' || status == 'done';   });  }; 

We’re validating that the title is present, that the title has a minimum length of 5 characters, and we’re using a function to validate that the status is either open or done. There are quite a few valitation functions that are built in, go ahead and check the project out on http://github.com/mde/geddy to learn more about them.


Creating the Todo Model Adapter

Now that we’ve set up our todo model, we can create somewhere to store our models. For the purposes of this tutorial, we’re just going to keep the data in memory. We’ll hang a todos array off of our global geddy object to stick the data in. In the next part of this series, we’ll start to get these persisted in a database.

Editing Your init.js File

Open up your config/init.js file. All that should be in there now is a global uncaught exception handler:

 // Add uncaught-exception handler in prod-like environments if (geddy.config.environment != 'development') {   process.addListener('uncaughtException', function (err) {     geddy.log.error(JSON.stringify(err));   }); } 

Right after that block of code, let’s hang our array off the geddy global:

 geddy.todos = []; 

There, now we’ve got a place to store our todos. Remember, this is in your application-memory, so it will disappear when you restart the server.

Creating the Model-adapter

A model-adapter provides the basic save, remove, load, and all methods a model needs. Our data source is pretty simple (just an array!), so writing our model adapter should be pretty simple too.

Create a directory in lib called model_adapters and create a file in lib/model_adapters called todo.js. Let’s open up that file and add in some boilerplate code:

 var Todo = new (function () { })(); exports.Todo = Todo; 

All we’re doing here is setting up a new blank object to be exported out to whatever ends up requiring this file. If you’d like to know a bit more about how Node’s require method works, this article has a pretty good overview. In this case, our init.js file will do the requiring.

Require the model adapter in init.js

So we set up a new Todo model-adapter object. It’s pretty barren right now, but we’ll get to that soon. For now, we’ll have to go back to init.js and add some code so that it’s loaded into our app when it starts up. After the geddy.todos = []; in config/init.js add these two lines:

 geddy.model.adapter = {}; geddy.model.adapter.Todo = require(process.cwd() + '/lib/model_adapters/todo').Todo; 

We created a blank model-adapter object, and added the Todo model adapter onto it.


Saving Todos

Now that we have our model and model adapter in place, we can start in on the app logic. Let’s start with adding to do items to our to do list.

Edit the save method on the adapter to save a todo instance

When working with data, the first place you should go is the model adapter. We need to be able to save an instance of our Todo model to our geddy.todos array. So open up lib/model_adapters/todo.js and add in a save method:

 var Todo = new (function () {   this.save = function (todo, opts, callback) {      if (typeof callback != 'function') {       callback = function(){};     }      todo.saved = true;     geddy.todos.push(todo);     return callback(null, todo);    } })(); 

All we have to do is set the instance’s saved property to true and push the item into the geddy.todos array. In Node, it’s best to do all I/O in a non-blocking way, so it’s a good idea to get in the habit of using callbacks to pass data around. For this tutorial it doesn’t matter as much, but later on when we start persisting things, it’ll come in handy. You’ll notice that we made sure that the callback is a function. If we don’t do that and use save without a callback, we’d get an error. Now let’s move on to the controller create action.

Edit the create action to save a todo instance

Go ahead and take a look at the create action in app/controllers/todos.js:

 this.create = function (req, resp, params) {   // Save the resource, then display index page   this.redirect({controller: this.name}); }; 

Pretty simple, right? Geddy has stubbed it out for you. So let’s modify it a little bit:

 this.create = function (req, resp, params) {   var self = this     , todo = geddy.model.Todo.create({         title: params.title       , id: geddy.string.uuid(10)       , status: 'open'       });   todo.save(function (err, data) {     if (err) {       params.errors = err;       self.transfer('add');     }     else {       self.redirect({controller: self.name});     }   }); }; 

First, we create a new instance of the Todo model with geddy.model.Todo.create, passing in the title that our form will post up to us, and setting up the defaults for the id and status.

Then we call the save method that we created on the model adapter and redirect the user back to the /todos route. If it didn’t pass validation, or we get an error, we use the controller’s transfer method to transfer the request back over to the add action.

Edit add.html.ejs

Now it’s time for us to set up the add template. Take a look at app/views/todos/add.html.ejs, it should look like this:

 <div class="hero-unit">   <h3>Params</h3>   <ul>   <% for (var p in params) { %>     <li><%= p + ': ' + params[p]; %></li>   <% } %>   </ul> </div> 

We won’t be needing that

for our use case, so let’s get rid of it for now. Make your add.html.ejs look like this:
 <div class="hero-unit">   <%= partial('_form', {params: params}); %> </div> 

An Intro to Partials

Partials give you an easy way to share code between your templates.

You’ll notice that we’re using a partial in this template. Partials give you an easy way to share code between your templates. Our add and edit templates are both going to use the same form, so let’s create this form partial now. Create a new file in the views/todos/ directory called _form.html.ejs. We use an underscore to easily tell if this template is a partial. Open it up and add in this code:

 <%   var isUpdate = params.action == 'edit'     , formTitle = isUpdate ? 'Update this To Do Item' : 'Create a new To Do Item'     , action = isUpdate ? '/todos/' + todo.id + '?_method=PUT' : '/todos'     , deleteAction = isUpdate ? '/todos/' + todo.id + '?_method=DELETE' : ''     , btnText = isUpdate ? 'Update' : 'Add'     , doneStatus = isUpdate ? 'checked' : ''     , titleValue = isUpdate ? todo.title : ''     , errors = params.errors; %> <form id="todo-form" class="form-horizontal" action="<%= action %>" method="POST">   <fieldset>     <legend><%= formTitle %></legend>     <div class="control-group">       <label for="title" class="control-label">Title</label>       <div class="controls">         <input type="text" class="span6" placeholder="enter title" name="title" value='<%= titleValue %>'/>         <%  if (errors) { %>           <p>           <% for (var p in errors) { %>             <div><%=  errors[p];  %></div>           <% } %>           </p>         <% } %>       </div>     </div>     <% if (isUpdate) { %>       <div class="control-group">         <label for="status">Status</label>         <div class="controls">           <select name="status">             <option>open</option>             <option>done</option>           </select>         </div>       </div>     <% } %>     <div class="form-actions">       <input type="submit" class="btn btn-primary" value="<%= btnText %>"/>       <% if (isUpdate) { %>         <button type="submit" formaction="<%= deleteAction %>" formmethod="POST" class="btn btn-danger">Remove</button>       <% } %>     </div>   </fieldset> </form> 

Whoa, that’s a lot of code there! Let’s see if we can walk through it. Since two different templates are going to be using this partial, we’ve got to make sure the form looks right in both of them. Most of this code is actually boilerplate from Twitter’s Bootstrap. It’s what allows this app to look so good right off the bat (and on mobile devices too!).

To make this app look even better, you can use the CSS file provided in the demo app download.

The first thing we did was set up some variables for us to use. In the add action we’re passing a params object down to the template in the respond method call. This gives us a few things – it tells us what controller and action this request has been routed to, and gives us any query parameters that were passed in the url. We set up the isUpdate variable to see if we’re currently on the update action, and then we set up a few more variables to help clean up our view code.

From there, all we did was make a form. If we’re on the add action, we just render the form as is. If we’re on the edit action, we fill in the form to let the user update the fields.

Notice that the form will send a POST request to the /todos/ with a _method=PUT parameter. Geddy uses the standard method override parameter to allow you to send PUT and DELETE requests up from the browser without having to use JavaScript. (on the front end at least!)

The last little detail we need to take a look at is that “Remove” button. We’re using html5’s formaction attribute to change the action for this form. You’ll notice that this button’s formaction sends a POST request up to the /todos/:id route with a _method=DELETE parameter. This will hit the remove action on the controller, which we’ll get to later.

Restart your server (geddy) and visit http://localhost:4000/todos/add to see your template in action. Create a To Do item while you’re at it.


Listing all Todos

Now that we have user input To Do items being added into our geddy.todos array, we should probably list them somewhere. Let’s start in on the all method in the model-adapter.

Edit the all method on the adapter to list all todos

Let’s open lib/model_adapters/todo.js again and add an all method right above thesave` method:

 this.all = function (callback) {   callback(null, geddy.todos); } 

This is probably the simplest model-adapter method that we’ll create today, all it does is accept a callback and call it with the an error (which is always null for now, we’ll upgrade this method in the next tutorial), and geddy.todos.

Edit the index action to show all todos

Open up /app/controllers/todos.js again and take a look at the index action. It should look something like this:

 this.index = function (req, resp, params) {   this.respond({params: params}); }; 

This part is really simple, we just use the all method that we just defined on the model-adapter to get all the todos and render them:

 this.index = function (req, resp, params) {   var self = this;   geddy.model.adapter.Todo.all(function(err, todos){     self.respond({params: params, todos: todos});   }); }; 

That’s it for the controller, now onto the view.

Edit index.html.ejs

Take a look at /app/views/todos/index.html.ejs, it should look like this:

 <div class="hero-unit">   <h3>Params</h3>   <ul>   <% for (var p in params) { %>     <li><%= p + ': ' + params[p]; %></li>   <% } %>   </ul> </div> 

Looks a lot like the add.html.ejs template doesn’t it. Again, we won’t need the params boilerplate here, so take that out, and make your index.html.ejs template look like this:

 <div class="hero-unit">   <h2>To Do List</h2>   <a href="/todos/add" class="btn pull-right">Create a new To Do</a></p> </div> <% if (todos &amp;&amp; todos.length) { %>   <% for (var i in todos) { %>   <div class="row todo-item">     <div class="span8"><h3><a href="/todos/<%= todos[i].id; %>/edit"><%= todos[i].title; %></a></h3></div>     <div class="span4"><h3><i class="icon-list-alt"></i><%= todos[i].status; %></h3></div>   </div>   <% } %> <% } %> 

This one is also pretty simple, but this time we’ve got a loop in our template. In the header there we’ve added a button to add new todo’s. Inside the loop we’re generating a row for each todo, displaying it’s title (as a link to it’s edit page), and it’s status.

To check it out, go to http://localhost:4000/todos.


Editing a Todo

Now that we have a link to the edit page, we should probably make it work!

Create a load method in the model adapter

Open up your model adapter again (/lib/model_adapters/todo.js). We’re going to add in a load method so that we can load a specific todo and use it in our edit page. It doesn’t matter where you add it, but for now let’s put it between the all method and the save method:

 this.load = function (id, callback) {   for (var i in geddy.todos) {     if (geddy.todos[i].id == id) {       return callback(null, geddy.todos[i]);     }   }   callback({message: "To Do not found"}, null); }; 

This load method takes an id and a callback. It loops through the items in geddy.todos and checks to see if the current item’s id matches the passed in id. If it does, it calls the callback, passing the todo item back. If it doesn’t find a match, it calls the callback with a error. Now we need to use this method in the todos controller’s show action.

Edit the edit action to find a todo

Open up your todos controller again and take a look at it’s edit action. It should look something like this:

 this.edit = function (req, resp, params) {   this.respond({params: params}); }; 

Let’s use the load method that we just created:

 this.edit = function (req, resp, params) {   var self = this;   geddy.model.Todo.load(params.id, function(err, todo){     self.respond({params: params, todo: todo});   }); }; 

All we’re doing here is loading the todo and sending it down to the template to be rendered. So let’s take a look at the template.

Edit edit.html.ejs

Open up /app/views/todos/edit.html.ejs. Once again we’re not going to need the params boilerplate, so let’s remove it. Make your edit.html.ejs look like this:

 <div class="hero-unit">   <%= partial('_form', {params: params, todo: todo}); %> </div> 

This should look very similar to the add.html.ejs file we just edited. You’ll notice that we’re sending a todo object down to the partial as well as the params this time. The cool thing is, since we already wrote the partial, this is all we’ll have to do to get the edit page to show up correctly.

Restart the server, create a new todo and click the link to see how this works. Now let’s make that update button work!

Edit the save method in the model-adapter

Open up the model-adapter again and find the save method. we’re going to be adding a bit to it so that we can save over existing todos. Make it look like this:

 this.save = function (todo, opts, callback) {   if (typeof callback != 'function') {     callback = function(){};   }   var todoErrors = null;   for (var i in geddy.todos) {     // if it's already there, save it     if (geddy.todos[i].id == todo.id) {       geddy.todos[i] = todo;       todoErrors = geddy.model.Todo.create(todo).errors;       return callback(todoErrors, todo);     }   }   todo.saved = true;   geddy.todos.push(todo);   return callback(null, todo); } 

This loops over all the todo’s in geddy.todos and if the id is already there, it replaces that todo with the new todo instance. We’re doing some stuff here to make sure that our validations work on update as well as create – in order to do this we have to pull the errors property off of a new model instance and pass that back in the callback. If it passed validations, it’ll just be undefined and our code will ignore it. If it didn’t pass, todoErrors will be an array of validation errors.

Now that we have that in place, let’s work on our controller’s update action.


Edit the update action to find a todo, change the status, and save it

Go ahead and open up the controller again and find the ‘update’ action, it should look something like this:

 this.update = function (req, resp, params) {   // Save the resource, then display the item page   this.redirect({controller: this.name, id: params.id}); }; 

You’ll want to edit it to make it look like this:

 this.update = function (req, resp, params) {   var self = this;   geddy.model.adapter.Todo.load(params.id, function (err, todo) {     todo.status = params.status;     todo.title = params.title;     todo.save(function (err, data) {       if (err) {         params.errors = err;         self.transfer('edit');       }       else {         self.redirect({controller: self.name});       }     });   }); }; 

What we’re doing here is loading the requested todo, editing some of it’s properties, and saving the todo again. The code we just wrote in the model-adapter should handle the rest. If we get an error back, that means the new properties didn’t pass validation, so we’ll transfer the request back to the edit action. If we didn’t get an error back, we’ll just redirect the request back over to the index action.

Go ahead and try it out. Restart the server, create a new todo, click on it’s edit link, change the status to done, and see that it get’s updated in the index. If you want to verify that you have your validations working, try changing the title to something shorter than 5 characters.

Now let’s get that “Remove” button working.


Removing a Todo

By now we’ve got a working to do list application, but if you start using it for a while, it’s going to get tough to find the todo item that you’re looking for on that index page. Let’s make that “Remove” button work so we can keep our list nice and short.

Create a remove method in the model-adapter

Let’s open up our model-adapter again, this time we’re going to want to add a remove method in there. Add this right after the save method:

 this.remove = function(id, callback) {   if (typeof callback != 'function') {     callback = function(){};   }   for (var i in geddy.todos) {     if (geddy.todos[i].id == id) {       geddy.todos.splice(i, 1);       return callback(null);     }   }   return callback({message: "To Do not found"}); } 

This one is pretty simple, it should look a lot like the load method. It loops through all the todos in geddy.todos to find the id that we’re looking for. It then splices that item out of the array and calls the callback. If it doesn’t find it in the array, it calls the callback with an error.

Let’s use this in our controller now.

Edit the remove action

Open up your controller again and fing the remove action. It should look something like this:

 this.remove = function (req, resp, params) {   this.respond({params: params}); }; 

Edit it to make it look like this:

 this.remove = function (req, resp, params) {   var self = this;   geddy.model.adapter.Todo.remove(params.id, function(err){     if (err) {       params.errors = err;       self.transfer('edit');     }     else {       self.redirect({controller: self.name});     }   }); } 

We pass the id that we got from the params in the form post into the remove method that we just created. If we get an error back, we redirect back to the edit action (we’re assuming the form posted the wrong info). If we didn’t get an error back, just send the request over to the index action.

Thats it! We’re done.

You can test the remove feature by restarting your server, creating a new todo item, clicking on it’s link, then clicking on the “Remove” button. If you did it right, you should be back on the index page with that item removed.


The Next Steps

In the next tutorial we’ll use http://i.tv’s awesome mongodb-wrapper module to persist our todo’s into MongoDB. With Geddy, this will be easy; all we’ll have to change is the model-adapter.

If you have any questions, please leave a comment here, or open up an issue on github.



50 comentarios:

  1. Woω thаt ωaѕ unuѕual.
    I ϳust wrote an extгеmеlу long comment but after I сlіckeԁ submit
    my comment ԁіdn't appear. Grrrr... well I'm
    not writing all that οver agаin. Αnywaу, ϳuѕt
    wanted tо ѕaу fantаѕtіc blоg!
    Feel free to visit my website :: DiamondLinks.net

    ResponderEliminar
  2. This iѕ a toρic which is close to mу hеaгt.
    .. Cheeгs! Εxactly where are yοur cοntact detаіlѕ
    though?
    my web page :: www.mysmileforu.com

    ResponderEliminar
  3. Hey just wanted to give you a quick heads up and
    let you know a few of the images aren't loading correctly. I'm not sure why
    but I think its a linking issue. I've tried it in two different web browsers and both show the same results.

    Here is my site; Social Bookmark

    ResponderEliminar
  4. I am sure this post has touched all the internet viewers, its really really nice article on building up new
    web site.

    Review my blog post how to lose belly Fat

    ResponderEliminar
  5. Hello, i think that i saw you visited my site thus i got here to go back the want?
    .I am attempting to in finding issues to enhance my web site!
    I guess its good enough to make use of some of your ideas!
    !

    Take a look at my homepage; How To lose belly fat

    ResponderEliminar
  6. Does your blog have a contact page? I'm having problems locating it but, I'd like to shoot you
    an e-mail. I've got some creative ideas for your blog you might be interested in hearing. Either way, great blog and I look forward to seeing it develop over time.

    My web-site :: Starcraft 2 Hack

    ResponderEliminar
  7. certainly like your website however you have to take a look at the spelling
    on several of your posts. A number of them are rife with spelling problems and I to find
    it very troublesome to inform the truth on the other hand I will
    definitely come back again.

    My blog ... http://www.ekabarety.net/

    ResponderEliminar
  8. Simply wish to say your article is as amazing. The clearness
    on your submit is simply excellent and i can think you are knowledgeable on
    this subject. Fine along with your permission let me to snatch your feed to keep up to
    date with impending post. Thank you a million and please continue the enjoyable work.



    Feel free to surf to my page coconut oil for hair

    ResponderEliminar
  9. I’m not that much of a online reader to be honest but your
    blogs really nice, keep it up! I'll go ahead and bookmark your site to come back down the road. All the best

    Also visit my blog; download 7Zip

    ResponderEliminar
  10. Pretty section of content. I just stumbled upon your web site and in accession
    capital to assert that I acquire in fact enjoyed account your
    blog posts. Anyway I will be subscribing to your augment and
    even I achievement you access consistently quickly.


    Review my web blog; Ps3 Jailbreak

    ResponderEliminar
  11. I was able to find good advice from your blog posts.

    My homepage - http://Www.diaperrashhomeremedies.com

    ResponderEliminar
  12. If you are going for finest contents like me, just visit this site everyday since it offers feature contents, thanks

    Look at my homepage Lose Belly Fat

    ResponderEliminar
  13. What's up, its pleasant piece of writing regarding media print, we all be familiar with media is a wonderful source of facts.

    Here is my web page; Unknown

    ResponderEliminar
  14. This paragraph will assist the internet visitors for
    setting up new weblog or even a weblog from start to end.


    Also visit my website: Unknown

    ResponderEliminar
  15. Yes! Finally someone writes about surgical tech career.


    Feel free to visit my homepage: Unknown

    ResponderEliminar
  16. Excellent pieces. Keep posting such kind of info on your site.
    Im really impressed by your blog.
    Hey there, You've performed a great job. I will definitely digg it and individually recommend to my friends. I'm confident
    they'll be benefited from this site.

    Feel free to surf to my blog post :: Stretch Marks

    ResponderEliminar
  17. I was pretty pleased to uncover this web site. I need to to thank you for ones time due to this fantastic read!
    ! I definitely really liked every part of it and i also have you saved
    as a favorite to see new information on your web site.


    Feel free to surf to my web page ... Dragonvale Cheats

    ResponderEliminar
  18. Quality content is the crucial to interest the people to pay a visit the website, that's what this web site is providing.

    Look at my web page: diaper rash home remedies

    ResponderEliminar
  19. This is a topic which is near to my heart... Best wishes!
    Exactly where are your contact details though?

    Here is my page Bestcoconutoilforhair.Com

    ResponderEliminar
  20. Pretty section of content. I just stumbled upon your web site and in
    accession capital to assert that I get in fact enjoyed account your blog
    posts. Anyway I'll be subscribing to your augment and even I achievement you access consistently rapidly.

    Here is my blog post Unknown

    ResponderEliminar
  21. What i do not realize is if truth be told how you
    are no longer actually a lot more well-preferred than you may be right now.
    You are so intelligent. You recognize therefore considerably on the subject of this topic, made me personally consider it from
    a lot of varied angles. Its like women and men aren't interested until it's something to accomplish with Lady gaga!

    Your personal stuffs great. All the time maintain it up!


    My web page: Unknown

    ResponderEliminar
  22. Its such as you read my thoughts! You seem to understand a lot about this, such as you wrote the book in it or something.

    I believe that you can do with a few % to power the message house
    a little bit, but instead of that, that is
    fantastic blog. A fantastic read. I'll certainly be back.

    Feel free to surf to my blog - pirater un Compte facebook

    ResponderEliminar
  23. This post is worth everyone's attention. How can I find out more?

    Here is my weblog; reputation management for individuals

    ResponderEliminar
  24. Good post. I learn something totally new and challenging on blogs I stumbleupon everyday.
    It's always helpful to read content from other writers and use something from other web sites.

    My web blog ... Unknown

    ResponderEliminar
  25. I visit everyday a few web pages and websites to
    read posts, except this web site provides quality based writing.


    Also visit my web blog; HTTP://www.dailymotion.com/video/x103361_code-psn-gratuit-generateur-de-code-psn

    ResponderEliminar
  26. Hi, I want to subscribe for this website to get hottest updates, therefore where can i do it please
    help.

    Here is my page: http://wilbertfu.Datingish.hk

    ResponderEliminar
  27. Hi there, always i used to check website posts here in the early hours in the break of day,
    for the reason that i love to find out more and more.


    Feel free to surf to my weblog :: Unknown

    ResponderEliminar
  28. I liκe the helpful infоrmation
    you prоvіde in youг artіclеs.

    Ӏ'll bookmark your blog and check again here regularly. I'm slіghtly certain I wіll bе informed many new stuff гіght right
    hеre! Best of luck for the next!

    Haѵe a looκ at my pagе ... arjun kanuri

    ResponderEliminar
  29. Have you ever thought about creating an e-book
    or guest authoring on other blogs? I have a blog based upon on the
    same ideas you discuss and would really like to have you share some stories/information.

    I know my subscribers would value your work. If you are even remotely interested, feel free
    to send me an e-mail.

    my blog - Unknown

    ResponderEliminar
  30. I am not certain where you are getting your information, however
    good topic. I needs to spend a while studying more or figuring out
    more. Thanks for wonderful info I used to be in search of this info for my mission.



    My blog: http://djldusty.mancouch.com/772722517/dry-scalp-hair-loss-will-cause-as-Well-as/

    ResponderEliminar
  31. Hi colleagues, how is the whole thing, and what you would like to say
    on the topic of this post, in my view its actually amazing for me.


    Look at my web blog: unknown

    ResponderEliminar
  32. Very nice post. I just stumbled upon your weblog and wished to say that I have truly enjoyed surfing around
    your blog posts. In any case I will be subscribing to
    your feed and I hope you write again very soon!


    Check out my web page :: Unknown

    ResponderEliminar
  33. Good way of telling, and nice paragraph to obtain information regarding
    my presentation subject, which i am going to deliver
    in college.

    Have a look at my web blog Microsoft Office Gratuit

    ResponderEliminar
  34. I love your blog.. very nice colors & theme. Did you create this website yourself or
    did you hire someone to do it for you? Plz respond as I'm looking to design my own blog and would like to find out where u got this from. cheers

    My site ... ps3 Jailbreak

    ResponderEliminar
  35. I am really grateful to the holder of this website who
    has shared this great post at at this place.

    Also visit my site: dry scalp treatments

    ResponderEliminar
  36. I really love your website.. Great colors
    & theme. Did you make this web site yourself? Please reply back as I'm planning to create my own blog and want to learn where you got this from or just what the theme is named. Cheers!

    My website; PS3 Jailbreak 4.41

    ResponderEliminar
  37. Hi there, every time i used to check web site posts here early in the break of
    day, as i love to gain knowledge of more and more.


    Feel free to surf to my web-site ... Psn Code Generator

    ResponderEliminar
  38. Incredible points. Outstanding arguments.
    Keep up the amazing effort.

    My web blog The Interlace

    ResponderEliminar
  39. Hi there! Do you know if they make any plugins
    to help with SEO? I'm trying to get my blog to rank for some targeted keywords but I'm not seeing
    very good results. If you know of any please share.

    Cheers!

    My weblog: Psn Code Generator

    ResponderEliminar
  40. Good write-up. I definitely appreciate this website.
    Keep writing!

    Also visit my web page ... your anchor text

    ResponderEliminar
  41. Definitely believe that which you stated. Your favorite reason
    appeared to be on the internet the simplest thing to be aware of.
    I say to you, I definitely get annoyed while people consider worries that they plainly don't know about. You managed to hit the nail upon the top and defined out the whole thing without having side-effects , people could take a signal. Will probably be back to get more. Thanks

    Here is my homepage: Dragon City Cheat Engine

    ResponderEliminar
  42. I hаve bеen brоwsіng on-lіne greater than 3 houгs nowaԁауѕ, yеt Ӏ
    by nο means discovered any attеntiοn-grabbing aгtіcle
    lіkе yours. Ιt's beautiful value sufficient for me. In my opinion, if all website owners and bloggers made excellent content as you did, the web will be much more useful than ever before.

    My homepage reputation management

    ResponderEliminar
  43. I was suggested this website by my cousin.
    I am not sure whether this post is written by him as nobody else know such detailed about my trouble.
    You are wonderful! Thanks!

    Look at my web page Code Psn Gratuit

    ResponderEliminar
  44. Greetings I am so thrilled I found your webpage, I really found
    you by accident, while I was researching on Google for something else, Anyways I am here now and would just
    like to say kudos for a incredible post and a all round thrilling blog (I also
    love the theme/design), I don't have time to look over it all at the moment but I have book-marked it and also included your RSS feeds, so when I have time I will be back to read more, Please do keep up the superb job.

    My web page: psn Code Generator

    ResponderEliminar
  45. Does your website have a contact page? I'm having a tough time locating it but, I'd like to send
    you an email. I've got some ideas for your blog you might be interested in hearing. Either way, great website and I look forward to seeing it improve over time.

    Also visit my homepage ... league Of legends Hack

    ResponderEliminar
  46. I am really enjoying the theme/design of
    your blog. Do you ever run into any internet browser compatibility issues?

    A couple of my blog audience have complained about my website not
    operating correctly in Explorer but looks great in Chrome.
    Do you have any recommendations to help fix this issue?


    Here is my weblog :: world Of Tanks hack

    ResponderEliminar
  47. Hello there I am so thrilled I found your blog, I really found you by error, while I
    was researching on Digg for something else, Anyways I am here now and would just
    like to say kudos for a fantastic post and a all round entertaining blog (I also
    love the theme/design), I don't have time to go through it all at the minute but I have book-marked it and also included your RSS feeds, so when I have time I will be back to read a great deal more, Please do keep up the fantastic work.

    Visit my blog post; your anchor text

    ResponderEliminar
  48. It is perfect time to make some plans for the future and it's time to be happy. I have read this post and if I could I desire to suggest you some interesting things or advice. Perhaps you could write next articles referring to this article. I wish to read more things about it!

    my page - minecraft gift Code Generator

    ResponderEliminar
  49. I am regular reader, how are you everybody? This paragraph posted at this web
    site is truly pleasant.

    Feel free to surf to my web page ... Ps3 Jailbreak 3.56

    ResponderEliminar
  50. Exceptional post but I was wondering if you could
    write a litte more on this topic? I'd be very grateful if you could elaborate a little bit further. Kudos!

    Visit my web blog jailbreak playstation 3 []

    ResponderEliminar