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 todo
s, 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 todo
s and registering it as a model in geddy. Let’s define some properties for our todo
s. 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 todo
s will have a title, an id, and a status, and all three will be required. Now let’s set some validations for our todo
s.
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 todo
s. 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 youradd.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 the
save` 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 todo
s 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 && 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 todo
s. 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 todo
s 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:
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
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
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
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
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
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
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/
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
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
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
I was able to find good advice from your blog posts.
My homepage - http://Www.diaperrashhomeremedies.com
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
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
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
Yes! Finally someone writes about surgical tech career.
Feel free to visit my homepage: Unknown
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
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
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
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
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
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
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
This post is worth everyone's attention. How can I find out more?
Here is my weblog; reputation management for individuals
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
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
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
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
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
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
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/
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
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
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
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
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
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
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
Incredible points. Outstanding arguments.
Keep up the amazing effort.
My web blog The Interlace
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
Good write-up. I definitely appreciate this website.
Keep writing!
Also visit my web page ... your anchor text
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
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
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
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
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
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
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
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
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
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 []
Publicar un comentario