My insight on the business of software development, expecially in agile methodolgies and lately into functional programming
Wednesday, October 19, 2016
Learn clojure.spec (cljs,spec) interactively
Friday, December 18, 2015
Clojurescript/Reagent: How to start in 1 minute
What is a great development environment for web applications?
One that:
- can be started and configured extremely easy
- allows instant feedback
- can be done in a great programming language
- doesn't need expensive tools
So Clojurescript. For clojure/clojurescript projects you need one tool installed: Leiningen, which will be used from the command line. (install from here. You need java installed before)
1. can be started and configured extremely easy
Create a new project
lein new figwheel hello_world -- --reagent
And start it:
cd hello_world
lein figwheeel
Now, go in your browser to localhost:3449
It is already there!!!
2. instant feedback
Open the code using some tool, preferably LightTable and do this change in src/hello_world/core.cljs in the hello_world component:
{:style {:color "red"}}
Save the file.

It is already in the browser!!
now, let's start that in console.
Boom!

It is already in the browser. Figwheel takes care of all that!
Now 3 and 4 are answered by clojurescript which is based on the great programming language clojure. We're also using Reagent (clojurescript library on top of Facebook React) and figwheel which allows all the instant feedback stuff.
Saturday, October 17, 2015
REST api using Clojure and MySql. Is Clojure the most productive, robust language on the planet?
In my life, I have written a lot of web applications, using Java, then .NET, PHP and lately REST API;s using Python. I thought there cannot be anything to match Python productivity using Flask and SqlAlchemy until today.
Making a REST api in Clojure using Ring/Compojure and SqlKorma
Update 2019
Update 2019:
(defproject asado "0.1.0-SNAPSHOT" :description "FIXME: write description" :dependencies [[org.clojure/clojure "1.10.0"] [metosin/compojure-api "2.0.0-alpha30"] [ring/ring "1.6.3"] [compojure "1.6.1"] [manifold "0.1.8"] [metosin/spec-tools "0.9.2"] ;database [korma "0.4.3"] [mysql/mysql-connector-java "8.0.12"] ] :ring {:handler asado.handler/app} :uberjar-name "server.jar" :profiles {:dev {:dependencies [[javax.servlet/javax.servlet-api "3.1.0"] [ring/ring-mock "0.3.2"]] :plugins [[lein-ring "0.12.5"]]}})
CREATE TABLE `items` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`title` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`is_complete` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Update 2019:
(ns asado.database (:require [korma.db :as korma])) (def db-connection-info (korma/mysql {:classname "com.mysql.cj.jdbc.Driver" :subprotocol "mysql" :user "root" :subname "//localhost:3307/todo"})) ; set up korma(korma/defdb db db-connection-info)
Update 2019:
(ns asado.query (:require [asado.database :refer [db]] [korma.core :refer :all] [schema.core :as s])) (s/defschema TitleBody {:title s/Str}) (s/defschema TodoBody {:id s/Int :title s/Str :is_complete s/Bool}) (defentity items) (defn get-todos [] (select items)) (defn add-todo [title] (insert items (values {:title title}))) (defn delete-todo [id] (delete items (where {:id id}))) (defn update-todo [id title is-complete] (update items (set-fields {:title title :is_complete is-complete}) (where {:id id}))) (defn get-todo [id] (first (select items (where {:id id}))))
Update 2019, using compojure-api:
(ns asado.handler (:require [compojure.api.sweet :refer :all] [ring.util.http-response :refer :all] [schema.core :as s] [asado.query :as q])) (def app (api {:swagger {:ui "/" :spec "/swagger.json" :data {:info {:title "Asado" :description "Compojure Api example"} :tags [{:name "api", :description "some apis"}]}}} (context "/api" [] :tags ["api"] (GET "/todos" [] (ok (q/get-todos ))) (GET "/api/todos/:id" [] :path-params [id :- s/Int] (ok (q/get-todo id))) (POST "/api/todos" [] :body [title-body q/TitleBody] (let [{:keys [title]} title-body] (ok (q/add-todo title)))) (PUT "/api/todos" [] :body [todo-body q/TodoBody] (let [{:keys [id title is_complete]} todo-body] (ok {:updated (q/update-todo id title is_complete)}))) (DELETE "/api/todos/:id" [] :path-params [id :- s/Int] (ok {:deleted (q/delete-todo id)})) )))
Conclusion
Friday, February 06, 2015
Fighting complexity through functional composition, part 1: how to implement functional composition
The problem
Requirement: in the json, we need to have a key “measurement”, that is mandatory, cannot be null, needs to be a string and cannot be empty string.
class TestValidations(unittest.TestCase):
def validate_pair(self,json, is_valid, number_of_errors):
errors = validate_simplest_json(json)
print "",is_valid, errors, json
self.assertEquals(len(errors)==0,is_valid)
self.assertEquals(len(errors),number_of_errors)
def test_json_validation(self):
self.validate_pair({},False,1)
def validate_simplest_json(json):
errors = []
return errors
def validate_simplest_json(json):
errors = []
if not json.has_key("measurement"):
errors.append("measurement cannot be missing”)
return errors
def test_json_validation(self):
self.validate_pair({},False,1)
self.validate_pair({"measurement":None},False,1)
def validate_simplest_json(json):
errors = []
if not json.has_key("measurement"):
errors.append("measurement cannot be missing")
else:
if json["measurement"]==None:
errors.append("measurement cannot be null”)
return errors
def test_json_validation(self):
self.validate_pair({},False,1)
self.validate_pair({"measurement":None},False,1)
self.validate_pair({"measurement":-1},False,1)
self.validate_pair({"measurement":{}},False,1)
self.validate_pair({"measurement":False},False,1)
self.validate_pair({"measurement":"abc"},True,0)
self.validate_pair({"measurement":u"Citroën"},True,0)
def validate_simplest_json(json):
errors = []
if not json.has_key("measurement"):
errors.append("measurement cannot be missing")
else:
if json["measurement"]==None:
errors.append("measurement cannot be null")
else:
if not isinstance(json["measurement"], str) and not isinstance(json["measurement"], unicode):
errors.append("measurement needs to string or unicode")
return errors
def test_json_validation(self):
self.validate_pair({},False,1)
self.validate_pair({"measurement":None},False,1)
self.validate_pair({"measurement":-1},False,1)
self.validate_pair({"measurement":{}},False,1)
self.validate_pair({"measurement":False},False,1)
self.validate_pair({"measurement":"abc"},True,0)
self.validate_pair({"measurement":u"Citroën"},True,0)
self.validate_pair({"measurement":""},False,1)
def validate_simplest_json(json):
errors = []
if not json.has_key("measurement"):
errors.append("measurement cannot be missing")
else:
if json["measurement"]==None:
errors.append("measurement cannot be null")
else:
if not isinstance(json["measurement"], str) and not isinstance(json["measurement"], unicode):
errors.append("measurement needs to string or unicode")
else:
if len(json["measurement"].strip())==0:
errors.append("measurement cannot be an empty string")
return errors
def test_json_validation(self):
self.validate_pair({},False,1)
self.validate_pair({"measurement":None},False,1)
self.validate_pair({"measurement":-1},False,1)
self.validate_pair({"measurement":{}},False,1)
self.validate_pair({"measurement":False},False,1)
self.validate_pair({"measurement":"abc"},True,0)
self.validate_pair({"measurement":u"Citroën"},True,0)
self.validate_pair({"measurement":""},False,1)
self.validate_pair({"measurement":"a"},False,1)
self.validate_pair({"measurement":"abcdefghijklmnefghij"},False,1)
self.validate_pair({"measurement":"password"},False,1)
self.validate_pair({"measurement":"archived"},False,1)
self.validate_pair({"measurement":"arCHived"},False,1)
def validate_simplest_json(json):
errors = []
if not json.has_key("measurement"):
errors.append("measurement cannot be missing")
else:
if json["measurement"]==None:
errors.append("measurement cannot be null")
else:
if not isinstance(json["measurement"], str) and not isinstance(json["measurement"], unicode):
errors.append("measurement needs to string or unicode")
else:
lenm=len(json["measurement"].strip())
if lenm==0:
errors.append("measurement cannot be an empty string")
else:
if lenm<3: data-blogger-escaped-div="">
errors.append("measurement needs at least 3 characters")
elif lenm>10:
errors.append("measurement needs at most 10 characters")
elif json["measurement"].strip().lower() in ["archived","password"]:
errors.append("measurement has a value which is not allowed")
return errors
The solution: implementing functional composition
def validate_simplest_json_imperative_linear(json):
errors = []
should_exit=False
key = "measurement"
if not key_exists(json,key):
errors.append("{0} cannot be missing".format(key))
should_exit=True
if not should_exit:
if value_null(json, key):
errors.append("{0} cannot be null".format(key))
should_exit=True
if not should_exit:
if not is_string_or_unicode(json, key):
errors.append("{0} needs to string or unicode".format(key))
should_exit=True
if not should_exit:
if is_empty_string(json, key):
errors.append("{0} cannot be an empty string".format(key))
should_exit=True
if not should_exit:
lenm=len(json[key].strip())
if lenm<3: data-blogger-escaped-div="">
errors.append("{0} needs at least 3 characters".format(key))
should_exit=True
elif lenm>10:
errors.append("{0} needs at most 10 characters".format(key))
should_exit=True
if not should_exit:
if json[key].strip().lower() in ["archived","password"]:
errors.append("{0} has a value which is not allowed".format(key))
should_exit=True
return errors
ValidationState = namedtuple("ValidationState","json key errors exit”)
def validate_simplest_json_imperative_linear_with_state(json):
initial_state = ValidationState(json=json, key="measurement",errors=[], exit=False)
state = validate_key_exists(initial_state)
if not state.exit:
state = validate_not_null(state)
if not state.exit:
state = validate_string_or_unicode(state)
if not state.exit:
state = validate_not_empty_string(state)
if not state.exit:
state = validate_length(state, 3,10)
if not state.exit:
state = validate_not_in(state, ["archived","password"])
return state.errors
def validate_key_exists(state):
print validate_key_exists.__name__,state
if not key_exists(state.json,state.key):
return state._replace(errors = state.errors+["{0} cannot be missing".format(state.key)])._replace(exit=True)
return state
def validate_not_null(state):
print validate_not_null.__name__,state
if value_null(state.json,state.key):
return state._replace(errors = state.errors+["{0} cannot be null".format(state.key)])._replace(exit=True)
return state
def validate_string_or_unicode(state):
print validate_string_or_unicode.__name__,state
if not is_string_or_unicode(state.json,state.key):
return state._replace(errors = state.errors+["{0} needs to string or unicode".format(state.key)])._replace(exit=True)
return state
def validate_not_empty_string(state):
print validate_not_empty_string.__name__,state
if is_empty_string(state.json,state.key):
return state._replace(errors = state.errors+["{0} cannot be an empty string".format(state.key)])._replace(exit=True)
return state
def validate_length(state, min, max):
print validate_length.__name__,state
lenm=len(state.json[state.key].strip())
if lenm
return state._replace(errors = state.errors+["{0} needs at least 3 characters".format(state.key)])._replace(exit=True)
elif lenm>max:
return state._replace(errors = state.errors+["{0} needs at most 10 characters".format(state.key)])._replace(exit=True)
return state
def validate_not_in(state,vals):
print validate_not_in.__name__,state
if state.json[state.key].strip().lower() in vals:
return state._replace(errors = state.errors+["{0} has a value which is not allowed".format(state.key)])._replace(exit=True)
return state
def compose2(f, g):
def run(x):
result_f = f(x)
if not result_f.exit:
return g(result_f)
else:
return result_f
return run
def validate_simplest_json_imperative_linear_with_state(json):
initial_state = ValidationState(json=json, key="measurement",errors=[], exit=False)
# state = validate_key_exists(initial_state)
#
# if not state.exit:
# state = validate_not_null(state)
composed_function = compose2(validate_key_exists, validate_not_null)
state = composed_function(initial_state)
#compose n functions
def compose(*functions):
return reduce(compose2, functions)
def validate_simplest_json_imperative_linear_with_state(json):
initial_state = ValidationState(json=json, key="measurement",errors=[], exit=False)
composed_function = compose(validate_key_exists, validate_not_null,validate_string_or_unicode,validate_not_empty_string)
state = composed_function(initial_state)
if not state.exit:
state = validate_length(state, 3,10)
if not state.exit:
state = validate_not_in(state, ["archived","password"])
return state.errors
def create_validate_length(min, max):
def validate_length(state):
print validate_length.__name__,state
lenm=len(state.json[state.key].strip())
if lenm
return state._replace(errors = state.errors+["{0} needs at least 3 characters".format(state.key)])._replace(exit=True)
elif lenm>max:
return state._replace(errors = state.errors+["{0} needs at most 10 characters".format(state.key)])._replace(exit=True)
return state
return validate_length
def create_validate_not_in(vals):
def validate_not_in(state):
print validate_not_in.__name__,state
if state.json[state.key].strip().lower() in vals:
return state._replace(errors = state.errors+["{0} has a value which is not allowed".format(state.key)])._replace(exit=True)
return state
return validate_not_in
def validate_simplest_functional_composition(json):
initial_state = ValidationState(json=json, key="measurement",errors=[], exit=False)
composed_function = compose(validate_key_exists, validate_not_null,validate_string_or_unicode,validate_not_empty_string, create_validate_length(3, 10), create_validate_not_in(["archived","password"]))
final_state = composed_function(initial_state)
return final_state.errors
http://runnable.com/VNMhoTKLSn9Tm0GI/fighting-complexity-through-functional-composition-for-python
Now you would think, how can a solution with ... lines of code be better then one with just 21. In part 2 of the article, called "Why is functional composition better" I will illustrate why, and how functional composition makes our code simpler, easier to understand, debug and change.
Sunday, February 01, 2015
The value of values, by Rich Hickey - prerequisite for functional programming
Friday, December 19, 2014
Functional programming (in javascript and python): Part 1: Why?
At the beginning of this year I started thinking how would I describe what I did as a programmer for the last 13 years in just a few words, and this is the conclusion:
List processing and debugging.
Now let me explain: If I abstract enough, everything looks like a list: arrays, dictionaries even objects, considering them a list of properties with values. Complex objects become lists of lists. They're everywhere: from databases where these days you get back lists of rows/objects, to models, to the UI for instance $("...") in jQuery. So basically all day long I wrote functions and methods to process these lists and lists of lists, transforming them, changing them, persisting and loading them, send and receive them through the networks and internet or displaying them on the screen.
The big problem is that if the code becomes a little complex, you expect at the end of some processing some result and it's not there. Basically you expect the system to be in a state, and it is in another, thus having an error or a bug, so you start following what happens to see why. If the system uses events and async operations your debugging work starts to take a lot of time and effort. Thinking about time, I would correct the conclusion above to
30% list processing, 70% debugging
The causes
For years I looked for solutions: from OOP, to patterns, to separation of concerns, agile methodologies practices such as unit testing and test driven development. All helped, but none seem to address the biggest issues: state is everywhere thus unexpected state changes, not to mention concurrent programming.
The solution
Then I remembered about LISP (List programming), and how strange it seemed. But somehow it seems to do exactly what I needed, unfortunately not in the programming languages that I needed, but then I started to realise that the languages I worked with are multi paradigm and yes functional programming concepts are embedded right in (javascript/python).
So basically what functional programming addresses is: list processing using 3 main functions: map, reduce and filter. Using these 3 functions, simpler data structures like arrays, dictionaries and lists instead of objects used as data structures and higher order functions, the code to process lists becomes simpler and smaller.
Example
Let's see the total of an order in the shopping cart, for groceries:
Python, using imperative programming and OOP (badly, like pretty much everyone):
class Order()
orderItems = []
def __init__():
pass
...
class OrderItem()
name = None
quantity = 0.0
price = 0.0
def __init__(name=None, quantity=0.0, price=0.0):
self.name=name
order = Order()
order.orderItems.append(OrderItem(name="", quantity=1,price=0.99))
return ...
sum = 0
for item in order.orderItems
if is_grocery(item):
sum+=item.quantity*item.price
print sum
Python, using FP:
order = {"orderItems":[
{ "name":"sada""quantity":2,"price":0.99},
{"name":"zjdfj""quantity":1,"price":2.99},
def is_grocery(item):
return ...
print reduce(
lambda total1,total2: total1+total2,
map(
lambda orderItem: i1["quantity"]*i1["price"]
filter(
lambda orderItem:is_grocery(orderItem),
order["orderItems"]
)
)
About state changes, we'll speak in the next article.
Conclusion
So why didn't functional programming replace imperative programming since it has many areas where it clearly is better (maybe not in the example above)? Because it is much more difficult to understand and use, even though the resulting code is smaller. If you look at the code above you see that is starts the other way round: it wants to "reduce" 2 totals for order items and then you see that the reduce is applied on a list of totals for order items which is applied on a filtered list of items that are groceries. This is the reason people say functional programming shows what the code does not how.
Thursday, July 31, 2014
Hierarchical persistent data structures in javascript (using underscore.js)
The problem
I have a model (using MVC pattern) and I want to keep both the initial model state, and the one that is changed afar some operations. I wanted initially to use something like cloning or deep copying but it seems just to waste memory, if for instance I change only one thing in a hierarchy, so I don't need everything clones.The solution : persistent data structures
I knew that what I wanted is to use persistent data structures in javascript and although they are available in libraries such as immutable or mori, I needed one that is hierarchical.Persistent data sturctures acording to wikipedia:
In computing, a persistent data structure is a data structure that always preserves the previous version of itself when it is modified. Such data structures are effectively immutable, as their operations do not (visibly) update the structure in-place, but instead always yield a new updated structure. (A persistent data structure is not a data structure committed to persistent storage, such as a disk; this is a different and unrelated sense of the word "persistent.")
Solution:
So I want something like:
initial_state = {a:1, b:2};
modified_state = change(model,"a","abc"))
where initial_state remains unchanged and modified_state is {a:"abc",b:2}. This is very simple using something like _.clone(initial_state) in underscore.js. But what if I want something like:
initial_state = model = {l:[{id:1}, {id:2}], b:2};
modified_state = change(model,"l[1].id",3)
resulting in: modified_state = {l:{id:1},{id:3}], b:2}. Here copy, deep copy or clone doesn't help. So what if I write a function like this:
function change(obj,path,val)
{
var new_obj = _.clone(obj);
var path = path.replace("[",".").replace("]","");
var levels = path.split(".");
if(levels.length>1)
{
var head = _.first(levels)
var tail = path.replace(head+".","")
new_obj[head] = change(obj[head],tail,val)
}
else
{
new_obj[path] = val;
}
return new_obj;
}
It works!
I can even do:
initial_state = {g:
[
{id:1, values:[1,2,3]},
{id:2, values:[3,4]}
],
b:2}
new_state1 = change(model,"g[1].values[1]",7);
new_state2=change(model,"g[1].values",[7,2]);
Wednesday, July 10, 2013
We were right: 3D is the future of UI/UX as iOS7 proves it
Thursday, March 28, 2013
10 greatest technologies of the last 13 years in my programming career
I will start from the beginning:
1. Java (In 2000 I had the choice of using C++ or Java, and I thought Java was incredible. In the meanwhile it might have come and gone)
2. JSP and Expression Language EL - Web programming with Servlets wasn't exactly easy
3. Hibernate - the ORM that showed it is possible
4. Ruby On Rails - the MVC framework that simplified and let to better organised web application code forever
5. C# and .NET - The Monorail/Activerecord/Castle frameworks - like Java, they came and now they're obsolete, at least for me. But in 2005, boy they were great!
6. Apache Lucene - searching for words in text would never be the same as before.
7. Javascript and jQuery - when I realised how powerful Javascript can be, I started to really doubt the power of static languages
8. HTML5/CSS3 - web would never be the same after this (CSS transforms and HTML5 canvas will change the world forever). And combined with PhoneGap led to TaiaApps out first iPad app, with a 3D UI ( www.taia76.com )
9. NoSQL databases - CouchDB - thinking about databases and how they keep data is now changed, as I said before - forever. Guess what powers TaiaApps?
10. Python - Flask/SQLAlchemy - no doubt Python is a great language, but SQLAlchemy is by far the greatest ORM I have ever used.
Saturday, December 29, 2012
Friday, December 21, 2012
3D user interfaces: Navigating in space between information nodes
In our cases the information nodes can either be iPad apps or people that recommend them.
www.taia76.com
Thursday, December 20, 2012
3D user interfaces: lists and scrolling in 3D
More on the app website:
www.taia76.com
Sunday, December 16, 2012
3D user interfaces in HTML5/JavaScript
Last night, I and a friend released our first app, for iPad, that is our approach on 3d user interfaces.
There have been many challenges, especially since the entire UI is HTML5/Javascript based. But one by one we managed to overcome them. How would you think scrolling in 3d works? :)
More information on the website: www.taia76.com
Monday, September 17, 2012
MVC in javascript
Problem
Today, in modern web applications, more and more code is moved from being generated server side (in java, c# with asp.net , ruby on ror, php) to be executed on the client browser, in javascript. This created a massive problem for all those used to having code well organized in modern web frameworks using the very powerfull Model View Controller pattern, since most of the code is written now in javascript.
Could MVC in javascript be a solution?
Well, there are more and more frameworks trying to adress the problem of having tons of javascript, whioch becomes a nightmare as well as replace the dynamic generation on the server , thinks such backbone.js, but I find it not very MVC. So why wouldn't I actually write my own MVC framework.
The first thing we want to do it actually list the users on the page, so that means the controller is invoked, it takes the decistion to load the users from the server into the model, and after that send the model to the view to render the users as html. So we have an html file index.html
...
<body>
<div id="list">
<a href="http://www.blogger.com/blogger.g?blogID=9648422#" id="add" onclick="controller.add();">Add</a>
<div id="usersList">
</div>
</div>
<div id="form">
</div>
<div id="tests">
<h1 id="qunit-header">
mvc.js</h1>
</div>
</body>
<h2 id="qunit-userAgent">
</h2>
<ol id="qunit-tests">
</ol>
</pre>
<script>
jQuery(window).load(function () {
controller = new UsersController();
controller.index();
});
</script>
</body>
</html>
now the controller class:
UsersController.prototype = new Object;
//constructor definition
UsersController.prototype.constructor = UsersController;
function UsersController() {
this.model = new UsersModel();
this.view=new UsersView();
};
UsersController.prototype.index=function(){
this.model.loadUsers();
this.view.renderUsersList(this.model);
};
and the model:UsersModel.prototype = new Object;
//constructor definition
UsersModel.prototype.constructor = UsersModel;
function UsersModel()
{
this.users=[];
}
UsersModel.prototype.loadUsers=function()
{
this.users = eval(new HttpRequest().get("http://localhost/users/list.aspx"));
};
now let's see the view that renders the list. For it we actually use jQuery templates:
UsersView.prototype = new Object;
//constructor definition
UsersView.prototype.constructor = UsersView;
function UsersView()
{
//templates
this.userLine = "<div if='user${user.id}' class='user' >"+
" <div id='fullName${user.id}' class='cell'>${user.fullName}</div>"+
" <div id='username${user.id}' class='cell'>${user.username}</div>"+
" <div id='email${user.id}' class='cell'>${user.email}</div>"+
" <div class='cell'><a id='edit${user.id}' href='#' onClick='controller.edit(${user.id})'>Edit</a></div>"+
" <div class='cell'><a id='delete${user.id}' href='#' onClick='controller.delete(${user.id})'>Delete</a></div>"+
"</div>";
jQuery.template("userLine", this.userLine);
}
UsersView.prototype.renderUsersList=function(model)
{
jQuery("#usersList").html("");
jQuery("#list").show();
jQuery("#form").hide();
for(var i=0;i<model.users.length;i++)
{
jQuery.tmpl("userLine", { user: model.users[i] }).appendTo("#usersList");
}
};
Hmmm, very simple. Now let's implement the add:UsersController.prototype.add=function(){
this.view.renderAdd();
};
UsersController.prototype.save=function(id,fullName,username,password,email){
id=this.model.users.length+1;
this.model.users.push({id:id,fullName:fullName,username:username,password:password, email:email});
};
and in the view:
UsersView.prototype = new Object;
//constructor definition
UsersView.prototype.constructor = UsersView;
function UsersView()
{
//templates
this.userLine = "<div if='user${user.id}' class='user' >"+
" <div id='fullName${user.id}' class='cell'>${user.fullName}</div>"+
" <div id='username${user.id}' class='cell'>${user.username}</div>"+
" <div id='email${user.id}' class='cell'>${user.email}</div>"+
" <div class='cell'><a id='edit${user.id}' href='#' onClick='controller.edit(${user.id})'>Edit</a></div>"+
" <div class='cell'><a id='delete${user.id}' href='#' onClick='controller.delete(${user.id})'>Delete</a></div>"+
"</div>";
jQuery.template("userLine", this.userLine);
this.userForm = "<div class='fields' style='display:table-row'>"+
" <div id='fullName_${user.id}' style='display:table-row'>Full Name:<input id='fullName' type='text' value='${user.fullName}'/></div>"+
" <div id='username_${user.id}' style='display:table-row'>Username:<input id='username' type='text' value='${user.username}'/></div>"+
" <div id='password_${user.id}' style='display:table-row'>Password:<input id='password' type='password' value='${user.password}'/></div>"+
" <div id='email_${user.id}' style='display:table-row'>Email:<input id='email' type='text' value='${user.email}'/></div>"+
" <a id='save' href='#' onClick='controller.save(\"${user.id}\",jQuery(\"#fullName\").val(),jQuery(\"#username\").val(),jQuery(\"#password\").val(),jQuery(\"#email\").val());'>Save</a>"+
" <a id='cancel' href='#' onClick='controller.list();'>Cancel</a>"+
"</div>";
jQuery.template("userForm", this.userForm);
}
...
UsersView.prototype.renderAdd=function()
{
var user={fullName:"",username:"",password:"",email:"" };
jQuery("#form").html("").show();
jQuery("#list").hide();
jQuery.tmpl("userForm", { user: user}).appendTo("#form");
};
Now what about edit?UsersController.prototype.edit=function(id){
var user = this.model.findUserById(id);
this.view.renderEdit(user);
};
UsersController.prototype.save=function(id,fullName,username,password,email){
if(id=="")
{
id=this.model.users.length+1;
this.model.users.push({id:id,fullName:fullName,username:username,password:password, email:email});
}
else
{
var user = this.model.findUserById(id);
user.fullName=fullName;
user.username=username;
user.password=password;
user.email=email;
}
this.view.renderUsersList(this.model);
};
and in the model:
UsersModel.prototype.findUserById=function(id)
{
for(var i=0;i<this.users.length;i++)
{
if(this.users[i].id==id)
return this.users[i];
}
return null;
};
and the view is refactored to:UsersView.prototype = new Object;
//constructor definition
UsersView.prototype.constructor = UsersView;
function UsersView()
{
//templates
this.userLine = "<div if='user${user.id}' class='user' >"+
" <div id='fullName${user.id}' class='cell'>${user.fullName}</div>"+
" <div id='username${user.id}' class='cell'>${user.username}</div>"+
" <div id='email${user.id}' class='cell'>${user.email}</div>"+
" <div class='cell'><a id='edit${user.id}' href='#' onClick='controller.edit(${user.id})'>Edit</a></div>"+
" <div class='cell'><a id='delete${user.id}' href='#' onClick='controller.delete(${user.id})'>Delete</a></div>"+
"</div>";
jQuery.template("userLine", this.userLine);
this.userForm = "<div class='fields' style='display:table-row'>"+
" <div id='fullName_${user.id}' style='display:table-row'>Full Name:<input id='fullName' type='text' value='${user.fullName}'/></div>"+
" <div id='username_${user.id}' style='display:table-row'>Username:<input id='username' type='text' value='${user.username}'/></div>"+
" <div id='password_${user.id}' style='display:table-row'>Password:<input id='password' type='password' value='${user.password}'/></div>"+
" <div id='email_${user.id}' style='display:table-row'>Email:<input id='email' type='text' value='${user.email}'/></div>"+
" <a id='save' href='#' onClick='controller.save(\"${user.id}\",jQuery(\"#fullName\").val(),jQuery(\"#username\").val(),jQuery(\"#password\").val(),jQuery(\"#email\").val());'>Save</a>"+
" <a id='cancel' href='#' onClick='controller.list();'>Cancel</a>"+
"</div>";
jQuery.template("userForm", this.userForm);
}
UsersView.prototype.renderUsersList=function(model)
{
jQuery("#usersList").html("");
jQuery("#list").show();
jQuery("#form").hide();
for(var i=0;i<model.users.length;i++)
{
jQuery.tmpl("userLine", { user: model.users[i] }).appendTo("#usersList");
}
};
UsersView.prototype.renderAdd=function()
{
var user={fullName:"",username:"",password:"",email:"" };
this.renderForm(user);
};
UsersView.prototype.renderEdit=function(user)
{
this.renderForm(user);
};
UsersView.prototype.renderForm=function(user)
{
jQuery("#form").html("").show();
jQuery("#list").hide();
jQuery.tmpl("userForm", { user: user}).appendTo("#form");
};
Conclusion
As you can see above, implementing MVC in javascript can be very simple and doesn't require any sort of frameworks. Once MVC is implemented the code is well organized depending on concerns: who commands - controller, where by one look you can see what the entire code does (list, add, save, edit) the model who holds the data and exchanges it with the server and the view which actually renders it in html when asked by the controller.Simple to implement, well organized, easy to follow the code, very extensible.
Friday, September 14, 2012
TDD - scurta introducere practica (romanian only)
Download prezentare ca pdf
Demo unit test
Demo integration test
Articole despre TDD:
http://danbunea.blogspot.ro/2005/12/test-little-code-little-practical.html
http://danbunea.blogspot.ro/2007/01/test-first-web-applications-tdding.html
http://danbunea.blogspot.ro/2006/05/refactoring-legacy-web-application.html
http://danbunea.blogspot.ro/2005/12/tdding-sales-report.html
http://danbunea.blogspot.ro/2005/11/model-view-presenter-is-testing_27.html
http://danbunea.blogspot.ro/2007/03/testing-dragndrop-with-watin.html
http://danbunea.blogspot.ro/2009/05/why-bdd-can-it-help-me.html
http://danbunea.blogspot.ro/2008/05/chapter-6-quality-and-testing.html
http://danbunea.blogspot.ro/2005/09/how-tdd-improves-development-speed-and.html
http://danbunea.blogspot.ro/2006/06/to-obtain-good-code-writing-tests-and.html
Monday, October 31, 2011
Wednesday, May 06, 2009
Why BDD? Can it help me?
I've been watching a lot of people arround me and on the internet talking about BDD. Of course as alwars, some were more extremist and said that TDD is dead, and BDD is much better.
Why is BDD needed, in general?
Most people develop code, and design, mainly on the simple tasks, by exploration. They write some code, see if it works, refactor it, make it better, then if it still doesn't work they sit down and think of a better solution. After this 3rd stage the real thinking is needed. This fact comes from human nature and the fact that most of us like to do things with the minimum effort required. Unfortunately, writing tests first, moves the real thinking phase ahead, thus making it more unconfortable, because the first thing you need to do is to think how to design the test that will design your code. This isn't as simple as just starting to type code then seeing if it works. So, conslusion one would be that writing tests first, is harder, because it doen't allow the people to warm up, before being sent onto the pitch.
On the other hand, it seems that there are still lots of people that do not make a clear difference between writing automated tests and Test Driven Development, where the tests are written first, and actually drive the way you're developing the code, making it usually smaller, and less dependent, as a general rule better. This fact has been noticed by some incredibly smart people, and they said maybe TDD is too abstract, and we need a simpler way to show people the philosophy behind it, so we need to simplify it and enforce somehow people to write the tests first, and let the the tests drive the way the code is written. So the second concusion is that, maybe TDD should be simplified, renamed, handed out to the masses, as Dan North (original developer of BDD concept) said:
I had a problem. While using and teaching agile practices like test-driven development (TDD) on projects in different environments, I kept coming across the same confusion and misunderstandings. Programmers wanted to know where to start, what to test and what not to test, how much to test in one go, what to call their tests, and how to understand why a test fails.
Why do I need it?
After practicing TDD for many years, I saw one drawback, that kept coming to me: in time, I forgot what they did, and sometimes I needed lots of time to understand them again. Unfortunately for this there isn't a solution:
- small easy to understand unit test: If you develop lots of small unit tests, lots means that you have to read a lot of them to understand what is there they test. The problem of quantity
- test names very explicit: well, even this sometimes becomes a mess, especially with integration or functional tests, as the names become longer and longer (TestMaskMoveRotateZoomWithUndo() or TestAddMoveRemovePointOnMaskAndCheckDimentionsOnPanelWithUndo() )
- comments: just like any other piece of code, tests need comments, and they seem to be the best solution to understand what they do. But just like code, people either forget about them, write something just to be there, or forget to update the comments when they update the tests or copy paste.
- lack of clear flow in tests: preparation, action and verification phases: any good test has 3 steps. One where the context is prepared, then when something is done, and the third is the verification phase. However, there are tests where these 3 aren't easy to spot, and with integration/functional tests especially, because they tend to take time, there are many cases where some of the steps are repeated (add/edit/delete tests for instance, where there is a preparation phase, then action add, verify, action edit, verify, action delete verify). This makes the tests harder to read.
So what do I do?
It seems that BDD tends to solve all the above problems, changing the naming and making test first easier to understand, and my problem by making tests easier to be understood later. It is not perfect, but definately helps.
But BDD is not just this, BDD is a lot more. BDD for the first time, allows or simplifies the real implementation of the acceptance tests. If you use XP, and you have stories, and for each you define acceptance tests with the customer (see sample http://danbunea.blogspot.com/2008/04/chapter-3-communicating-and.html), which means that both you and him understand them, because they use english and not c, java, ruby or c#, BDD allows you to write them in code very close to the way they are written on the story card. Now this is a breakthough:
Story: import images
The customer will be able to import and process images using the API.
Acceptance test X:
Given an image over 20Mb
When the user imports it
Then the program should reject it for being too big
would be in code:
[Test]
public void ShouldntImportOver20Mb()
{
Image image = null;
ImportResult result = null;
"Given an image over 20Mb".Given(
() =>
{
image = new Image("largeImage.jpg");
});
"When the user imports it".When(
() =>
{
result = Importer.Import(image);
});
"Then the program should reject it for being too big".Then(
() =>
{
Assert.Equal("Image is too big, over 20Mb", result.Message);
});
}
Now this isn't any fancy BDD framework, but a simple string Extention methods, proposed by Mihai Lazar (brilliantly simple idea), which despite the simplicity of the test, allows me to read easily what is going on, see clearly the flow and see the real correspondence with the acceptance test in the story.
Conclusion
The example above is too simple, to allow real advantages over TDD, because as a unit test it would be:
[Test]
public void ShouldntImportOver20Mb()
{
Image image = new Image("largeImage.jpg");
ImportResult result = Importer.Import(image);
Assert.Equal("Image is too big, over 20Mb", result.Message);
}
which is fairly simple to understand, but what if it were more complicated:
[Test]
public void Test2AddMoveRemovePointOnMaskAndPanelDimentionsWithUndo()
{
ViewShape view = DragDropShape();
ShapeEditorControl ctrl = (ShapeEditorControl)view.FindName("shapeEditor");
int pointsBefore = ctrl.CurrentOption.Points[0].Count;
//simulate adding new point near to first point
Point newPoint = new Point(ctrl.CurrentOption.Points[0][0].X + 1, ctrl.CurrentOption.Points[0][0].Y + 5);
ctrl.InsertThumb(newPoint);
Assert.AreEqual(pointsBefore + 1, ctrl.CurrentOption.Points[0].Count);
Assert.AreEqual(1, ctrl.CurrentOption.UndoActionsCount, "added one undo action");
//move
Point pointBefore = new Point(ctrl.CurrentOption.Points[0][0].X, ctrl.CurrentOption.Points[0][0].Y);
Thumb thumb = ctrl.Thumbs[0];
thumb.RaiseEvent(new DragDeltaEventArgs(3, 3));
thumb.RaiseEvent(new DragCompletedEventArgs(1, 1, false));
Assert.AreEqual(pointBefore.X + 3, ctrl.CurrentOption.Points[0][0].X);
Assert.AreEqual(pointBefore.Y + 3, ctrl.CurrentOption.Points[0][0].Y);
Assert.AreEqual(2, ctrl.CurrentOption.UndoActionsCount, "added two undo actions");
ctrl.IsTestMode = true;
//delete
MouseButtonEventArgs mbea = new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left);
mbea.RoutedEvent = Mouse.MouseDownEvent;
ctrl.thumb_MouseDoubleClick(thumb, mbea);
Assert.AreEqual(pointsBefore, ctrl.CurrentOption.Points[0].Count);
Assert.AreEqual(3, ctrl.CurrentOption.UndoActionsCount, "added 3 undo actions");
//Undo
ClickUndo(view);
Assert.AreEqual(pointsBefore + 1, ctrl.CurrentOption.Points[0].Count);
Assert.AreEqual(2, ctrl.CurrentOption.UndoActionsCount, "undone delete");
//undo
ClickUndo(view);
Assert.AreEqual(pointBefore.X, ctrl.CurrentOption.Points[0][0].X);
Assert.AreEqual(pointBefore.Y, ctrl.CurrentOption.Points[0][0].Y);
Assert.AreEqual(1, ctrl.CurrentOption.UndoActionsCount, "undone move");
//undo
ClickUndo(view);
Assert.AreEqual(pointsBefore, ctrl.CurrentOption.Points[0].Count);
Assert.AreEqual(0, ctrl.CurrentOption.UndoActionsCount, "undone add");
}
and now:
[Test]
public void Test2AddMoveRemovePointOnMaskAndPanelDimentionsWithUndo()
{
ViewShape view = null;
ShapeEditorControl ctrl = null;
int pointsBefore = -1;
"Given the countour editor open for an image ".Given(
() =>
{
view = DragDropShape();
ctrl = (ShapeEditorControl)view.FindName("shapeEditor");
int pointsBefore = ctrl.CurrentOption.Points[0].Count;
});
"Adding a new point".When(
() =>
{
Point newPoint = new Point(ctrl.CurrentOption.Points[0][0].X + 1, ctrl.CurrentOption.Points[0][0].Y + 5);
ctrl.InsertThumb(newPoint);
});
"should have one more point, and possibility to undo".Then(
() =>
{
Assert.AreEqual(pointsBefore + 1, ctrl.CurrentOption.Points[0].Count);
Assert.AreEqual(1, ctrl.CurrentOption.UndoActionsCount, "added one undo action");
});
Point pointBefore=null;
"and having a point".When(
() =>
{
pointBefore = new Point(ctrl.CurrentOption.Points[0][0].X, ctrl.CurrentOption.Points[0][0].Y);
});
"when moving it".When(
() =>
{
Thumb thumb = ctrl.Thumbs[0];
thumb.RaiseEvent(new DragDeltaEventArgs(3, 3));
thumb.RaiseEvent(new DragCompletedEventArgs(1, 1, false));
});
"should be in another position".Then(
() =>
{ //move
Assert.AreEqual(pointBefore.X + 3, ctrl.CurrentOption.Points[0][0].X);
Assert.AreEqual(pointBefore.Y + 3, ctrl.CurrentOption.Points[0][0].Y);
Assert.AreEqual(2, ctrl.CurrentOption.UndoActionsCount, "added two undo actions"); });
"when deleting it".When(
() =>
{
....
});
"should be gone".Then(
() =>
{
....
});
"clicking undo".When(
() =>
{
....
});
"should have the deleted point back".Then(
() =>
{
....
});
"re-clicking undo".When(
() =>
{
....
});
"should move the point back at initial position".Then(
() =>
{
....
});
"clicking undo".When(
() =>
{
....
});
"should get rid of the added point".Then(
() =>
{
....
});
}
which now looks like a scenario, making it simpler to follow and understand how the application should behave in this case.
It seems, as a final word, that the first and most productive property of BDD, is that the description of the behavior of the application suddently is in english, and could be read (if you just output the strings on the console) even by the customer. A great leap forward, at least for me.




