diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a7640be --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +Thumbs.db +db.json +debug.log +node_modules/ +public/ +.deploy/ \ No newline at end of file diff --git a/2012/02/07/bootstrapping-a-new-project/index.html b/2012/02/07/bootstrapping-a-new-project/index.html deleted file mode 100644 index 4d5c02e..0000000 --- a/2012/02/07/bootstrapping-a-new-project/index.html +++ /dev/null @@ -1,283 +0,0 @@ - - - - - - Bootstrapping A New Project | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- - -
- - -

- Bootstrapping A New Project -

- - -
- -
- -

My take on: How to give your new project a good starting point

So, you have a new shiny project on your lap, so now what?
Today everyone is talking about “agile”, and how its better then the old “waterfall” methodology of doing things

-

Waterfall

The “Waterfall” approach is notorious in the software community, some highlights of why this is the case:

-
    -
  • It assumes that the initial requirements will not change
  • -
  • There is only one version delivered to the stakeholders, at the end of the process
  • -
  • All the planning and “thinking” happen only in the beginning of the project
  • -
-

Agile

Everyone likes “Agile”, its the “cool” way to manage a project, it asks for less paperwork, less work upfront, built from the ground up to support changes, to name a few conceptions about it.
While I call it “Agile” here, in reality it is a world of sub cultures, some of which are: “XP” (eXtreme Programming), “scrum”, “Kanban” to name a few.
The difference between specific cultures is not always clear, we don’t necessarily have to stick to one, we can mix and match, like I am doing here, thus from this point onward I will just ignorantly call it “Agile” with out specifying the specific methodology.
The main strengths going for the Agile methodology are

-
    -
  • Short upfront work phase (requirements, design ..)
  • -
  • Iterations - project is broken down to deliverables, which is translated to real value delivered very early in the project lifetime
  • -
  • Changes are welcome and expected and are introduced into following iterations (bugs found in previous deliverables, design “inadequacies”)
  • -
-

Doing things in small pieces is great, just ask a WEB developer, how there’s nothing quite like writing a bit of code, refreshing the page and seeing the changes reflected, especially when working on a big complex project. This gives you a feeling of progress and achievement, even though the end goal is far far away..

-

Some Agile approaches talk beyond writing business code and going into proactive bug searching using TDD (Test Driven Development), which turns out to be a good fit to the overall process as it secures quality of delivered pieces of logic.
BDD (Behavior Driven Development) is very similar to TDD, but instead of going after “test coverage” it takes on a higher level, business specification driven approach, where the business embodied user stories are part of the actual test suite.

-

My recipe for success

    -
  • User Stories
  • -
  • Mock-ups (in the case of an app that has UI)
  • -
  • HLD (High Level -system component- Design)
  • -
  • Process Sequence diagram / Workflow diagram (possibly atop HLD)
  • -
-

Tooling

Tools that keep me productive:

-
    -
  • Google Document (User Stories)
  • -
  • Google Drawing (mockups, diagrams, HLD)
  • -
  • Lucidchart (diagrams)
  • -
-

In addition, I use a light weight web based project management tool as a central place to keep reference to important docs and user stories, and see my progress (and others working on the project). A great tool for the job is Trello.com - which free and super simple!

-

REATED UPDATES:

User Stories

- - -
- -
- - - - - -
- - -
-
- -
-
-
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/2012/02/10/setting-up-intellij-for-android-and-scala/index.html b/2012/02/10/setting-up-intellij-for-android-and-scala/index.html deleted file mode 100644 index 87c145c..0000000 --- a/2012/02/10/setting-up-intellij-for-android-and-scala/index.html +++ /dev/null @@ -1,330 +0,0 @@ - - - - - - Setting Up IntelliJ For Android And Scala On Ubuntu | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- - -
- - -

- Setting Up IntelliJ For Android And Scala On Ubuntu -

- - -
- -
- -

Prerequisites

Android SDK

    -
  1. get the latest SDK

    -
  2. -
  3. after extracting, run ./tools/android

    -
  4. -
  5. from the list select the Android version your afer, right now its Android 4.0.3 (API 15) (I also installed Android 2.3.3), click on Install packages to get the installation started

    -
  6. -
  7. setup your env echo "export ANDROID_HOME=~/Applications/android-sdk-linux/" >> ~/.bashrc

    -
  8. -
  9. refresh your currect session source ~/.bashrc

    -
  10. -
-

Adding SBT support to IntelliJ

    -
  1. open File > Settings > Plugin

    -
  2. -
  3. click on Browser repositories

    -
  4. -
  5. find SBT, right click and select Download and install

    -
  6. -
-

Setup SBT

    -
  1. create folder ~/bin/ (if you dont have ~/bin in your PATH, then you can do this: echo "PATH=$PATH:~/bin" >> ~/.bashrc (refresh your currect session source ~/.bashrc)

    -
  2. -
  3. download sbt-launcher.jar and place it under ~/bin

    -
  4. -
  5. create launcher: touch ~/bin/sbt and then echo 'java -Xmx512M -jar `dirname $0`/sbt-launch.jar "$@"' >> ~/bin/sbt

    -
  6. -
  7. make it executable: chmod u+x ~/bin/sbt

    -
  8. -
-

Setup android-plugin for Scala support

    -
  1. install giter8 curl https://raw.github.com/n8han/conscript/master/setup.sh | sh

    -
  2. -
  3. run it ~/bin/cs n8han/giter8

    -
  4. -
-

Setup sbt-idea

    -
  1. create folder (if it doesnt exist) mkdir -p ~/.sbt/plugins/

    -
  2. -
  3. create the file ~/.sbt/plugins/build.sbt and add the following lines to it:

    -
    resolvers += "sbt-idea-repo" at "http://mpeltonen.github.com/maven/"
    -addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.0.0")
    -
  4. -
-

Finally, now that you have all the tools you need, proceed to creating a new project

    -
  1. run the android plugin project setup tool: ~/bin/g8 jberkel/android-app, follow the onscreen questions (or press return for defaults)
    this will create the project folder and create the files needed to build an android project

    -
  2. -
  3. open the new folder cd <project name>

    -
  4. -
  5. setup IntelliJ project support sbt gen-idea

    -
  6. -
  7. open the project in IntelliJ

    -
  8. -
- - -
- -
- - - - - -
- - -
-
- -
-
-
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/2012/02/25/user-stories/index.html b/2012/02/25/user-stories/index.html deleted file mode 100644 index dabc693..0000000 --- a/2012/02/25/user-stories/index.html +++ /dev/null @@ -1,295 +0,0 @@ - - - - - - User Stories | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- - -
- - -

- User Stories -

- - -
- -
- -

This post is a follow-up on “Bootstrapping A New Project”

-

“User Story” is an important communication tool, it is used between you (the developer) and who ever is making the requirements (the stakeholder).
A “contract” which (unlike one written by a lawyer) is clear, understood and agreed upon.
On our side (development) user stories embody a work unit that can be further broken down and estimated.

-

Key value points:

-
    -
  • Language - its key that user stories are written in a language everyone can understand (stakeholders, developers etc..)
  • -
  • Perspective - user stories need to be written from the perspective of the “feature user”, so they represent real world value
  • -
  • Estimation - to help you get better and more precise estimation for the project overall
  • -
  • Prioritization - what needs to be delivered soon/later
  • -
-

Lets imagine we just got a call from the product manager, where he is telling us about this new survey tool:

    -
  1. It needs to collect answers from users via a link sent to our users by e-mail
  2. -
  3. It needs to be summarized and sent to … once a month on the 1st
  4. -
  5. Managers need to be able to see past surveys
  6. -
-

Hmm.. OK, thats pretty clear, but suppose this can be written clearer, lats try using the following template: (not my invention;))
As a [role], I want [Requirement / Feature], So that [Justigication/ Reason]

-

The requirements now look like this:

-
    -
  1. As an end user, I will be able to follow a link I got via e-mail, so that I can fill a survey and express my opinion
  2. -
  3. As a manager, I will receive an e-mail with a digest of surveys, so that I can evaluate performance
  4. -
  5. As a manager, I will be able to access old surveys, so that I can compare and evaluate performance over time
  6. -
-

By reading the above I can clearly understand the required functionality and the motivation behind writing it, this gives me a good starting point.
Since I am the one to implement this, I will need to provide estimates, so that product can plan ahead and communicate the product road map onward.
Unfortunately, “The Devil is In The Details” as they say, so giving estimates based on points 1,2,3 is still difficult.
Points 2 & 3 are “huge” stories, also known as “epics”, we need to further break down these stories, while still using the same language and practices:

-
    -
  1. As an end user, I will receive an email with a link as a result of some communication with a company, so that I can follow it and fill a survey
  2. -
  3. As an end user, I get a set of questions to answer, so that my opinion is collected
  4. -
  5. As a manager, I will receive an e-mail with a digest of surveys, so that I can evaluate companies performance
  6. -
  7. As a manager, I will have a survey management portal, so that I can overview previous results and compare to
  8. -
  9. As a manager, I can select a specific survey, so that I can extract meaningful actionable information
  10. -
-

The functionality expected of deliverables from the points above is clear, and now I feel more comfortable estimating the time it will take me to complete each.
Along with the estimation, these can now be prioritized against each other and against other ongoing projects..

-

Summery

The beginning of a new project is the perfect time to introduce user stories, it will help with getting the conversation going, further clarifying the scope and requirements.
While the project scope might change, user stories are (optimally) modular, decoupled and encapsulated, this should help with managing changes and their effect on the overall project.

- - -
- -
- - - - - -
- - -
-
- -
-
-
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/2012/03/07/hld-high-level-design/index.html b/2012/03/07/hld-high-level-design/index.html deleted file mode 100644 index a85b31d..0000000 --- a/2012/03/07/hld-high-level-design/index.html +++ /dev/null @@ -1,289 +0,0 @@ - - - - - - HLD High Level Design | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- - -
- - -

- HLD High Level Design -

- - -
- -
- -

This post is a follow-up on “Bootstrapping A New Project”

-

Previously…

I’ve written about “User Stories”, a tool that helps with expectation setting when working with (mostly) non-technical stakeholders.

-

Designing Toward

    -
  • database (schema, tables, indexes..)
  • -
  • application layout
  • -
  • deployment
  • -
  • modules
  • -
  • internal and external APIs
  • -
  • application infra (frameworks, tools etc..)
  • -
-

Design Tools At Your Disposal

-

Audiences

    -
  • System Architect - mostly interested in the software stack, deployment and the database design
  • -
  • Team Members - mostly interested in the more lower level stuff like modules and API’s
  • -
  • Other Teams - mostly interested in API’s
  • -
-

Feedback on design is great for everyone involved, setup meetings with relevant parties to do DR (design review)

-

UML and Other Vegetables

UML is complex, you don’t need to know it perfectly, start with what makes sense, and slowly pick up tutorials. Remember, its a visual tool that helps you communicate, not everyone who will look at your designs has the time (or cares for) to read throughly a book about UML and its specifics. In Einstein’s words “Make everything as simple as possible, but not simpler.”

-

Disclosure

Design does not last. Nobody is going to update the design as the system grows and evolves. Having said that, can still be used a good reference.

- - -
- -
- - - - - -
- - -
-
- -
-
-
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/2012/09/22/typing-in-javascript/index.html b/2012/09/22/typing-in-javascript/index.html deleted file mode 100644 index 68e2262..0000000 --- a/2012/09/22/typing-in-javascript/index.html +++ /dev/null @@ -1,290 +0,0 @@ - - - - - - Typing in JavaScript? | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- - -
- - -

- Typing in JavaScript? -

- - -
- -
- -

“Strong typing is for people with weak memories” - not sure who originally said that, but you have got to love the enthusiasm people have for dynamic languges. What is there not to love? powerful basic language constructs such as closure, prototypal inheritance, objects as first class citizens and anonymous functions, mainly, with which you can simulate almost anything static languages have to offer!

-

Proof you say? no problem! here are few implementations that are worthy of noting:

-

Compile time type checking https://developers.google.com/closure/compiler/docs/js-for-compiler#types

-

Classic OO inheritance http://mootools.net/docs/core/Class/Class & http://coffeescript.org/#classes

-

(if you have other interesting examples let me know!)

-

Convention over configuration

The combination of CoffeeScript’s “classical” Class like system and Backbone.JS’s MVC style structure, give (IMHO) the best value in terms of complexity and pure fun writing apps. The challenges I come across while building large complex applications is the inherited nature of a true dynamic language “typelessness“.

-

The ugly monster, that is -a large application written in JavaScript, pushed me into struggling with these two main challenges:

-
    -
  • Many-to-many PubSub communication
  • -
  • 3rd party API’s
  • -
-

Many-to-many PubSub communication

While the PubSub (also known as observer pattern) in JavaScript is a powerful tool to decouple your code, having multiple callers and listeners on the same event makes it really difficult to keep track of the actual common API for these events. Lats try to reflect on this while looking at an actual implementation.

-

(please excuse my CoffeeScript)

-

Pub/Sub implementation in Backbone/Underscore.JS

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
pubsub = _.extend {}, Backbone.Events
pubsub.on 'myEvent', (param1, param2)->
x1 = param1 + param2
# do something with x1

# in some other obscure location in the code
pubsub.on 'myEvent', (param1)->
x2 = param1
# do something with x2

# ...
pubsub.trigger 'myEvent', 5
# ...
pubsub.trigger 'myEvent', 5,10
-

The “pubsub.on(event name, callback)” API is very common amongst different JS pub/sub frameworks. here’s an example of how a generic implementation might look like:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pubsub = {
_listeners : {}
on : (channel, callback)->
@_listeners[ channel ] ?= []
@_listeners[ channel ].push callback

trigger : (channel, params...)->
@_listeners[ channel ]?.forEach (cb)-> cb.apply(null, params)
}

pubsub.on 'myEvent', (param1, param2)->
x1 = param1 + param2
# ... do something with x1..

# in some other obscure location in the code
pubsub.on 'myEvent', (param1)->
x2 = param1
# .... do something with x2

# ...
pubsub.trigger 'myEvent', 5
# ...
pubsub.trigger 'myEvent', 5,10
-

If your using the very popular Socket.IO library, for realtime client/server communication, you are using the exact same API, in which case it makes it even more complicated to keep track of, since the ambiguity is manifested on both the client and the server.

-

3rd party API’s

This is how a type related bug might look like in a dynamic language

-
1
2
3
a = "42" # returned from some API
a == 42
=> false
-

Enter “Contractor” & “Backbone-typed”

Contractor
https://github.com/romansky/Contractor

-

Backbone-typed
https://github.com/romansky/backbone-typed

-

I’m introducing a convention here, that helps keep the API consistent across the application, the approach here is to create wrapper functions, that are used as both the API documentation and run-time validation facility for required and optional arguments.

-

Example:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# declare the events and their APIs
Messages = {
LoginEvent : Contractor.Create( "LoginEvent", Contractor.Required("user ID"), Contractor.Optional("additional info") )
AppErrorEvent : Contractor.Create( "AppErrorEvent", Contractor.Required("user ID"), Contractor.Required("error description"), Contractor.Optional("exception") )
}

# register on event API
pubsub.on Messages.LoginEvent, (userID, info)-> #... do stuff here
pubsub.on Messages.AppErrorEvent, (userID, description, exception)-> #... do stuff here

# trigger the event
pubsub.apply(pubsub, Messages.LoginEvent(112233, "mobile"))
pubsub.apply(pubsub, Messages.LoginEvent(112233))

pubsub.apply(pubsub, Messages.AppErrorEvent(112233, "user did not have access to resource", e))
pubsub.apply(pubsub, Messages.AppErrorEvent(112233)) # since we did not provide the second required argument, this will log an error and return null
-

With NodeJS, I am using the same code on the front and back end, which makes it really easy to manage my Socket.IO communication.

-

If you have been paying attention, there’s still the type issue. Since I work allot with Backbone, it made a lot of sense to write “backbone-typed” which adds optional typing to Backbone-Models.
Example:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Wearing = {
"bracelet" : "bracelet"
"watch" : "watch"
}

class User extends TypedModel
defaults: {
name: null
email: null
lotteryNumber: null
isAwesome: null
}

types: {
name: Types.String
email: Types.String
lotteryNumber: Types.Integer
isAwesome: Types.Boolean
wearing : Types.Enum(Wearing)
}

user1 = new User({name: "foo", email: "foo@bar.com", lotteryNumber: 12345, isAwesome: true, wearing: Wearing.watch})
user1.toJSON() #=> {name: "foo", email: "foo@bar.com", lotteryNumber: 12345, isAwesome: true, wearing: "watch"} - nothing special going on here..

user2 = new User({name: "foo", email: "foo@bar.com", lotteryNumber: "54321", isAwesome: "false", wearing: "thong"})
user2.toJSON() #=> {name: "bar", email: "bar@foo.com", lotteryNumber: 54321, isAwesome: false, wearing: null} - shit happens!

user2.set({wearing: Wearing.bracelet})

if user2.get("lotteryNumber") == 54321 and user2.get("wearing") == Wearing.bracelet then user2.set({isAwesome: "true"})
user2.toJSON() #=> {name: "bar", email: "bar@foo.com", lotteryNumber: 54321, isAwesome: true, wearing: "bracelet"} - awesome for sure..
-

If you want to know a little more about how these work, please visit the respective Git repositories, and let me know what you think!

- - -
- -
- - - - - -
- - -
-
- -
-
-
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/2012/10/06/using-contractor-with-child-processes/index.html b/2012/10/06/using-contractor-with-child-processes/index.html deleted file mode 100644 index 37dae93..0000000 --- a/2012/10/06/using-contractor-with-child-processes/index.html +++ /dev/null @@ -1,277 +0,0 @@ - - - - - - Using Contractor With Child Processes | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- - -
- - -

- Using Contractor With Child Processes -

- - -
- -
- -

Contractor is a factory of contracts, that helps with documenting our APIs.
The child_process facility built into node is using EventEmitter (v1), and does not support callbacks, so it is a great fit for Contractor!
Since I needed it for a distributed worker framework I am working on, I added a helper facility called “Lawyer”, it basically reads the contracts and routes them to the provided object.

-

Let us take the parent and child example code from Node’s official documentation (http://nodejs.org/api/child_process.html) and see how simple it would be to add some documentation.

-

Parent

-
1
2
3
4
5
6
7
8
9
var cp = require('child_process');

var n = cp.fork(__dirname + '/sub.js');

n.on('message', function(m) {
console.log('PARENT got message:', m);
});

n.send({ hello: 'world' });
-

Child

-
1
2
3
4
5
process.on('message', function(m) {
console.log('CHILD got message:', m);
});

process.send({ foo: 'bar' });
-

Node’s child_process facility provied us with a very basic means of communication, we simply call n.send({any: "thing"}) and it automagically recieved by the forked child. Let’s spice it up with a more RPC message passing style, with contracts and lawyers

-

contracts.js

-
1
2
3
4
5
6
7
8
9
10
var Contractor = require('contractor').Contractor;
exports.ChildPublish = {
"GreetingResponse" : Contractor.Create("GreetingResponse", Contractor.Required("childs greeting response")).
"StatusResponse" : Contractor.Create("StatusResponse", Contractor.Required("errors count"), Contractor.Required("number of completed jobs"))
}

exports.ChildSubscribe = {
"Greeting": Contractor.Create("Greeting", Contractor.Required("greeting word")),
"StatusQuery" : Contractor.Create("StatusQuery")
}
-

parent.js

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var contracts = require('./contracts');
var Contractor = require('contractor').Contractor;
var Lawyer = require('contractor').Lawyer;
var cp = require('child_process');

var n = cp.fork(__dirname + '/sub.js');

n.on('message', function(m) {
Lawyer.Read(m, {
"GreetingResponse" : function(childResponse){ console.log("child responded:" + childResponse) },
"StatusResponse" : function(errorCount, numCompletedJobs){ console.log("childs status, errors:"+errorCount+" jobs done:" + numCompletedJobs) }
});
});
n.send(contracts.ChildSubscribe.Greeting("this is your father!"));
n.send(contracts.ChildSubscribe.StatusQuery());
-

sub.js

-
1
2
3
4
5
6
7
8
9
var contracts = require('./contracts');
var Contractor = require('contractor').Contractor;
var Lawyer = require('contractor').Lawyer;
process.on('message', function(m) {
Lawyer.Read(m, {
"Greeting" : function(parentGreeting){ process.send( contracts.ChildPublish.GreetingResponse("Hi "+parentGreeting+" can I have 10$?")) },
"StatusQuery" : function(){ process.send( contracts.ChildPublish.StatusResponse(0, 42) ) }
});
});
-

If we were to run these

-
1
2
3
#> node parent.js
child responded:Hi this is your father! can I have 10$?
childs status, errors:0 jobs done:42
-

If you found Contractor / Lawyer interesting and/or useful, I would love to hear about it!

- - -
- -
- - - - - -
- - -
-
- -
-
-
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/2012/10/20/distributed-scheduled-queue-with-redis/index.html b/2012/10/20/distributed-scheduled-queue-with-redis/index.html deleted file mode 100644 index 540047a..0000000 --- a/2012/10/20/distributed-scheduled-queue-with-redis/index.html +++ /dev/null @@ -1,286 +0,0 @@ - - - - - - Distributed Scheduled Queue With Redis | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- - -
- - -

- Distributed Scheduled Queue With Redis -

- - -
- -
- -

Unlike the classic FIFO queue, jobs are scheduled for execution in the future, possibly on a specific day, week, month year.. similar to “cron” in a Unix OS. Unlike the normal Unix “cron” implementation, we want it to be distributed, so that several machines(workers) can query and then execute these jobs.

-

Implementing

    -
  • Planning - The standart Unix cron format is very flexible and has been time tested for describing schedules
  • -
  • Managing/Coordinating - The queues are stored in Redis Sorted Sets, Lua will be used for atomic execution
  • -
  • Execution - Getting the pieces together
  • -
-

Planning

“Unix Time” format, is the number of milliseconds since (UTC) 1 January 1970
Example (* the actual unix time number is in UTC, while the formatted date is localized)

-
1350745504313 == Sat Oct 20 2012 17:05:04 GMT+0200 (IST)
-

Using this format will make it possible to slice the queue with specific point in time in mind.

-

Managing/Coordinating

Redis’st will be holding the job IDs as members scored by their corresponding execution times(in unix time format).

-

-

Redis commands that will come in handy

-
    -
  • Scheduling job run: ZADD cron:queue 1350745504313 1000
  • -
  • Finding a job to run: ZRANGEBYSCORE cron:queue 1350745504314 LIMIT 0 1
  • -
-

Execution

Without going into any platform specific implementation details, this is what the process looks like

-
    -
  1. When a new schedule is added, jobs are created for upcoming execution times, which are calculated for the next two hours and added to our cron queue
  2. -
  3. A special job is added to be run every hour, it will run over all the schedules, calculate and add their execution time to the scheduled queue, two hours into the future
  4. -
  5. Workers will be polling the database every second to find jobs
  6. -
  7. Once an entry comes up, it is removed from the queue and added to another Sorted Set queue, so we dont “forget” any jobs..
  8. -
-

As for items 3 and 4 above, it is not possible to implement this logic in the client, because for example when we find a job that needs to be executed, another worker might grab it, or worse they both “grabbed” it and are now working on the same job. To work around this, we need to write a little Lua script, since Lua scripts are executed serially, just like any other Redis command

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
local currentTimeMS = KEYS[1]
local workingQueue = "scheduler:working"
local scheduleQueue = "schedule"
-- for easier reading, epoch time is in milliseconds
local second = 1000

local function foundJob(jobID)
-- implement removing the job from all other queues
-- implement updating job status where the actual job object is stored
end

local function findNextJob()
local result
-- check if there are any "stale" jobs
result = redis.call( 'ZRANGEBYSCORE', workingQueue, 0, currentTimeMS-60*second, 'LIMIT',0,1 )
if result[1] ~= nil then return foundJob(result[1]) end
-- check the normal queue
result = redis.call( 'ZRANGEBYSCORE', scheduleQueue, 0, currentTimeMS, 'LIMIT',0,1 )
if result[1] ~= nil then return foundJob(result[1]) end
-- if we didnt find anything return nil
return nil
end

return findNextJob()
-

And there you have it! a “Distributed scheduled queue” implemented on top of Redis.

-

If you are going to implement this with node, I created a little cron format parser you can find here:
JSCron

- - -
- -
- - - - - -
- - -
-
- -
-
-
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/2012/11/14/making-the-case-for-kotlin/index.html b/2012/11/14/making-the-case-for-kotlin/index.html deleted file mode 100644 index 6969604..0000000 --- a/2012/11/14/making-the-case-for-kotlin/index.html +++ /dev/null @@ -1,305 +0,0 @@ - - - - - - Making The Case For Kotlin | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- - -
- - -

- Making The Case For Kotlin -

- - -
- -
- -

The Kotlin project page has this to say about Kotlin: “is a statically typed programming language that compiles to JVM byte codes and JavaScript.” project page

-

Kotlin is a very interesting project, I wanted to check it out, so I decided to write this blog post exploring some design aspects behind Kotlin, and specifically how it compares to the leading JVM “hacker” language - Scala.

-

Who Is Behind Kotlin?

Even though there are several decent free IDE’s available today (Eclipse, NetBeans, Visual Studio Express..), it (could be) surprising that JetBrains is able to rack in between 199$ for a personal license and up to 699$ for a commercial license (for companies) including 1 year update subscription (I have indeed bought one personal license for my development needs). If you are developing on top of VisualStudio you are (most) probably familiar with the 250$+ plugin ReSharper.

-

I would argue that this makes them a good candidate, with insight into good and bad language features, but lets not get ahead of ourselves and set the expectations too high.

-

Why Another JVM Language?

JVM is a well known platform, its weaknesses and scalability strategies are available and documented well on the web, its being used as classic Java run-time VM and for some other popular languages like Scala, Groovy, Clojure and others. Many modules have been already written in Java, normally JVM run languages are compatible with the existing Java ecosystem.

-

Why Static Typing?

In the old (and still ongoing) battle of dynamic and static languages, the consensus is that dynamic languages are faster and easier way to get things done, on the static languages side they allow programmers harness the power of intelligent IDEs to scale up and maintain their code. Type-inferred languages try to find a middle ground approach so that you don’t need to write so much boilerplate code (cue JAVA) to support the static typing, while having the benefits of a full compile time type enforcement.

-

Finally, Kotlin Vs. Scala - Fight!

Scala today is the fastest growing JVM language and is probably competing for the same crowd as Kotlin, Kotlin put up a comparison page link so we will simply go over the omissions part and try to make sense of it.

-

OMITTED - Implicit conversions, parameters

What is it? Scala implicits is (IMHO) a single biggest weakness in the language, once they are used, along with the ability to boast symbols for syntax, it quickly stops being human readable and more like a DSL. Which requires many trips to the docs or even worse - the source code.

-

SBT (Scala Build Tool) is the Maven/Ant of the Scala world link, its very difficult to get things done with it without many trips to google/documentation

-
1
2
3
4
5
lazy val pricing = Project (
"pricing",
file ("cdap2-pricing"),
settings = buildSettings ++ Seq (libraryDependencies ++= pricingDeps)
) dependsOn (common, compact, server)
-

Lift Is a popular web framework, here’s a snippet I took from one of its documentation pages link (see the "#result *" #> x part)

-
1
2
3
4
5
6
7
8
9
def render = inputParam match {
case Full(x) =>
println("Input is: "+x)
"#result *" #> x

case _ =>
println("No input present! Rendering input form HTML")
PassThru
}
-

Thoughts - Indeed for many people this is the part that makes Kotlin a better “scalable” language, by not allowing programmers and libraries hinder the code unreadable without an IDE

-

OMITTED - Overridable Type Members

What is it? Type members in Scala allow you to manage local generic types by assign a type to a class/abstract class/trait member

-
1
2
3
abstract class HasTypedMember {
type typeMe
}
-

An extending class will provide a specific type for this type type member

-
1
2
3
class ExtendingClass extends HasTypedMember {
type typeMe = Int
}
-

Thoughts - Frankly, its not really clear why Kotlin decided to omit having typed members, I could be missing something.. Never the less Kotlin usually takes the minimalistic and simplistic route, my guess is that if this is indeed useful, the door is open to add it later..

-

OMITTED - Path-dependent types

What is it? Pointing to a class under another class in Java would mean a specific type (Parent.Child), well not so in Scala. Scala makes the nested class of two same class instances different, much like you would expect two members of two different instances of a single class. Confused? here is some code to explain this better

-

Java:

-
1
2
3
4
5
6
7
8
9
10
11
12
public class Outer {
public class Inner {}
public void useInner(Outer.Inner inner){...}
}
//.. somewhere in the code
o1 = new Outer()
o2 = new Outer()
i1 = new o1.inner()
i2 = new o2.inner()
// this would work fine
o1.useInner(i2) // works
o2.useInner(i1) // works
-

Scala:

1
2
3
4
5
6
7
8
9
10
11
12
class Outer {
class Inner {}
def useInner(inner : Inner){...}
}
//.. somewhere in the code..
val o1 = new Outer
val o2 = new Outer
val i1 = new o1.Inner
val i2 = new o2.Inner
o1.useInner(i1) // works fine
o2.useInner(i2) // works fine
o1.useInner(i2) // THROWS type mismatch exception!

-

Thoughts - It makes some sense that the path to a type that can only exist in an instance is enforced as such.. It would be interesting to know why Kotlin decided to implement this feature in a similar way to Java (conservative?)

-

OMITTED - Existential types

What is it? This is yet another trade-off for Scala to be able to inter-op with Java
Let’s see an example:

-
1
2
3
4
5
// Create a case class with generic type
case class Z[T]( blah:T )
// create an instance and cast it to a type created with existential type annotation
Seq( Z[Int]( 5 ), Z[String]("blah") ) : Seq[Z[X] forSome { type X >: String with Int }]
res1 = Seq[Z[_ >: String with Int]] = List(Z(5), Z(blah))
-

Thoughts - Scala supports type variance to describe generic type bounds, Scala designer (Odersky) chose to add existential types support due to three main reasons link: erasure, raw types (lack of generics) and Java’s “wildcard” types.

-

Kotlin took the more “purist” approach (also known as reified types) of dropping support for erasure, raw types, wildcard types and finally existential types.

-

OMITTED - Complicated logic for initialization of traits

What is it? Traits are similar to abstract classes, except that you may use several traits on the same class at the same time (like interfaces).

-

In Scala, traits are constructed before the extended classes

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
trait Averager {
val first : Int
val second : Int
val avg : Int = first / second
}
// if we were to naively construct a class like so:
class Naive extends Averager {
val first = 20
val second = 10
}
// create instance
> new Naive()
java.lang.ArithmeticException: / by zero
// what happened was that our val "avg" was computed at construction time before seeing the values we put in Naive
-

Alternatively the trait can be used with a pre-initialized anonymous class val res = new { val first = 20 ; val second = 10 } with Averager, also the val which depends on other vals (avg in our case) can be made “lazy” with the (obvious) lazy keyword lazy val avg : Int = first / second.

-

As a side note, using def for “avg” could work, but it would mean evaluating whatever is defined under “avg” every-time this method is called.

-

Thoughts - Traits in Scala can have state which could be made tricky. If you read this blog post link you will see Kotlin developers thoughts and a nice insight into what went into designing the inheritance in Kotlin. (don’t forget to read the comments!)

-

OMITTED - Custom symbolic operations

What is it? Scala does not have a built in handling of symbols (like =,+,/ etc), they are actually method calls, as you can have symbols used for method names.

-

Thoughts - This may sound useful at first, but can and (arguably) will create very confusing looking code, like we saw before in the Lift and SBT examples.

-

OMITTED - Built-in XML

What is it? Exactly as it sounds, Scala built in XML native support, so you can do stuff like this

-
1
val holdsXml = <outer><inner>some text</inner></outer>
-

Thoughts - Having a built in support for XML as a language feature is interesting, and sometimes useful. An interview link with Odersky reveals that he would consider removing the native XML support in Scala if had written Scala now.

-

Bottom Line

Kotlin is an idiomatic language that draws from the many good features in todays leading languages, specifically from Scala which is a leading language on the JVM platform today. We’ve looked into omissions on Kotlin’s part versus Scala

-

I’ve very much enjoyed writing this blog post and learning about the evolution that the design of Java/Scala/Kotlin languages, and the choices the respective authors made along the way.

-

To get a better feeling of Kotlin I might pick it up for a small project, at which point I will surely blog about it some more..

- - -
- -
- - - - - -
- - -
-
- -
-
-
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/2014/06/14/Using-Spray-to-mock-3rd-party-APIs-in-your-tests/index.html b/2014/06/14/Using-Spray-to-mock-3rd-party-APIs-in-your-tests/index.html deleted file mode 100644 index 6de2ec4..0000000 --- a/2014/06/14/Using-Spray-to-mock-3rd-party-APIs-in-your-tests/index.html +++ /dev/null @@ -1,266 +0,0 @@ - - - - - - Using Spray to mock 3rd party APIs in your tests | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- - -
- - -

- Using Spray to mock 3rd party APIs in your tests -

- - -
- -
- -

If you need to test/mock 3rd party API’s / a crawler / external web service, and you dont want to use some fancy DI framework (I hate the magic in DI frameworks, I use constructor DI instead..), anyway, here’s a helper class I put togather to that end

-

If you haven’t got a chance to try out Spray, you totally should, its a high performance web server built on top of Netty and Akka

-

Spray has a very nice HTTP API. Few things I liked:

-
    -
  • HTTP method mapping spray.http.HttpMethods - GET, POST etc..
  • -
  • HttpRequest and HttpResopnse are basically wrappers around data you would expect to have in a request and response data
  • -
  • spray.http.Uri wrapper class around what you would expect to find in the URI
  • -
  • it really is just an Akka Actor, with just the right amount of control exposed and the rest well hidden behind its nice APIs
  • -
-

So, for testing/mocking all I really need is:

-
    -
  • HTTP method
  • -
  • URI
  • -
  • body(content)
  • -
-

Here’s an implementation of the helper class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import akka.actor._
import akka.io.IO
import java.util.concurrent.atomic.AtomicInteger
import spray.can.Http
import spray.http.{Uri, HttpMethod, HttpResponse, HttpRequest}

object Helpers {

lazy implicit val testSystem = ActorSystem("helpers-test-system")
// used to track the last assigned port
val lastPort = new AtomicInteger(20000)
// a single expected triplet of method+uri+body
type Replay = (HttpMethod,Uri.Path,String)

// factory method to get a replater to play around with
def webReplayer(replay : List[Replay]) : (ActorRef,Int) = {
// everytime the factory is used a new port is assigned,
// so many instances can co-exist and tests can run in parallel without getting
// on eachother's toes
val newPort = lastPort.addAndGet(1)
// instantiate a new Spray server. Yes, its that simple!
val newServer = testSystem.actorOf(Props(new Helpers.ReplayerActor(replay)))
IO(Http) ! Http.Bind(newServer, interface = "127.0.0.1", port = newPort)
// return a pair of the server instance and the assigned port
// the newPort should be used by client code to construct the remote URI with
(newServer,newPort)
}

// object to use to tell the server to shutdown..
object ShutTestServer

// a concrete implementation for a Spray Actor to listen to incoming HttpRequest(..) messages
// provided a list of messages to be matched against arrived and content to be sent back
class ReplayerActor(var replay :List[Replay]) extends Actor {
var ioServer : ActorRef = null
override def receive: Receive = {
case _ : Http.Connected =>
sender ! Http.Register(self)
ioServer = sender()
// this is basically a "catch all" HttpRequest pattern
case HttpRequest(method, uri,_,_,_) =>
// a test to see that this is indeed expected to happen at this point
if (!replay.isEmpty && replay.head._1 == method && uri.path == replay.head._2){
sender ! HttpResponse(entity = replay.head._3)
replay = replay.tail
} else {
sender ! HttpResponse(status = 400, entity = "was expecting a different request at this point")
}
case ShutTestServer => ioServer ! PoisonPill
case _ => //ignore
}
}
}
-

And now a cocrete client code example to use this tool

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// some random content I would like to receive in this scenario (or test..)
val content =
s"""
|<html>
|<head>
|<title>sometitle</title>
|</head>
|<body>some body</body>
|</html>
""".stripMargin

// we use the factory method to get an instance of "replayer" to play around with
val (_testWebServer,port) = Helpers.webReplayer(
List(
(GET,Uri.Path("/somesite"),content)
)
)

// and where ever in your actual code, use the port we got back above and have it "hit" the server
val whatEver = YourHTTPGetter.fetch(s"http://127.0.0.1:$port/somesite")
assert(whatEver == content)
-

Profit!

- - -
- -
- - - - - -
- - -
-
- -
-
-
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..bc06f5b --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +blog +==== + +zblog diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..f4a669b --- /dev/null +++ b/_config.yml @@ -0,0 +1,93 @@ +# Hexo Configuration +## Docs: http://hexo.io/docs/configuration.html +## Source: https://github.com/tommy351/hexo/ + +# Site +title: Uniformlyrandom +subtitle: +description: The land where entropy can not predict the expected stream of random thoughts +author: Roman Landenband +email: roman.land@gmail.com +language: + +# URL +## If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/' +url: http://uniformlyrandom.com +root: / +permalink: :year/:month/:day/:title/ +tag_dir: tags +archive_dir: archives +category_dir: categories +code_dir: downloads/code + +# Directory +source_dir: source +public_dir: public + +# Writing +new_post_name: :title.md # File name of new posts +default_layout: post +auto_spacing: false # Add spaces between asian characters and western characters +titlecase: false # Transform title into titlecase +external_link: true # Open external links in new tab +max_open_file: 100 +multi_thread: true +filename_case: 0 +render_drafts: false +post_asset_folder: false +relative_link: false +highlight: + enable: true + line_number: true + tab_replace: + +# Category & Tag +default_category: uncategorized +category_map: +tag_map: + +# Archives +## 2: Enable pagination +## 1: Disable pagination +## 0: Fully Disable +archive: 2 +category: 2 +tag: 2 + +# Server +## Hexo uses Connect as a server +## You can customize the logger format as defined in +## http://www.senchalabs.org/connect/logger.html +port: 4000 +server_ip: 0.0.0.0 +logger: true +logger_format: + +# Date / Time format +## Hexo uses Moment.js to parse and display date +## You can customize the date format as defined in +## http://momentjs.com/docs/#/displaying/format/ +date_format: MMM D YYYY +time_format: H:mm:ss + +# Pagination +## Set per_page to 0 to disable pagination +per_page: 10 +pagination_dir: page + +# Disqus +disqus_shortname: romanskys-blog + +# Extensions +## Plugins: https://github.com/tommy351/hexo/wiki/Plugins +## Themes: https://github.com/tommy351/hexo/wiki/Themes +theme: landscape +exclude_generator: + +# Deployment +## Docs: http://hexo.io/docs/deployment.html +deploy: + type: git + root: / + repo: git@github.com:uniformlyrandom/blog.git + branch: master diff --git a/archives/2012/02/index.html b/archives/2012/02/index.html deleted file mode 100644 index 4025efc..0000000 --- a/archives/2012/02/index.html +++ /dev/null @@ -1,436 +0,0 @@ - - - - - - Archives: 2012/2 | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- User Stories -

- - -
- -
- -

This post is a follow-up on “Bootstrapping A New Project”

-

“User Story” is an important communication tool, it is used between you (the developer) and who ever is making the requirements (the stakeholder).
A “contract” which (unlike one written by a lawyer) is clear, understood and agreed upon.
On our side (development) user stories embody a work unit that can be further broken down and estimated.

-

Key value points:

-
    -
  • Language - its key that user stories are written in a language everyone can understand (stakeholders, developers etc..)
  • -
  • Perspective - user stories need to be written from the perspective of the “feature user”, so they represent real world value
  • -
  • Estimation - to help you get better and more precise estimation for the project overall
  • -
  • Prioritization - what needs to be delivered soon/later
  • -
-

Lets imagine we just got a call from the product manager, where he is telling us about this new survey tool:

    -
  1. It needs to collect answers from users via a link sent to our users by e-mail
  2. -
  3. It needs to be summarized and sent to … once a month on the 1st
  4. -
  5. Managers need to be able to see past surveys
  6. -
-

Hmm.. OK, thats pretty clear, but suppose this can be written clearer, lats try using the following template: (not my invention;))
As a [role], I want [Requirement / Feature], So that [Justigication/ Reason]

-

The requirements now look like this:

-
    -
  1. As an end user, I will be able to follow a link I got via e-mail, so that I can fill a survey and express my opinion
  2. -
  3. As a manager, I will receive an e-mail with a digest of surveys, so that I can evaluate performance
  4. -
  5. As a manager, I will be able to access old surveys, so that I can compare and evaluate performance over time
  6. -
-

By reading the above I can clearly understand the required functionality and the motivation behind writing it, this gives me a good starting point.
Since I am the one to implement this, I will need to provide estimates, so that product can plan ahead and communicate the product road map onward.
Unfortunately, “The Devil is In The Details” as they say, so giving estimates based on points 1,2,3 is still difficult.
Points 2 & 3 are “huge” stories, also known as “epics”, we need to further break down these stories, while still using the same language and practices:

-
    -
  1. As an end user, I will receive an email with a link as a result of some communication with a company, so that I can follow it and fill a survey
  2. -
  3. As an end user, I get a set of questions to answer, so that my opinion is collected
  4. -
  5. As a manager, I will receive an e-mail with a digest of surveys, so that I can evaluate companies performance
  6. -
  7. As a manager, I will have a survey management portal, so that I can overview previous results and compare to
  8. -
  9. As a manager, I can select a specific survey, so that I can extract meaningful actionable information
  10. -
-

The functionality expected of deliverables from the points above is clear, and now I feel more comfortable estimating the time it will take me to complete each.
Along with the estimation, these can now be prioritized against each other and against other ongoing projects..

-

Summery

The beginning of a new project is the perfect time to introduce user stories, it will help with getting the conversation going, further clarifying the scope and requirements.
While the project scope might change, user stories are (optimally) modular, decoupled and encapsulated, this should help with managing changes and their effect on the overall project.

- - -
- -
- -
- - - -
- -
- - -
- - -

- Setting Up IntelliJ For Android And Scala On Ubuntu -

- - -
- -
- -

Prerequisites

Android SDK

    -
  1. get the latest SDK

    -
  2. -
  3. after extracting, run ./tools/android

    -
  4. -
  5. from the list select the Android version your afer, right now its Android 4.0.3 (API 15) (I also installed Android 2.3.3), click on Install packages to get the installation started

    -
  6. -
  7. setup your env echo "export ANDROID_HOME=~/Applications/android-sdk-linux/" >> ~/.bashrc

    -
  8. -
  9. refresh your currect session source ~/.bashrc

    -
  10. -
-

Adding SBT support to IntelliJ

    -
  1. open File > Settings > Plugin

    -
  2. -
  3. click on Browser repositories

    -
  4. -
  5. find SBT, right click and select Download and install

    -
  6. -
-

Setup SBT

    -
  1. create folder ~/bin/ (if you dont have ~/bin in your PATH, then you can do this: echo "PATH=$PATH:~/bin" >> ~/.bashrc (refresh your currect session source ~/.bashrc)

    -
  2. -
  3. download sbt-launcher.jar and place it under ~/bin

    -
  4. -
  5. create launcher: touch ~/bin/sbt and then echo 'java -Xmx512M -jar `dirname $0`/sbt-launch.jar "$@"' >> ~/bin/sbt

    -
  6. -
  7. make it executable: chmod u+x ~/bin/sbt

    -
  8. -
-

Setup android-plugin for Scala support

    -
  1. install giter8 curl https://raw.github.com/n8han/conscript/master/setup.sh | sh

    -
  2. -
  3. run it ~/bin/cs n8han/giter8

    -
  4. -
-

Setup sbt-idea

    -
  1. create folder (if it doesnt exist) mkdir -p ~/.sbt/plugins/

    -
  2. -
  3. create the file ~/.sbt/plugins/build.sbt and add the following lines to it:

    -
    resolvers += "sbt-idea-repo" at "http://mpeltonen.github.com/maven/"
    -addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.0.0")
    -
  4. -
-

Finally, now that you have all the tools you need, proceed to creating a new project

    -
  1. run the android plugin project setup tool: ~/bin/g8 jberkel/android-app, follow the onscreen questions (or press return for defaults)
    this will create the project folder and create the files needed to build an android project

    -
  2. -
  3. open the new folder cd <project name>

    -
  4. -
  5. setup IntelliJ project support sbt gen-idea

    -
  6. -
  7. open the project in IntelliJ

    -
  8. -
- - -
- -
- -
- - - -
- -
- - -
- - -

- Bootstrapping A New Project -

- - -
- -
- -

My take on: How to give your new project a good starting point

So, you have a new shiny project on your lap, so now what?
Today everyone is talking about “agile”, and how its better then the old “waterfall” methodology of doing things

-

Waterfall

The “Waterfall” approach is notorious in the software community, some highlights of why this is the case:

-
    -
  • It assumes that the initial requirements will not change
  • -
  • There is only one version delivered to the stakeholders, at the end of the process
  • -
  • All the planning and “thinking” happen only in the beginning of the project
  • -
-

Agile

Everyone likes “Agile”, its the “cool” way to manage a project, it asks for less paperwork, less work upfront, built from the ground up to support changes, to name a few conceptions about it.
While I call it “Agile” here, in reality it is a world of sub cultures, some of which are: “XP” (eXtreme Programming), “scrum”, “Kanban” to name a few.
The difference between specific cultures is not always clear, we don’t necessarily have to stick to one, we can mix and match, like I am doing here, thus from this point onward I will just ignorantly call it “Agile” with out specifying the specific methodology.
The main strengths going for the Agile methodology are

-
    -
  • Short upfront work phase (requirements, design ..)
  • -
  • Iterations - project is broken down to deliverables, which is translated to real value delivered very early in the project lifetime
  • -
  • Changes are welcome and expected and are introduced into following iterations (bugs found in previous deliverables, design “inadequacies”)
  • -
-

Doing things in small pieces is great, just ask a WEB developer, how there’s nothing quite like writing a bit of code, refreshing the page and seeing the changes reflected, especially when working on a big complex project. This gives you a feeling of progress and achievement, even though the end goal is far far away..

-

Some Agile approaches talk beyond writing business code and going into proactive bug searching using TDD (Test Driven Development), which turns out to be a good fit to the overall process as it secures quality of delivered pieces of logic.
BDD (Behavior Driven Development) is very similar to TDD, but instead of going after “test coverage” it takes on a higher level, business specification driven approach, where the business embodied user stories are part of the actual test suite.

-

My recipe for success

    -
  • User Stories
  • -
  • Mock-ups (in the case of an app that has UI)
  • -
  • HLD (High Level -system component- Design)
  • -
  • Process Sequence diagram / Workflow diagram (possibly atop HLD)
  • -
-

Tooling

Tools that keep me productive:

-
    -
  • Google Document (User Stories)
  • -
  • Google Drawing (mockups, diagrams, HLD)
  • -
  • Lucidchart (diagrams)
  • -
-

In addition, I use a light weight web based project management tool as a central place to keep reference to important docs and user stories, and see my progress (and others working on the project). A great tool for the job is Trello.com - which free and super simple!

-

REATED UPDATES:

User Stories

- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/archives/2012/03/index.html b/archives/2012/03/index.html deleted file mode 100644 index 78fbf0b..0000000 --- a/archives/2012/03/index.html +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - Archives: 2012/3 | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- HLD High Level Design -

- - -
- -
- -

This post is a follow-up on “Bootstrapping A New Project”

-

Previously…

I’ve written about “User Stories”, a tool that helps with expectation setting when working with (mostly) non-technical stakeholders.

-

Designing Toward

    -
  • database (schema, tables, indexes..)
  • -
  • application layout
  • -
  • deployment
  • -
  • modules
  • -
  • internal and external APIs
  • -
  • application infra (frameworks, tools etc..)
  • -
-

Design Tools At Your Disposal

-

Audiences

    -
  • System Architect - mostly interested in the software stack, deployment and the database design
  • -
  • Team Members - mostly interested in the more lower level stuff like modules and API’s
  • -
  • Other Teams - mostly interested in API’s
  • -
-

Feedback on design is great for everyone involved, setup meetings with relevant parties to do DR (design review)

-

UML and Other Vegetables

UML is complex, you don’t need to know it perfectly, start with what makes sense, and slowly pick up tutorials. Remember, its a visual tool that helps you communicate, not everyone who will look at your designs has the time (or cares for) to read throughly a book about UML and its specifics. In Einstein’s words “Make everything as simple as possible, but not simpler.”

-

Disclosure

Design does not last. Nobody is going to update the design as the system grows and evolves. Having said that, can still be used a good reference.

- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/archives/2012/09/index.html b/archives/2012/09/index.html deleted file mode 100644 index 34d45a2..0000000 --- a/archives/2012/09/index.html +++ /dev/null @@ -1,265 +0,0 @@ - - - - - - Archives: 2012/9 | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- Typing in JavaScript? -

- - -
- -
- -

“Strong typing is for people with weak memories” - not sure who originally said that, but you have got to love the enthusiasm people have for dynamic languges. What is there not to love? powerful basic language constructs such as closure, prototypal inheritance, objects as first class citizens and anonymous functions, mainly, with which you can simulate almost anything static languages have to offer!

-

Proof you say? no problem! here are few implementations that are worthy of noting:

-

Compile time type checking https://developers.google.com/closure/compiler/docs/js-for-compiler#types

-

Classic OO inheritance http://mootools.net/docs/core/Class/Class & http://coffeescript.org/#classes

-

(if you have other interesting examples let me know!)

-

Convention over configuration

The combination of CoffeeScript’s “classical” Class like system and Backbone.JS’s MVC style structure, give (IMHO) the best value in terms of complexity and pure fun writing apps. The challenges I come across while building large complex applications is the inherited nature of a true dynamic language “typelessness“.

-

The ugly monster, that is -a large application written in JavaScript, pushed me into struggling with these two main challenges:

-
    -
  • Many-to-many PubSub communication
  • -
  • 3rd party API’s
  • -
-

Many-to-many PubSub communication

While the PubSub (also known as observer pattern) in JavaScript is a powerful tool to decouple your code, having multiple callers and listeners on the same event makes it really difficult to keep track of the actual common API for these events. Lats try to reflect on this while looking at an actual implementation.

-

(please excuse my CoffeeScript)

-

Pub/Sub implementation in Backbone/Underscore.JS

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
pubsub = _.extend {}, Backbone.Events
pubsub.on 'myEvent', (param1, param2)->
x1 = param1 + param2
# do something with x1

# in some other obscure location in the code
pubsub.on 'myEvent', (param1)->
x2 = param1
# do something with x2

# ...
pubsub.trigger 'myEvent', 5
# ...
pubsub.trigger 'myEvent', 5,10
-

The “pubsub.on(event name, callback)” API is very common amongst different JS pub/sub frameworks. here’s an example of how a generic implementation might look like:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pubsub = {
_listeners : {}
on : (channel, callback)->
@_listeners[ channel ] ?= []
@_listeners[ channel ].push callback

trigger : (channel, params...)->
@_listeners[ channel ]?.forEach (cb)-> cb.apply(null, params)
}

pubsub.on 'myEvent', (param1, param2)->
x1 = param1 + param2
# ... do something with x1..

# in some other obscure location in the code
pubsub.on 'myEvent', (param1)->
x2 = param1
# .... do something with x2

# ...
pubsub.trigger 'myEvent', 5
# ...
pubsub.trigger 'myEvent', 5,10
-

If your using the very popular Socket.IO library, for realtime client/server communication, you are using the exact same API, in which case it makes it even more complicated to keep track of, since the ambiguity is manifested on both the client and the server.

-

3rd party API’s

This is how a type related bug might look like in a dynamic language

-
1
2
3
a = "42" # returned from some API
a == 42
=> false
-

Enter “Contractor” & “Backbone-typed”

Contractor
https://github.com/romansky/Contractor

-

Backbone-typed
https://github.com/romansky/backbone-typed

-

I’m introducing a convention here, that helps keep the API consistent across the application, the approach here is to create wrapper functions, that are used as both the API documentation and run-time validation facility for required and optional arguments.

-

Example:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# declare the events and their APIs
Messages = {
LoginEvent : Contractor.Create( "LoginEvent", Contractor.Required("user ID"), Contractor.Optional("additional info") )
AppErrorEvent : Contractor.Create( "AppErrorEvent", Contractor.Required("user ID"), Contractor.Required("error description"), Contractor.Optional("exception") )
}

# register on event API
pubsub.on Messages.LoginEvent, (userID, info)-> #... do stuff here
pubsub.on Messages.AppErrorEvent, (userID, description, exception)-> #... do stuff here

# trigger the event
pubsub.apply(pubsub, Messages.LoginEvent(112233, "mobile"))
pubsub.apply(pubsub, Messages.LoginEvent(112233))

pubsub.apply(pubsub, Messages.AppErrorEvent(112233, "user did not have access to resource", e))
pubsub.apply(pubsub, Messages.AppErrorEvent(112233)) # since we did not provide the second required argument, this will log an error and return null
-

With NodeJS, I am using the same code on the front and back end, which makes it really easy to manage my Socket.IO communication.

-

If you have been paying attention, there’s still the type issue. Since I work allot with Backbone, it made a lot of sense to write “backbone-typed” which adds optional typing to Backbone-Models.
Example:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Wearing = {
"bracelet" : "bracelet"
"watch" : "watch"
}

class User extends TypedModel
defaults: {
name: null
email: null
lotteryNumber: null
isAwesome: null
}

types: {
name: Types.String
email: Types.String
lotteryNumber: Types.Integer
isAwesome: Types.Boolean
wearing : Types.Enum(Wearing)
}

user1 = new User({name: "foo", email: "foo@bar.com", lotteryNumber: 12345, isAwesome: true, wearing: Wearing.watch})
user1.toJSON() #=> {name: "foo", email: "foo@bar.com", lotteryNumber: 12345, isAwesome: true, wearing: "watch"} - nothing special going on here..

user2 = new User({name: "foo", email: "foo@bar.com", lotteryNumber: "54321", isAwesome: "false", wearing: "thong"})
user2.toJSON() #=> {name: "bar", email: "bar@foo.com", lotteryNumber: 54321, isAwesome: false, wearing: null} - shit happens!

user2.set({wearing: Wearing.bracelet})

if user2.get("lotteryNumber") == 54321 and user2.get("wearing") == Wearing.bracelet then user2.set({isAwesome: "true"})
user2.toJSON() #=> {name: "bar", email: "bar@foo.com", lotteryNumber: 54321, isAwesome: true, wearing: "bracelet"} - awesome for sure..
-

If you want to know a little more about how these work, please visit the respective Git repositories, and let me know what you think!

- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/archives/2012/10/index.html b/archives/2012/10/index.html deleted file mode 100644 index 932045a..0000000 --- a/archives/2012/10/index.html +++ /dev/null @@ -1,319 +0,0 @@ - - - - - - Archives: 2012/10 | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- Distributed Scheduled Queue With Redis -

- - -
- -
- -

Unlike the classic FIFO queue, jobs are scheduled for execution in the future, possibly on a specific day, week, month year.. similar to “cron” in a Unix OS. Unlike the normal Unix “cron” implementation, we want it to be distributed, so that several machines(workers) can query and then execute these jobs.

-

Implementing

    -
  • Planning - The standart Unix cron format is very flexible and has been time tested for describing schedules
  • -
  • Managing/Coordinating - The queues are stored in Redis Sorted Sets, Lua will be used for atomic execution
  • -
  • Execution - Getting the pieces together
  • -
-

Planning

“Unix Time” format, is the number of milliseconds since (UTC) 1 January 1970
Example (* the actual unix time number is in UTC, while the formatted date is localized)

-
1350745504313 == Sat Oct 20 2012 17:05:04 GMT+0200 (IST)
-

Using this format will make it possible to slice the queue with specific point in time in mind.

-

Managing/Coordinating

Redis’st will be holding the job IDs as members scored by their corresponding execution times(in unix time format).

-

-

Redis commands that will come in handy

-
    -
  • Scheduling job run: ZADD cron:queue 1350745504313 1000
  • -
  • Finding a job to run: ZRANGEBYSCORE cron:queue 1350745504314 LIMIT 0 1
  • -
-

Execution

Without going into any platform specific implementation details, this is what the process looks like

-
    -
  1. When a new schedule is added, jobs are created for upcoming execution times, which are calculated for the next two hours and added to our cron queue
  2. -
  3. A special job is added to be run every hour, it will run over all the schedules, calculate and add their execution time to the scheduled queue, two hours into the future
  4. -
  5. Workers will be polling the database every second to find jobs
  6. -
  7. Once an entry comes up, it is removed from the queue and added to another Sorted Set queue, so we dont “forget” any jobs..
  8. -
-

As for items 3 and 4 above, it is not possible to implement this logic in the client, because for example when we find a job that needs to be executed, another worker might grab it, or worse they both “grabbed” it and are now working on the same job. To work around this, we need to write a little Lua script, since Lua scripts are executed serially, just like any other Redis command

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
local currentTimeMS = KEYS[1]
local workingQueue = "scheduler:working"
local scheduleQueue = "schedule"
-- for easier reading, epoch time is in milliseconds
local second = 1000

local function foundJob(jobID)
-- implement removing the job from all other queues
-- implement updating job status where the actual job object is stored
end

local function findNextJob()
local result
-- check if there are any "stale" jobs
result = redis.call( 'ZRANGEBYSCORE', workingQueue, 0, currentTimeMS-60*second, 'LIMIT',0,1 )
if result[1] ~= nil then return foundJob(result[1]) end
-- check the normal queue
result = redis.call( 'ZRANGEBYSCORE', scheduleQueue, 0, currentTimeMS, 'LIMIT',0,1 )
if result[1] ~= nil then return foundJob(result[1]) end
-- if we didnt find anything return nil
return nil
end

return findNextJob()
-

And there you have it! a “Distributed scheduled queue” implemented on top of Redis.

-

If you are going to implement this with node, I created a little cron format parser you can find here:
JSCron

- - -
- -
- -
- - - -
- -
- - -
- - -

- Using Contractor With Child Processes -

- - -
- -
- -

Contractor is a factory of contracts, that helps with documenting our APIs.
The child_process facility built into node is using EventEmitter (v1), and does not support callbacks, so it is a great fit for Contractor!
Since I needed it for a distributed worker framework I am working on, I added a helper facility called “Lawyer”, it basically reads the contracts and routes them to the provided object.

-

Let us take the parent and child example code from Node’s official documentation (http://nodejs.org/api/child_process.html) and see how simple it would be to add some documentation.

-

Parent

-
1
2
3
4
5
6
7
8
9
var cp = require('child_process');

var n = cp.fork(__dirname + '/sub.js');

n.on('message', function(m) {
console.log('PARENT got message:', m);
});

n.send({ hello: 'world' });
-

Child

-
1
2
3
4
5
process.on('message', function(m) {
console.log('CHILD got message:', m);
});

process.send({ foo: 'bar' });
-

Node’s child_process facility provied us with a very basic means of communication, we simply call n.send({any: "thing"}) and it automagically recieved by the forked child. Let’s spice it up with a more RPC message passing style, with contracts and lawyers

-

contracts.js

-
1
2
3
4
5
6
7
8
9
10
var Contractor = require('contractor').Contractor;
exports.ChildPublish = {
"GreetingResponse" : Contractor.Create("GreetingResponse", Contractor.Required("childs greeting response")).
"StatusResponse" : Contractor.Create("StatusResponse", Contractor.Required("errors count"), Contractor.Required("number of completed jobs"))
}

exports.ChildSubscribe = {
"Greeting": Contractor.Create("Greeting", Contractor.Required("greeting word")),
"StatusQuery" : Contractor.Create("StatusQuery")
}
-

parent.js

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var contracts = require('./contracts');
var Contractor = require('contractor').Contractor;
var Lawyer = require('contractor').Lawyer;
var cp = require('child_process');

var n = cp.fork(__dirname + '/sub.js');

n.on('message', function(m) {
Lawyer.Read(m, {
"GreetingResponse" : function(childResponse){ console.log("child responded:" + childResponse) },
"StatusResponse" : function(errorCount, numCompletedJobs){ console.log("childs status, errors:"+errorCount+" jobs done:" + numCompletedJobs) }
});
});
n.send(contracts.ChildSubscribe.Greeting("this is your father!"));
n.send(contracts.ChildSubscribe.StatusQuery());
-

sub.js

-
1
2
3
4
5
6
7
8
9
var contracts = require('./contracts');
var Contractor = require('contractor').Contractor;
var Lawyer = require('contractor').Lawyer;
process.on('message', function(m) {
Lawyer.Read(m, {
"Greeting" : function(parentGreeting){ process.send( contracts.ChildPublish.GreetingResponse("Hi "+parentGreeting+" can I have 10$?")) },
"StatusQuery" : function(){ process.send( contracts.ChildPublish.StatusResponse(0, 42) ) }
});
});
-

If we were to run these

-
1
2
3
#> node parent.js
child responded:Hi this is your father! can I have 10$?
childs status, errors:0 jobs done:42
-

If you found Contractor / Lawyer interesting and/or useful, I would love to hear about it!

- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/archives/2012/11/index.html b/archives/2012/11/index.html deleted file mode 100644 index 0bc49a8..0000000 --- a/archives/2012/11/index.html +++ /dev/null @@ -1,277 +0,0 @@ - - - - - - Archives: 2012/11 | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- Making The Case For Kotlin -

- - -
- -
- -

The Kotlin project page has this to say about Kotlin: “is a statically typed programming language that compiles to JVM byte codes and JavaScript.” project page

-

Kotlin is a very interesting project, I wanted to check it out, so I decided to write this blog post exploring some design aspects behind Kotlin, and specifically how it compares to the leading JVM “hacker” language - Scala.

-

Who Is Behind Kotlin?

Even though there are several decent free IDE’s available today (Eclipse, NetBeans, Visual Studio Express..), it (could be) surprising that JetBrains is able to rack in between 199$ for a personal license and up to 699$ for a commercial license (for companies) including 1 year update subscription (I have indeed bought one personal license for my development needs). If you are developing on top of VisualStudio you are (most) probably familiar with the 250$+ plugin ReSharper.

-

I would argue that this makes them a good candidate, with insight into good and bad language features, but lets not get ahead of ourselves and set the expectations too high.

-

Why Another JVM Language?

JVM is a well known platform, its weaknesses and scalability strategies are available and documented well on the web, its being used as classic Java run-time VM and for some other popular languages like Scala, Groovy, Clojure and others. Many modules have been already written in Java, normally JVM run languages are compatible with the existing Java ecosystem.

-

Why Static Typing?

In the old (and still ongoing) battle of dynamic and static languages, the consensus is that dynamic languages are faster and easier way to get things done, on the static languages side they allow programmers harness the power of intelligent IDEs to scale up and maintain their code. Type-inferred languages try to find a middle ground approach so that you don’t need to write so much boilerplate code (cue JAVA) to support the static typing, while having the benefits of a full compile time type enforcement.

-

Finally, Kotlin Vs. Scala - Fight!

Scala today is the fastest growing JVM language and is probably competing for the same crowd as Kotlin, Kotlin put up a comparison page link so we will simply go over the omissions part and try to make sense of it.

-

OMITTED - Implicit conversions, parameters

What is it? Scala implicits is (IMHO) a single biggest weakness in the language, once they are used, along with the ability to boast symbols for syntax, it quickly stops being human readable and more like a DSL. Which requires many trips to the docs or even worse - the source code.

-

SBT (Scala Build Tool) is the Maven/Ant of the Scala world link, its very difficult to get things done with it without many trips to google/documentation

-
1
2
3
4
5
lazy val pricing = Project (
"pricing",
file ("cdap2-pricing"),
settings = buildSettings ++ Seq (libraryDependencies ++= pricingDeps)
) dependsOn (common, compact, server)
-

Lift Is a popular web framework, here’s a snippet I took from one of its documentation pages link (see the "#result *" #> x part)

-
1
2
3
4
5
6
7
8
9
def render = inputParam match {
case Full(x) =>
println("Input is: "+x)
"#result *" #> x

case _ =>
println("No input present! Rendering input form HTML")
PassThru
}
-

Thoughts - Indeed for many people this is the part that makes Kotlin a better “scalable” language, by not allowing programmers and libraries hinder the code unreadable without an IDE

-

OMITTED - Overridable Type Members

What is it? Type members in Scala allow you to manage local generic types by assign a type to a class/abstract class/trait member

-
1
2
3
abstract class HasTypedMember {
type typeMe
}
-

An extending class will provide a specific type for this type type member

-
1
2
3
class ExtendingClass extends HasTypedMember {
type typeMe = Int
}
-

Thoughts - Frankly, its not really clear why Kotlin decided to omit having typed members, I could be missing something.. Never the less Kotlin usually takes the minimalistic and simplistic route, my guess is that if this is indeed useful, the door is open to add it later..

-

OMITTED - Path-dependent types

What is it? Pointing to a class under another class in Java would mean a specific type (Parent.Child), well not so in Scala. Scala makes the nested class of two same class instances different, much like you would expect two members of two different instances of a single class. Confused? here is some code to explain this better

-

Java:

-
1
2
3
4
5
6
7
8
9
10
11
12
public class Outer {
public class Inner {}
public void useInner(Outer.Inner inner){...}
}
//.. somewhere in the code
o1 = new Outer()
o2 = new Outer()
i1 = new o1.inner()
i2 = new o2.inner()
// this would work fine
o1.useInner(i2) // works
o2.useInner(i1) // works
-

Scala:

1
2
3
4
5
6
7
8
9
10
11
12
class Outer {
class Inner {}
def useInner(inner : Inner){...}
}
//.. somewhere in the code..
val o1 = new Outer
val o2 = new Outer
val i1 = new o1.Inner
val i2 = new o2.Inner
o1.useInner(i1) // works fine
o2.useInner(i2) // works fine
o1.useInner(i2) // THROWS type mismatch exception!

-

Thoughts - It makes some sense that the path to a type that can only exist in an instance is enforced as such.. It would be interesting to know why Kotlin decided to implement this feature in a similar way to Java (conservative?)

-

OMITTED - Existential types

What is it? This is yet another trade-off for Scala to be able to inter-op with Java
Let’s see an example:

-
1
2
3
4
5
// Create a case class with generic type
case class Z[T]( blah:T )
// create an instance and cast it to a type created with existential type annotation
Seq( Z[Int]( 5 ), Z[String]("blah") ) : Seq[Z[X] forSome { type X >: String with Int }]
res1 = Seq[Z[_ >: String with Int]] = List(Z(5), Z(blah))
-

Thoughts - Scala supports type variance to describe generic type bounds, Scala designer (Odersky) chose to add existential types support due to three main reasons link: erasure, raw types (lack of generics) and Java’s “wildcard” types.

-

Kotlin took the more “purist” approach (also known as reified types) of dropping support for erasure, raw types, wildcard types and finally existential types.

-

OMITTED - Complicated logic for initialization of traits

What is it? Traits are similar to abstract classes, except that you may use several traits on the same class at the same time (like interfaces).

-

In Scala, traits are constructed before the extended classes

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
trait Averager {
val first : Int
val second : Int
val avg : Int = first / second
}
// if we were to naively construct a class like so:
class Naive extends Averager {
val first = 20
val second = 10
}
// create instance
> new Naive()
java.lang.ArithmeticException: / by zero
// what happened was that our val "avg" was computed at construction time before seeing the values we put in Naive
-

Alternatively the trait can be used with a pre-initialized anonymous class val res = new { val first = 20 ; val second = 10 } with Averager, also the val which depends on other vals (avg in our case) can be made “lazy” with the (obvious) lazy keyword lazy val avg : Int = first / second.

-

As a side note, using def for “avg” could work, but it would mean evaluating whatever is defined under “avg” every-time this method is called.

-

Thoughts - Traits in Scala can have state which could be made tricky. If you read this blog post link you will see Kotlin developers thoughts and a nice insight into what went into designing the inheritance in Kotlin. (don’t forget to read the comments!)

-

OMITTED - Custom symbolic operations

What is it? Scala does not have a built in handling of symbols (like =,+,/ etc), they are actually method calls, as you can have symbols used for method names.

-

Thoughts - This may sound useful at first, but can and (arguably) will create very confusing looking code, like we saw before in the Lift and SBT examples.

-

OMITTED - Built-in XML

What is it? Exactly as it sounds, Scala built in XML native support, so you can do stuff like this

-
1
val holdsXml = <outer><inner>some text</inner></outer>
-

Thoughts - Having a built in support for XML as a language feature is interesting, and sometimes useful. An interview link with Odersky reveals that he would consider removing the native XML support in Scala if had written Scala now.

-

Bottom Line

Kotlin is an idiomatic language that draws from the many good features in todays leading languages, specifically from Scala which is a leading language on the JVM platform today. We’ve looked into omissions on Kotlin’s part versus Scala

-

I’ve very much enjoyed writing this blog post and learning about the evolution that the design of Java/Scala/Kotlin languages, and the choices the respective authors made along the way.

-

To get a better feeling of Kotlin I might pick it up for a small project, at which point I will surely blog about it some more..

- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/archives/2012/index.html b/archives/2012/index.html deleted file mode 100644 index 169c75e..0000000 --- a/archives/2012/index.html +++ /dev/null @@ -1,790 +0,0 @@ - - - - - - Archives: 2012 | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- Making The Case For Kotlin -

- - -
- -
- -

The Kotlin project page has this to say about Kotlin: “is a statically typed programming language that compiles to JVM byte codes and JavaScript.” project page

-

Kotlin is a very interesting project, I wanted to check it out, so I decided to write this blog post exploring some design aspects behind Kotlin, and specifically how it compares to the leading JVM “hacker” language - Scala.

-

Who Is Behind Kotlin?

Even though there are several decent free IDE’s available today (Eclipse, NetBeans, Visual Studio Express..), it (could be) surprising that JetBrains is able to rack in between 199$ for a personal license and up to 699$ for a commercial license (for companies) including 1 year update subscription (I have indeed bought one personal license for my development needs). If you are developing on top of VisualStudio you are (most) probably familiar with the 250$+ plugin ReSharper.

-

I would argue that this makes them a good candidate, with insight into good and bad language features, but lets not get ahead of ourselves and set the expectations too high.

-

Why Another JVM Language?

JVM is a well known platform, its weaknesses and scalability strategies are available and documented well on the web, its being used as classic Java run-time VM and for some other popular languages like Scala, Groovy, Clojure and others. Many modules have been already written in Java, normally JVM run languages are compatible with the existing Java ecosystem.

-

Why Static Typing?

In the old (and still ongoing) battle of dynamic and static languages, the consensus is that dynamic languages are faster and easier way to get things done, on the static languages side they allow programmers harness the power of intelligent IDEs to scale up and maintain their code. Type-inferred languages try to find a middle ground approach so that you don’t need to write so much boilerplate code (cue JAVA) to support the static typing, while having the benefits of a full compile time type enforcement.

-

Finally, Kotlin Vs. Scala - Fight!

Scala today is the fastest growing JVM language and is probably competing for the same crowd as Kotlin, Kotlin put up a comparison page link so we will simply go over the omissions part and try to make sense of it.

-

OMITTED - Implicit conversions, parameters

What is it? Scala implicits is (IMHO) a single biggest weakness in the language, once they are used, along with the ability to boast symbols for syntax, it quickly stops being human readable and more like a DSL. Which requires many trips to the docs or even worse - the source code.

-

SBT (Scala Build Tool) is the Maven/Ant of the Scala world link, its very difficult to get things done with it without many trips to google/documentation

-
1
2
3
4
5
lazy val pricing = Project (
"pricing",
file ("cdap2-pricing"),
settings = buildSettings ++ Seq (libraryDependencies ++= pricingDeps)
) dependsOn (common, compact, server)
-

Lift Is a popular web framework, here’s a snippet I took from one of its documentation pages link (see the "#result *" #> x part)

-
1
2
3
4
5
6
7
8
9
def render = inputParam match {
case Full(x) =>
println("Input is: "+x)
"#result *" #> x

case _ =>
println("No input present! Rendering input form HTML")
PassThru
}
-

Thoughts - Indeed for many people this is the part that makes Kotlin a better “scalable” language, by not allowing programmers and libraries hinder the code unreadable without an IDE

-

OMITTED - Overridable Type Members

What is it? Type members in Scala allow you to manage local generic types by assign a type to a class/abstract class/trait member

-
1
2
3
abstract class HasTypedMember {
type typeMe
}
-

An extending class will provide a specific type for this type type member

-
1
2
3
class ExtendingClass extends HasTypedMember {
type typeMe = Int
}
-

Thoughts - Frankly, its not really clear why Kotlin decided to omit having typed members, I could be missing something.. Never the less Kotlin usually takes the minimalistic and simplistic route, my guess is that if this is indeed useful, the door is open to add it later..

-

OMITTED - Path-dependent types

What is it? Pointing to a class under another class in Java would mean a specific type (Parent.Child), well not so in Scala. Scala makes the nested class of two same class instances different, much like you would expect two members of two different instances of a single class. Confused? here is some code to explain this better

-

Java:

-
1
2
3
4
5
6
7
8
9
10
11
12
public class Outer {
public class Inner {}
public void useInner(Outer.Inner inner){...}
}
//.. somewhere in the code
o1 = new Outer()
o2 = new Outer()
i1 = new o1.inner()
i2 = new o2.inner()
// this would work fine
o1.useInner(i2) // works
o2.useInner(i1) // works
-

Scala:

1
2
3
4
5
6
7
8
9
10
11
12
class Outer {
class Inner {}
def useInner(inner : Inner){...}
}
//.. somewhere in the code..
val o1 = new Outer
val o2 = new Outer
val i1 = new o1.Inner
val i2 = new o2.Inner
o1.useInner(i1) // works fine
o2.useInner(i2) // works fine
o1.useInner(i2) // THROWS type mismatch exception!

-

Thoughts - It makes some sense that the path to a type that can only exist in an instance is enforced as such.. It would be interesting to know why Kotlin decided to implement this feature in a similar way to Java (conservative?)

-

OMITTED - Existential types

What is it? This is yet another trade-off for Scala to be able to inter-op with Java
Let’s see an example:

-
1
2
3
4
5
// Create a case class with generic type
case class Z[T]( blah:T )
// create an instance and cast it to a type created with existential type annotation
Seq( Z[Int]( 5 ), Z[String]("blah") ) : Seq[Z[X] forSome { type X >: String with Int }]
res1 = Seq[Z[_ >: String with Int]] = List(Z(5), Z(blah))
-

Thoughts - Scala supports type variance to describe generic type bounds, Scala designer (Odersky) chose to add existential types support due to three main reasons link: erasure, raw types (lack of generics) and Java’s “wildcard” types.

-

Kotlin took the more “purist” approach (also known as reified types) of dropping support for erasure, raw types, wildcard types and finally existential types.

-

OMITTED - Complicated logic for initialization of traits

What is it? Traits are similar to abstract classes, except that you may use several traits on the same class at the same time (like interfaces).

-

In Scala, traits are constructed before the extended classes

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
trait Averager {
val first : Int
val second : Int
val avg : Int = first / second
}
// if we were to naively construct a class like so:
class Naive extends Averager {
val first = 20
val second = 10
}
// create instance
> new Naive()
java.lang.ArithmeticException: / by zero
// what happened was that our val "avg" was computed at construction time before seeing the values we put in Naive
-

Alternatively the trait can be used with a pre-initialized anonymous class val res = new { val first = 20 ; val second = 10 } with Averager, also the val which depends on other vals (avg in our case) can be made “lazy” with the (obvious) lazy keyword lazy val avg : Int = first / second.

-

As a side note, using def for “avg” could work, but it would mean evaluating whatever is defined under “avg” every-time this method is called.

-

Thoughts - Traits in Scala can have state which could be made tricky. If you read this blog post link you will see Kotlin developers thoughts and a nice insight into what went into designing the inheritance in Kotlin. (don’t forget to read the comments!)

-

OMITTED - Custom symbolic operations

What is it? Scala does not have a built in handling of symbols (like =,+,/ etc), they are actually method calls, as you can have symbols used for method names.

-

Thoughts - This may sound useful at first, but can and (arguably) will create very confusing looking code, like we saw before in the Lift and SBT examples.

-

OMITTED - Built-in XML

What is it? Exactly as it sounds, Scala built in XML native support, so you can do stuff like this

-
1
val holdsXml = <outer><inner>some text</inner></outer>
-

Thoughts - Having a built in support for XML as a language feature is interesting, and sometimes useful. An interview link with Odersky reveals that he would consider removing the native XML support in Scala if had written Scala now.

-

Bottom Line

Kotlin is an idiomatic language that draws from the many good features in todays leading languages, specifically from Scala which is a leading language on the JVM platform today. We’ve looked into omissions on Kotlin’s part versus Scala

-

I’ve very much enjoyed writing this blog post and learning about the evolution that the design of Java/Scala/Kotlin languages, and the choices the respective authors made along the way.

-

To get a better feeling of Kotlin I might pick it up for a small project, at which point I will surely blog about it some more..

- - -
- -
- -
- - - -
- -
- - -
- - -

- Distributed Scheduled Queue With Redis -

- - -
- -
- -

Unlike the classic FIFO queue, jobs are scheduled for execution in the future, possibly on a specific day, week, month year.. similar to “cron” in a Unix OS. Unlike the normal Unix “cron” implementation, we want it to be distributed, so that several machines(workers) can query and then execute these jobs.

-

Implementing

    -
  • Planning - The standart Unix cron format is very flexible and has been time tested for describing schedules
  • -
  • Managing/Coordinating - The queues are stored in Redis Sorted Sets, Lua will be used for atomic execution
  • -
  • Execution - Getting the pieces together
  • -
-

Planning

“Unix Time” format, is the number of milliseconds since (UTC) 1 January 1970
Example (* the actual unix time number is in UTC, while the formatted date is localized)

-
1350745504313 == Sat Oct 20 2012 17:05:04 GMT+0200 (IST)
-

Using this format will make it possible to slice the queue with specific point in time in mind.

-

Managing/Coordinating

Redis’st will be holding the job IDs as members scored by their corresponding execution times(in unix time format).

-

-

Redis commands that will come in handy

-
    -
  • Scheduling job run: ZADD cron:queue 1350745504313 1000
  • -
  • Finding a job to run: ZRANGEBYSCORE cron:queue 1350745504314 LIMIT 0 1
  • -
-

Execution

Without going into any platform specific implementation details, this is what the process looks like

-
    -
  1. When a new schedule is added, jobs are created for upcoming execution times, which are calculated for the next two hours and added to our cron queue
  2. -
  3. A special job is added to be run every hour, it will run over all the schedules, calculate and add their execution time to the scheduled queue, two hours into the future
  4. -
  5. Workers will be polling the database every second to find jobs
  6. -
  7. Once an entry comes up, it is removed from the queue and added to another Sorted Set queue, so we dont “forget” any jobs..
  8. -
-

As for items 3 and 4 above, it is not possible to implement this logic in the client, because for example when we find a job that needs to be executed, another worker might grab it, or worse they both “grabbed” it and are now working on the same job. To work around this, we need to write a little Lua script, since Lua scripts are executed serially, just like any other Redis command

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
local currentTimeMS = KEYS[1]
local workingQueue = "scheduler:working"
local scheduleQueue = "schedule"
-- for easier reading, epoch time is in milliseconds
local second = 1000

local function foundJob(jobID)
-- implement removing the job from all other queues
-- implement updating job status where the actual job object is stored
end

local function findNextJob()
local result
-- check if there are any "stale" jobs
result = redis.call( 'ZRANGEBYSCORE', workingQueue, 0, currentTimeMS-60*second, 'LIMIT',0,1 )
if result[1] ~= nil then return foundJob(result[1]) end
-- check the normal queue
result = redis.call( 'ZRANGEBYSCORE', scheduleQueue, 0, currentTimeMS, 'LIMIT',0,1 )
if result[1] ~= nil then return foundJob(result[1]) end
-- if we didnt find anything return nil
return nil
end

return findNextJob()
-

And there you have it! a “Distributed scheduled queue” implemented on top of Redis.

-

If you are going to implement this with node, I created a little cron format parser you can find here:
JSCron

- - -
- -
- -
- - - -
- -
- - -
- - -

- Using Contractor With Child Processes -

- - -
- -
- -

Contractor is a factory of contracts, that helps with documenting our APIs.
The child_process facility built into node is using EventEmitter (v1), and does not support callbacks, so it is a great fit for Contractor!
Since I needed it for a distributed worker framework I am working on, I added a helper facility called “Lawyer”, it basically reads the contracts and routes them to the provided object.

-

Let us take the parent and child example code from Node’s official documentation (http://nodejs.org/api/child_process.html) and see how simple it would be to add some documentation.

-

Parent

-
1
2
3
4
5
6
7
8
9
var cp = require('child_process');

var n = cp.fork(__dirname + '/sub.js');

n.on('message', function(m) {
console.log('PARENT got message:', m);
});

n.send({ hello: 'world' });
-

Child

-
1
2
3
4
5
process.on('message', function(m) {
console.log('CHILD got message:', m);
});

process.send({ foo: 'bar' });
-

Node’s child_process facility provied us with a very basic means of communication, we simply call n.send({any: "thing"}) and it automagically recieved by the forked child. Let’s spice it up with a more RPC message passing style, with contracts and lawyers

-

contracts.js

-
1
2
3
4
5
6
7
8
9
10
var Contractor = require('contractor').Contractor;
exports.ChildPublish = {
"GreetingResponse" : Contractor.Create("GreetingResponse", Contractor.Required("childs greeting response")).
"StatusResponse" : Contractor.Create("StatusResponse", Contractor.Required("errors count"), Contractor.Required("number of completed jobs"))
}

exports.ChildSubscribe = {
"Greeting": Contractor.Create("Greeting", Contractor.Required("greeting word")),
"StatusQuery" : Contractor.Create("StatusQuery")
}
-

parent.js

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var contracts = require('./contracts');
var Contractor = require('contractor').Contractor;
var Lawyer = require('contractor').Lawyer;
var cp = require('child_process');

var n = cp.fork(__dirname + '/sub.js');

n.on('message', function(m) {
Lawyer.Read(m, {
"GreetingResponse" : function(childResponse){ console.log("child responded:" + childResponse) },
"StatusResponse" : function(errorCount, numCompletedJobs){ console.log("childs status, errors:"+errorCount+" jobs done:" + numCompletedJobs) }
});
});
n.send(contracts.ChildSubscribe.Greeting("this is your father!"));
n.send(contracts.ChildSubscribe.StatusQuery());
-

sub.js

-
1
2
3
4
5
6
7
8
9
var contracts = require('./contracts');
var Contractor = require('contractor').Contractor;
var Lawyer = require('contractor').Lawyer;
process.on('message', function(m) {
Lawyer.Read(m, {
"Greeting" : function(parentGreeting){ process.send( contracts.ChildPublish.GreetingResponse("Hi "+parentGreeting+" can I have 10$?")) },
"StatusQuery" : function(){ process.send( contracts.ChildPublish.StatusResponse(0, 42) ) }
});
});
-

If we were to run these

-
1
2
3
#> node parent.js
child responded:Hi this is your father! can I have 10$?
childs status, errors:0 jobs done:42
-

If you found Contractor / Lawyer interesting and/or useful, I would love to hear about it!

- - -
- -
- -
- - - -
- -
- - -
- - -

- Typing in JavaScript? -

- - -
- -
- -

“Strong typing is for people with weak memories” - not sure who originally said that, but you have got to love the enthusiasm people have for dynamic languges. What is there not to love? powerful basic language constructs such as closure, prototypal inheritance, objects as first class citizens and anonymous functions, mainly, with which you can simulate almost anything static languages have to offer!

-

Proof you say? no problem! here are few implementations that are worthy of noting:

-

Compile time type checking https://developers.google.com/closure/compiler/docs/js-for-compiler#types

-

Classic OO inheritance http://mootools.net/docs/core/Class/Class & http://coffeescript.org/#classes

-

(if you have other interesting examples let me know!)

-

Convention over configuration

The combination of CoffeeScript’s “classical” Class like system and Backbone.JS’s MVC style structure, give (IMHO) the best value in terms of complexity and pure fun writing apps. The challenges I come across while building large complex applications is the inherited nature of a true dynamic language “typelessness“.

-

The ugly monster, that is -a large application written in JavaScript, pushed me into struggling with these two main challenges:

-
    -
  • Many-to-many PubSub communication
  • -
  • 3rd party API’s
  • -
-

Many-to-many PubSub communication

While the PubSub (also known as observer pattern) in JavaScript is a powerful tool to decouple your code, having multiple callers and listeners on the same event makes it really difficult to keep track of the actual common API for these events. Lats try to reflect on this while looking at an actual implementation.

-

(please excuse my CoffeeScript)

-

Pub/Sub implementation in Backbone/Underscore.JS

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
pubsub = _.extend {}, Backbone.Events
pubsub.on 'myEvent', (param1, param2)->
x1 = param1 + param2
# do something with x1

# in some other obscure location in the code
pubsub.on 'myEvent', (param1)->
x2 = param1
# do something with x2

# ...
pubsub.trigger 'myEvent', 5
# ...
pubsub.trigger 'myEvent', 5,10
-

The “pubsub.on(event name, callback)” API is very common amongst different JS pub/sub frameworks. here’s an example of how a generic implementation might look like:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pubsub = {
_listeners : {}
on : (channel, callback)->
@_listeners[ channel ] ?= []
@_listeners[ channel ].push callback

trigger : (channel, params...)->
@_listeners[ channel ]?.forEach (cb)-> cb.apply(null, params)
}

pubsub.on 'myEvent', (param1, param2)->
x1 = param1 + param2
# ... do something with x1..

# in some other obscure location in the code
pubsub.on 'myEvent', (param1)->
x2 = param1
# .... do something with x2

# ...
pubsub.trigger 'myEvent', 5
# ...
pubsub.trigger 'myEvent', 5,10
-

If your using the very popular Socket.IO library, for realtime client/server communication, you are using the exact same API, in which case it makes it even more complicated to keep track of, since the ambiguity is manifested on both the client and the server.

-

3rd party API’s

This is how a type related bug might look like in a dynamic language

-
1
2
3
a = "42" # returned from some API
a == 42
=> false
-

Enter “Contractor” & “Backbone-typed”

Contractor
https://github.com/romansky/Contractor

-

Backbone-typed
https://github.com/romansky/backbone-typed

-

I’m introducing a convention here, that helps keep the API consistent across the application, the approach here is to create wrapper functions, that are used as both the API documentation and run-time validation facility for required and optional arguments.

-

Example:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# declare the events and their APIs
Messages = {
LoginEvent : Contractor.Create( "LoginEvent", Contractor.Required("user ID"), Contractor.Optional("additional info") )
AppErrorEvent : Contractor.Create( "AppErrorEvent", Contractor.Required("user ID"), Contractor.Required("error description"), Contractor.Optional("exception") )
}

# register on event API
pubsub.on Messages.LoginEvent, (userID, info)-> #... do stuff here
pubsub.on Messages.AppErrorEvent, (userID, description, exception)-> #... do stuff here

# trigger the event
pubsub.apply(pubsub, Messages.LoginEvent(112233, "mobile"))
pubsub.apply(pubsub, Messages.LoginEvent(112233))

pubsub.apply(pubsub, Messages.AppErrorEvent(112233, "user did not have access to resource", e))
pubsub.apply(pubsub, Messages.AppErrorEvent(112233)) # since we did not provide the second required argument, this will log an error and return null
-

With NodeJS, I am using the same code on the front and back end, which makes it really easy to manage my Socket.IO communication.

-

If you have been paying attention, there’s still the type issue. Since I work allot with Backbone, it made a lot of sense to write “backbone-typed” which adds optional typing to Backbone-Models.
Example:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Wearing = {
"bracelet" : "bracelet"
"watch" : "watch"
}

class User extends TypedModel
defaults: {
name: null
email: null
lotteryNumber: null
isAwesome: null
}

types: {
name: Types.String
email: Types.String
lotteryNumber: Types.Integer
isAwesome: Types.Boolean
wearing : Types.Enum(Wearing)
}

user1 = new User({name: "foo", email: "foo@bar.com", lotteryNumber: 12345, isAwesome: true, wearing: Wearing.watch})
user1.toJSON() #=> {name: "foo", email: "foo@bar.com", lotteryNumber: 12345, isAwesome: true, wearing: "watch"} - nothing special going on here..

user2 = new User({name: "foo", email: "foo@bar.com", lotteryNumber: "54321", isAwesome: "false", wearing: "thong"})
user2.toJSON() #=> {name: "bar", email: "bar@foo.com", lotteryNumber: 54321, isAwesome: false, wearing: null} - shit happens!

user2.set({wearing: Wearing.bracelet})

if user2.get("lotteryNumber") == 54321 and user2.get("wearing") == Wearing.bracelet then user2.set({isAwesome: "true"})
user2.toJSON() #=> {name: "bar", email: "bar@foo.com", lotteryNumber: 54321, isAwesome: true, wearing: "bracelet"} - awesome for sure..
-

If you want to know a little more about how these work, please visit the respective Git repositories, and let me know what you think!

- - -
- -
- -
- - - -
- -
- - -
- - -

- HLD High Level Design -

- - -
- -
- -

This post is a follow-up on “Bootstrapping A New Project”

-

Previously…

I’ve written about “User Stories”, a tool that helps with expectation setting when working with (mostly) non-technical stakeholders.

-

Designing Toward

    -
  • database (schema, tables, indexes..)
  • -
  • application layout
  • -
  • deployment
  • -
  • modules
  • -
  • internal and external APIs
  • -
  • application infra (frameworks, tools etc..)
  • -
-

Design Tools At Your Disposal

-

Audiences

    -
  • System Architect - mostly interested in the software stack, deployment and the database design
  • -
  • Team Members - mostly interested in the more lower level stuff like modules and API’s
  • -
  • Other Teams - mostly interested in API’s
  • -
-

Feedback on design is great for everyone involved, setup meetings with relevant parties to do DR (design review)

-

UML and Other Vegetables

UML is complex, you don’t need to know it perfectly, start with what makes sense, and slowly pick up tutorials. Remember, its a visual tool that helps you communicate, not everyone who will look at your designs has the time (or cares for) to read throughly a book about UML and its specifics. In Einstein’s words “Make everything as simple as possible, but not simpler.”

-

Disclosure

Design does not last. Nobody is going to update the design as the system grows and evolves. Having said that, can still be used a good reference.

- - -
- -
- -
- - - -
- -
- - -
- - -

- User Stories -

- - -
- -
- -

This post is a follow-up on “Bootstrapping A New Project”

-

“User Story” is an important communication tool, it is used between you (the developer) and who ever is making the requirements (the stakeholder).
A “contract” which (unlike one written by a lawyer) is clear, understood and agreed upon.
On our side (development) user stories embody a work unit that can be further broken down and estimated.

-

Key value points:

-
    -
  • Language - its key that user stories are written in a language everyone can understand (stakeholders, developers etc..)
  • -
  • Perspective - user stories need to be written from the perspective of the “feature user”, so they represent real world value
  • -
  • Estimation - to help you get better and more precise estimation for the project overall
  • -
  • Prioritization - what needs to be delivered soon/later
  • -
-

Lets imagine we just got a call from the product manager, where he is telling us about this new survey tool:

    -
  1. It needs to collect answers from users via a link sent to our users by e-mail
  2. -
  3. It needs to be summarized and sent to … once a month on the 1st
  4. -
  5. Managers need to be able to see past surveys
  6. -
-

Hmm.. OK, thats pretty clear, but suppose this can be written clearer, lats try using the following template: (not my invention;))
As a [role], I want [Requirement / Feature], So that [Justigication/ Reason]

-

The requirements now look like this:

-
    -
  1. As an end user, I will be able to follow a link I got via e-mail, so that I can fill a survey and express my opinion
  2. -
  3. As a manager, I will receive an e-mail with a digest of surveys, so that I can evaluate performance
  4. -
  5. As a manager, I will be able to access old surveys, so that I can compare and evaluate performance over time
  6. -
-

By reading the above I can clearly understand the required functionality and the motivation behind writing it, this gives me a good starting point.
Since I am the one to implement this, I will need to provide estimates, so that product can plan ahead and communicate the product road map onward.
Unfortunately, “The Devil is In The Details” as they say, so giving estimates based on points 1,2,3 is still difficult.
Points 2 & 3 are “huge” stories, also known as “epics”, we need to further break down these stories, while still using the same language and practices:

-
    -
  1. As an end user, I will receive an email with a link as a result of some communication with a company, so that I can follow it and fill a survey
  2. -
  3. As an end user, I get a set of questions to answer, so that my opinion is collected
  4. -
  5. As a manager, I will receive an e-mail with a digest of surveys, so that I can evaluate companies performance
  6. -
  7. As a manager, I will have a survey management portal, so that I can overview previous results and compare to
  8. -
  9. As a manager, I can select a specific survey, so that I can extract meaningful actionable information
  10. -
-

The functionality expected of deliverables from the points above is clear, and now I feel more comfortable estimating the time it will take me to complete each.
Along with the estimation, these can now be prioritized against each other and against other ongoing projects..

-

Summery

The beginning of a new project is the perfect time to introduce user stories, it will help with getting the conversation going, further clarifying the scope and requirements.
While the project scope might change, user stories are (optimally) modular, decoupled and encapsulated, this should help with managing changes and their effect on the overall project.

- - -
- -
- -
- - - -
- -
- - -
- - -

- Setting Up IntelliJ For Android And Scala On Ubuntu -

- - -
- -
- -

Prerequisites

Android SDK

    -
  1. get the latest SDK

    -
  2. -
  3. after extracting, run ./tools/android

    -
  4. -
  5. from the list select the Android version your afer, right now its Android 4.0.3 (API 15) (I also installed Android 2.3.3), click on Install packages to get the installation started

    -
  6. -
  7. setup your env echo "export ANDROID_HOME=~/Applications/android-sdk-linux/" >> ~/.bashrc

    -
  8. -
  9. refresh your currect session source ~/.bashrc

    -
  10. -
-

Adding SBT support to IntelliJ

    -
  1. open File > Settings > Plugin

    -
  2. -
  3. click on Browser repositories

    -
  4. -
  5. find SBT, right click and select Download and install

    -
  6. -
-

Setup SBT

    -
  1. create folder ~/bin/ (if you dont have ~/bin in your PATH, then you can do this: echo "PATH=$PATH:~/bin" >> ~/.bashrc (refresh your currect session source ~/.bashrc)

    -
  2. -
  3. download sbt-launcher.jar and place it under ~/bin

    -
  4. -
  5. create launcher: touch ~/bin/sbt and then echo 'java -Xmx512M -jar `dirname $0`/sbt-launch.jar "$@"' >> ~/bin/sbt

    -
  6. -
  7. make it executable: chmod u+x ~/bin/sbt

    -
  8. -
-

Setup android-plugin for Scala support

    -
  1. install giter8 curl https://raw.github.com/n8han/conscript/master/setup.sh | sh

    -
  2. -
  3. run it ~/bin/cs n8han/giter8

    -
  4. -
-

Setup sbt-idea

    -
  1. create folder (if it doesnt exist) mkdir -p ~/.sbt/plugins/

    -
  2. -
  3. create the file ~/.sbt/plugins/build.sbt and add the following lines to it:

    -
    resolvers += "sbt-idea-repo" at "http://mpeltonen.github.com/maven/"
    -addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.0.0")
    -
  4. -
-

Finally, now that you have all the tools you need, proceed to creating a new project

    -
  1. run the android plugin project setup tool: ~/bin/g8 jberkel/android-app, follow the onscreen questions (or press return for defaults)
    this will create the project folder and create the files needed to build an android project

    -
  2. -
  3. open the new folder cd <project name>

    -
  4. -
  5. setup IntelliJ project support sbt gen-idea

    -
  6. -
  7. open the project in IntelliJ

    -
  8. -
- - -
- -
- -
- - - -
- -
- - -
- - -

- Bootstrapping A New Project -

- - -
- -
- -

My take on: How to give your new project a good starting point

So, you have a new shiny project on your lap, so now what?
Today everyone is talking about “agile”, and how its better then the old “waterfall” methodology of doing things

-

Waterfall

The “Waterfall” approach is notorious in the software community, some highlights of why this is the case:

-
    -
  • It assumes that the initial requirements will not change
  • -
  • There is only one version delivered to the stakeholders, at the end of the process
  • -
  • All the planning and “thinking” happen only in the beginning of the project
  • -
-

Agile

Everyone likes “Agile”, its the “cool” way to manage a project, it asks for less paperwork, less work upfront, built from the ground up to support changes, to name a few conceptions about it.
While I call it “Agile” here, in reality it is a world of sub cultures, some of which are: “XP” (eXtreme Programming), “scrum”, “Kanban” to name a few.
The difference between specific cultures is not always clear, we don’t necessarily have to stick to one, we can mix and match, like I am doing here, thus from this point onward I will just ignorantly call it “Agile” with out specifying the specific methodology.
The main strengths going for the Agile methodology are

-
    -
  • Short upfront work phase (requirements, design ..)
  • -
  • Iterations - project is broken down to deliverables, which is translated to real value delivered very early in the project lifetime
  • -
  • Changes are welcome and expected and are introduced into following iterations (bugs found in previous deliverables, design “inadequacies”)
  • -
-

Doing things in small pieces is great, just ask a WEB developer, how there’s nothing quite like writing a bit of code, refreshing the page and seeing the changes reflected, especially when working on a big complex project. This gives you a feeling of progress and achievement, even though the end goal is far far away..

-

Some Agile approaches talk beyond writing business code and going into proactive bug searching using TDD (Test Driven Development), which turns out to be a good fit to the overall process as it secures quality of delivered pieces of logic.
BDD (Behavior Driven Development) is very similar to TDD, but instead of going after “test coverage” it takes on a higher level, business specification driven approach, where the business embodied user stories are part of the actual test suite.

-

My recipe for success

    -
  • User Stories
  • -
  • Mock-ups (in the case of an app that has UI)
  • -
  • HLD (High Level -system component- Design)
  • -
  • Process Sequence diagram / Workflow diagram (possibly atop HLD)
  • -
-

Tooling

Tools that keep me productive:

-
    -
  • Google Document (User Stories)
  • -
  • Google Drawing (mockups, diagrams, HLD)
  • -
  • Lucidchart (diagrams)
  • -
-

In addition, I use a light weight web based project management tool as a central place to keep reference to important docs and user stories, and see my progress (and others working on the project). A great tool for the job is Trello.com - which free and super simple!

-

REATED UPDATES:

User Stories

- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/archives/2014/06/index.html b/archives/2014/06/index.html deleted file mode 100644 index 6656d5d..0000000 --- a/archives/2014/06/index.html +++ /dev/null @@ -1,250 +0,0 @@ - - - - - - Archives: 2014/6 | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- Using Spray to mock 3rd party APIs in your tests -

- - -
- -
- -

If you need to test/mock 3rd party API’s / a crawler / external web service, and you dont want to use some fancy DI framework (I hate the magic in DI frameworks, I use constructor DI instead..), anyway, here’s a helper class I put togather to that end

-

If you haven’t got a chance to try out Spray, you totally should, its a high performance web server built on top of Netty and Akka

-

Spray has a very nice HTTP API. Few things I liked:

-
    -
  • HTTP method mapping spray.http.HttpMethods - GET, POST etc..
  • -
  • HttpRequest and HttpResopnse are basically wrappers around data you would expect to have in a request and response data
  • -
  • spray.http.Uri wrapper class around what you would expect to find in the URI
  • -
  • it really is just an Akka Actor, with just the right amount of control exposed and the rest well hidden behind its nice APIs
  • -
-

So, for testing/mocking all I really need is:

-
    -
  • HTTP method
  • -
  • URI
  • -
  • body(content)
  • -
-

Here’s an implementation of the helper class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import akka.actor._
import akka.io.IO
import java.util.concurrent.atomic.AtomicInteger
import spray.can.Http
import spray.http.{Uri, HttpMethod, HttpResponse, HttpRequest}

object Helpers {

lazy implicit val testSystem = ActorSystem("helpers-test-system")
// used to track the last assigned port
val lastPort = new AtomicInteger(20000)
// a single expected triplet of method+uri+body
type Replay = (HttpMethod,Uri.Path,String)

// factory method to get a replater to play around with
def webReplayer(replay : List[Replay]) : (ActorRef,Int) = {
// everytime the factory is used a new port is assigned,
// so many instances can co-exist and tests can run in parallel without getting
// on eachother's toes
val newPort = lastPort.addAndGet(1)
// instantiate a new Spray server. Yes, its that simple!
val newServer = testSystem.actorOf(Props(new Helpers.ReplayerActor(replay)))
IO(Http) ! Http.Bind(newServer, interface = "127.0.0.1", port = newPort)
// return a pair of the server instance and the assigned port
// the newPort should be used by client code to construct the remote URI with
(newServer,newPort)
}

// object to use to tell the server to shutdown..
object ShutTestServer

// a concrete implementation for a Spray Actor to listen to incoming HttpRequest(..) messages
// provided a list of messages to be matched against arrived and content to be sent back
class ReplayerActor(var replay :List[Replay]) extends Actor {
var ioServer : ActorRef = null
override def receive: Receive = {
case _ : Http.Connected =>
sender ! Http.Register(self)
ioServer = sender()
// this is basically a "catch all" HttpRequest pattern
case HttpRequest(method, uri,_,_,_) =>
// a test to see that this is indeed expected to happen at this point
if (!replay.isEmpty && replay.head._1 == method && uri.path == replay.head._2){
sender ! HttpResponse(entity = replay.head._3)
replay = replay.tail
} else {
sender ! HttpResponse(status = 400, entity = "was expecting a different request at this point")
}
case ShutTestServer => ioServer ! PoisonPill
case _ => //ignore
}
}
}
-

And now a cocrete client code example to use this tool

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// some random content I would like to receive in this scenario (or test..)
val content =
s"""
|<html>
|<head>
|<title>sometitle</title>
|</head>
|<body>some body</body>
|</html>
""".stripMargin

// we use the factory method to get an instance of "replayer" to play around with
val (_testWebServer,port) = Helpers.webReplayer(
List(
(GET,Uri.Path("/somesite"),content)
)
)

// and where ever in your actual code, use the port we got back above and have it "hit" the server
val whatEver = YourHTTPGetter.fetch(s"http://127.0.0.1:$port/somesite")
assert(whatEver == content)
-

Profit!

- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/archives/2014/index.html b/archives/2014/index.html deleted file mode 100644 index 44e22fb..0000000 --- a/archives/2014/index.html +++ /dev/null @@ -1,250 +0,0 @@ - - - - - - Archives: 2014 | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- Using Spray to mock 3rd party APIs in your tests -

- - -
- -
- -

If you need to test/mock 3rd party API’s / a crawler / external web service, and you dont want to use some fancy DI framework (I hate the magic in DI frameworks, I use constructor DI instead..), anyway, here’s a helper class I put togather to that end

-

If you haven’t got a chance to try out Spray, you totally should, its a high performance web server built on top of Netty and Akka

-

Spray has a very nice HTTP API. Few things I liked:

-
    -
  • HTTP method mapping spray.http.HttpMethods - GET, POST etc..
  • -
  • HttpRequest and HttpResopnse are basically wrappers around data you would expect to have in a request and response data
  • -
  • spray.http.Uri wrapper class around what you would expect to find in the URI
  • -
  • it really is just an Akka Actor, with just the right amount of control exposed and the rest well hidden behind its nice APIs
  • -
-

So, for testing/mocking all I really need is:

-
    -
  • HTTP method
  • -
  • URI
  • -
  • body(content)
  • -
-

Here’s an implementation of the helper class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import akka.actor._
import akka.io.IO
import java.util.concurrent.atomic.AtomicInteger
import spray.can.Http
import spray.http.{Uri, HttpMethod, HttpResponse, HttpRequest}

object Helpers {

lazy implicit val testSystem = ActorSystem("helpers-test-system")
// used to track the last assigned port
val lastPort = new AtomicInteger(20000)
// a single expected triplet of method+uri+body
type Replay = (HttpMethod,Uri.Path,String)

// factory method to get a replater to play around with
def webReplayer(replay : List[Replay]) : (ActorRef,Int) = {
// everytime the factory is used a new port is assigned,
// so many instances can co-exist and tests can run in parallel without getting
// on eachother's toes
val newPort = lastPort.addAndGet(1)
// instantiate a new Spray server. Yes, its that simple!
val newServer = testSystem.actorOf(Props(new Helpers.ReplayerActor(replay)))
IO(Http) ! Http.Bind(newServer, interface = "127.0.0.1", port = newPort)
// return a pair of the server instance and the assigned port
// the newPort should be used by client code to construct the remote URI with
(newServer,newPort)
}

// object to use to tell the server to shutdown..
object ShutTestServer

// a concrete implementation for a Spray Actor to listen to incoming HttpRequest(..) messages
// provided a list of messages to be matched against arrived and content to be sent back
class ReplayerActor(var replay :List[Replay]) extends Actor {
var ioServer : ActorRef = null
override def receive: Receive = {
case _ : Http.Connected =>
sender ! Http.Register(self)
ioServer = sender()
// this is basically a "catch all" HttpRequest pattern
case HttpRequest(method, uri,_,_,_) =>
// a test to see that this is indeed expected to happen at this point
if (!replay.isEmpty && replay.head._1 == method && uri.path == replay.head._2){
sender ! HttpResponse(entity = replay.head._3)
replay = replay.tail
} else {
sender ! HttpResponse(status = 400, entity = "was expecting a different request at this point")
}
case ShutTestServer => ioServer ! PoisonPill
case _ => //ignore
}
}
}
-

And now a cocrete client code example to use this tool

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// some random content I would like to receive in this scenario (or test..)
val content =
s"""
|<html>
|<head>
|<title>sometitle</title>
|</head>
|<body>some body</body>
|</html>
""".stripMargin

// we use the factory method to get an instance of "replayer" to play around with
val (_testWebServer,port) = Helpers.webReplayer(
List(
(GET,Uri.Path("/somesite"),content)
)
)

// and where ever in your actual code, use the port we got back above and have it "hit" the server
val whatEver = YourHTTPGetter.fetch(s"http://127.0.0.1:$port/somesite")
assert(whatEver == content)
-

Profit!

- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/archives/index.html b/archives/index.html deleted file mode 100644 index fc01b41..0000000 --- a/archives/index.html +++ /dev/null @@ -1,848 +0,0 @@ - - - - - - Archives | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- Using Spray to mock 3rd party APIs in your tests -

- - -
- -
- -

If you need to test/mock 3rd party API’s / a crawler / external web service, and you dont want to use some fancy DI framework (I hate the magic in DI frameworks, I use constructor DI instead..), anyway, here’s a helper class I put togather to that end

-

If you haven’t got a chance to try out Spray, you totally should, its a high performance web server built on top of Netty and Akka

-

Spray has a very nice HTTP API. Few things I liked:

-
    -
  • HTTP method mapping spray.http.HttpMethods - GET, POST etc..
  • -
  • HttpRequest and HttpResopnse are basically wrappers around data you would expect to have in a request and response data
  • -
  • spray.http.Uri wrapper class around what you would expect to find in the URI
  • -
  • it really is just an Akka Actor, with just the right amount of control exposed and the rest well hidden behind its nice APIs
  • -
-

So, for testing/mocking all I really need is:

-
    -
  • HTTP method
  • -
  • URI
  • -
  • body(content)
  • -
-

Here’s an implementation of the helper class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import akka.actor._
import akka.io.IO
import java.util.concurrent.atomic.AtomicInteger
import spray.can.Http
import spray.http.{Uri, HttpMethod, HttpResponse, HttpRequest}

object Helpers {

lazy implicit val testSystem = ActorSystem("helpers-test-system")
// used to track the last assigned port
val lastPort = new AtomicInteger(20000)
// a single expected triplet of method+uri+body
type Replay = (HttpMethod,Uri.Path,String)

// factory method to get a replater to play around with
def webReplayer(replay : List[Replay]) : (ActorRef,Int) = {
// everytime the factory is used a new port is assigned,
// so many instances can co-exist and tests can run in parallel without getting
// on eachother's toes
val newPort = lastPort.addAndGet(1)
// instantiate a new Spray server. Yes, its that simple!
val newServer = testSystem.actorOf(Props(new Helpers.ReplayerActor(replay)))
IO(Http) ! Http.Bind(newServer, interface = "127.0.0.1", port = newPort)
// return a pair of the server instance and the assigned port
// the newPort should be used by client code to construct the remote URI with
(newServer,newPort)
}

// object to use to tell the server to shutdown..
object ShutTestServer

// a concrete implementation for a Spray Actor to listen to incoming HttpRequest(..) messages
// provided a list of messages to be matched against arrived and content to be sent back
class ReplayerActor(var replay :List[Replay]) extends Actor {
var ioServer : ActorRef = null
override def receive: Receive = {
case _ : Http.Connected =>
sender ! Http.Register(self)
ioServer = sender()
// this is basically a "catch all" HttpRequest pattern
case HttpRequest(method, uri,_,_,_) =>
// a test to see that this is indeed expected to happen at this point
if (!replay.isEmpty && replay.head._1 == method && uri.path == replay.head._2){
sender ! HttpResponse(entity = replay.head._3)
replay = replay.tail
} else {
sender ! HttpResponse(status = 400, entity = "was expecting a different request at this point")
}
case ShutTestServer => ioServer ! PoisonPill
case _ => //ignore
}
}
}
-

And now a cocrete client code example to use this tool

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// some random content I would like to receive in this scenario (or test..)
val content =
s"""
|<html>
|<head>
|<title>sometitle</title>
|</head>
|<body>some body</body>
|</html>
""".stripMargin

// we use the factory method to get an instance of "replayer" to play around with
val (_testWebServer,port) = Helpers.webReplayer(
List(
(GET,Uri.Path("/somesite"),content)
)
)

// and where ever in your actual code, use the port we got back above and have it "hit" the server
val whatEver = YourHTTPGetter.fetch(s"http://127.0.0.1:$port/somesite")
assert(whatEver == content)
-

Profit!

- - -
- -
- -
- - - -
- -
- - -
- - -

- Making The Case For Kotlin -

- - -
- -
- -

The Kotlin project page has this to say about Kotlin: “is a statically typed programming language that compiles to JVM byte codes and JavaScript.” project page

-

Kotlin is a very interesting project, I wanted to check it out, so I decided to write this blog post exploring some design aspects behind Kotlin, and specifically how it compares to the leading JVM “hacker” language - Scala.

-

Who Is Behind Kotlin?

Even though there are several decent free IDE’s available today (Eclipse, NetBeans, Visual Studio Express..), it (could be) surprising that JetBrains is able to rack in between 199$ for a personal license and up to 699$ for a commercial license (for companies) including 1 year update subscription (I have indeed bought one personal license for my development needs). If you are developing on top of VisualStudio you are (most) probably familiar with the 250$+ plugin ReSharper.

-

I would argue that this makes them a good candidate, with insight into good and bad language features, but lets not get ahead of ourselves and set the expectations too high.

-

Why Another JVM Language?

JVM is a well known platform, its weaknesses and scalability strategies are available and documented well on the web, its being used as classic Java run-time VM and for some other popular languages like Scala, Groovy, Clojure and others. Many modules have been already written in Java, normally JVM run languages are compatible with the existing Java ecosystem.

-

Why Static Typing?

In the old (and still ongoing) battle of dynamic and static languages, the consensus is that dynamic languages are faster and easier way to get things done, on the static languages side they allow programmers harness the power of intelligent IDEs to scale up and maintain their code. Type-inferred languages try to find a middle ground approach so that you don’t need to write so much boilerplate code (cue JAVA) to support the static typing, while having the benefits of a full compile time type enforcement.

-

Finally, Kotlin Vs. Scala - Fight!

Scala today is the fastest growing JVM language and is probably competing for the same crowd as Kotlin, Kotlin put up a comparison page link so we will simply go over the omissions part and try to make sense of it.

-

OMITTED - Implicit conversions, parameters

What is it? Scala implicits is (IMHO) a single biggest weakness in the language, once they are used, along with the ability to boast symbols for syntax, it quickly stops being human readable and more like a DSL. Which requires many trips to the docs or even worse - the source code.

-

SBT (Scala Build Tool) is the Maven/Ant of the Scala world link, its very difficult to get things done with it without many trips to google/documentation

-
1
2
3
4
5
lazy val pricing = Project (
"pricing",
file ("cdap2-pricing"),
settings = buildSettings ++ Seq (libraryDependencies ++= pricingDeps)
) dependsOn (common, compact, server)
-

Lift Is a popular web framework, here’s a snippet I took from one of its documentation pages link (see the "#result *" #> x part)

-
1
2
3
4
5
6
7
8
9
def render = inputParam match {
case Full(x) =>
println("Input is: "+x)
"#result *" #> x

case _ =>
println("No input present! Rendering input form HTML")
PassThru
}
-

Thoughts - Indeed for many people this is the part that makes Kotlin a better “scalable” language, by not allowing programmers and libraries hinder the code unreadable without an IDE

-

OMITTED - Overridable Type Members

What is it? Type members in Scala allow you to manage local generic types by assign a type to a class/abstract class/trait member

-
1
2
3
abstract class HasTypedMember {
type typeMe
}
-

An extending class will provide a specific type for this type type member

-
1
2
3
class ExtendingClass extends HasTypedMember {
type typeMe = Int
}
-

Thoughts - Frankly, its not really clear why Kotlin decided to omit having typed members, I could be missing something.. Never the less Kotlin usually takes the minimalistic and simplistic route, my guess is that if this is indeed useful, the door is open to add it later..

-

OMITTED - Path-dependent types

What is it? Pointing to a class under another class in Java would mean a specific type (Parent.Child), well not so in Scala. Scala makes the nested class of two same class instances different, much like you would expect two members of two different instances of a single class. Confused? here is some code to explain this better

-

Java:

-
1
2
3
4
5
6
7
8
9
10
11
12
public class Outer {
public class Inner {}
public void useInner(Outer.Inner inner){...}
}
//.. somewhere in the code
o1 = new Outer()
o2 = new Outer()
i1 = new o1.inner()
i2 = new o2.inner()
// this would work fine
o1.useInner(i2) // works
o2.useInner(i1) // works
-

Scala:

1
2
3
4
5
6
7
8
9
10
11
12
class Outer {
class Inner {}
def useInner(inner : Inner){...}
}
//.. somewhere in the code..
val o1 = new Outer
val o2 = new Outer
val i1 = new o1.Inner
val i2 = new o2.Inner
o1.useInner(i1) // works fine
o2.useInner(i2) // works fine
o1.useInner(i2) // THROWS type mismatch exception!

-

Thoughts - It makes some sense that the path to a type that can only exist in an instance is enforced as such.. It would be interesting to know why Kotlin decided to implement this feature in a similar way to Java (conservative?)

-

OMITTED - Existential types

What is it? This is yet another trade-off for Scala to be able to inter-op with Java
Let’s see an example:

-
1
2
3
4
5
// Create a case class with generic type
case class Z[T]( blah:T )
// create an instance and cast it to a type created with existential type annotation
Seq( Z[Int]( 5 ), Z[String]("blah") ) : Seq[Z[X] forSome { type X >: String with Int }]
res1 = Seq[Z[_ >: String with Int]] = List(Z(5), Z(blah))
-

Thoughts - Scala supports type variance to describe generic type bounds, Scala designer (Odersky) chose to add existential types support due to three main reasons link: erasure, raw types (lack of generics) and Java’s “wildcard” types.

-

Kotlin took the more “purist” approach (also known as reified types) of dropping support for erasure, raw types, wildcard types and finally existential types.

-

OMITTED - Complicated logic for initialization of traits

What is it? Traits are similar to abstract classes, except that you may use several traits on the same class at the same time (like interfaces).

-

In Scala, traits are constructed before the extended classes

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
trait Averager {
val first : Int
val second : Int
val avg : Int = first / second
}
// if we were to naively construct a class like so:
class Naive extends Averager {
val first = 20
val second = 10
}
// create instance
> new Naive()
java.lang.ArithmeticException: / by zero
// what happened was that our val "avg" was computed at construction time before seeing the values we put in Naive
-

Alternatively the trait can be used with a pre-initialized anonymous class val res = new { val first = 20 ; val second = 10 } with Averager, also the val which depends on other vals (avg in our case) can be made “lazy” with the (obvious) lazy keyword lazy val avg : Int = first / second.

-

As a side note, using def for “avg” could work, but it would mean evaluating whatever is defined under “avg” every-time this method is called.

-

Thoughts - Traits in Scala can have state which could be made tricky. If you read this blog post link you will see Kotlin developers thoughts and a nice insight into what went into designing the inheritance in Kotlin. (don’t forget to read the comments!)

-

OMITTED - Custom symbolic operations

What is it? Scala does not have a built in handling of symbols (like =,+,/ etc), they are actually method calls, as you can have symbols used for method names.

-

Thoughts - This may sound useful at first, but can and (arguably) will create very confusing looking code, like we saw before in the Lift and SBT examples.

-

OMITTED - Built-in XML

What is it? Exactly as it sounds, Scala built in XML native support, so you can do stuff like this

-
1
val holdsXml = <outer><inner>some text</inner></outer>
-

Thoughts - Having a built in support for XML as a language feature is interesting, and sometimes useful. An interview link with Odersky reveals that he would consider removing the native XML support in Scala if had written Scala now.

-

Bottom Line

Kotlin is an idiomatic language that draws from the many good features in todays leading languages, specifically from Scala which is a leading language on the JVM platform today. We’ve looked into omissions on Kotlin’s part versus Scala

-

I’ve very much enjoyed writing this blog post and learning about the evolution that the design of Java/Scala/Kotlin languages, and the choices the respective authors made along the way.

-

To get a better feeling of Kotlin I might pick it up for a small project, at which point I will surely blog about it some more..

- - -
- -
- -
- - - -
- -
- - -
- - -

- Distributed Scheduled Queue With Redis -

- - -
- -
- -

Unlike the classic FIFO queue, jobs are scheduled for execution in the future, possibly on a specific day, week, month year.. similar to “cron” in a Unix OS. Unlike the normal Unix “cron” implementation, we want it to be distributed, so that several machines(workers) can query and then execute these jobs.

-

Implementing

    -
  • Planning - The standart Unix cron format is very flexible and has been time tested for describing schedules
  • -
  • Managing/Coordinating - The queues are stored in Redis Sorted Sets, Lua will be used for atomic execution
  • -
  • Execution - Getting the pieces together
  • -
-

Planning

“Unix Time” format, is the number of milliseconds since (UTC) 1 January 1970
Example (* the actual unix time number is in UTC, while the formatted date is localized)

-
1350745504313 == Sat Oct 20 2012 17:05:04 GMT+0200 (IST)
-

Using this format will make it possible to slice the queue with specific point in time in mind.

-

Managing/Coordinating

Redis’st will be holding the job IDs as members scored by their corresponding execution times(in unix time format).

-

-

Redis commands that will come in handy

-
    -
  • Scheduling job run: ZADD cron:queue 1350745504313 1000
  • -
  • Finding a job to run: ZRANGEBYSCORE cron:queue 1350745504314 LIMIT 0 1
  • -
-

Execution

Without going into any platform specific implementation details, this is what the process looks like

-
    -
  1. When a new schedule is added, jobs are created for upcoming execution times, which are calculated for the next two hours and added to our cron queue
  2. -
  3. A special job is added to be run every hour, it will run over all the schedules, calculate and add their execution time to the scheduled queue, two hours into the future
  4. -
  5. Workers will be polling the database every second to find jobs
  6. -
  7. Once an entry comes up, it is removed from the queue and added to another Sorted Set queue, so we dont “forget” any jobs..
  8. -
-

As for items 3 and 4 above, it is not possible to implement this logic in the client, because for example when we find a job that needs to be executed, another worker might grab it, or worse they both “grabbed” it and are now working on the same job. To work around this, we need to write a little Lua script, since Lua scripts are executed serially, just like any other Redis command

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
local currentTimeMS = KEYS[1]
local workingQueue = "scheduler:working"
local scheduleQueue = "schedule"
-- for easier reading, epoch time is in milliseconds
local second = 1000

local function foundJob(jobID)
-- implement removing the job from all other queues
-- implement updating job status where the actual job object is stored
end

local function findNextJob()
local result
-- check if there are any "stale" jobs
result = redis.call( 'ZRANGEBYSCORE', workingQueue, 0, currentTimeMS-60*second, 'LIMIT',0,1 )
if result[1] ~= nil then return foundJob(result[1]) end
-- check the normal queue
result = redis.call( 'ZRANGEBYSCORE', scheduleQueue, 0, currentTimeMS, 'LIMIT',0,1 )
if result[1] ~= nil then return foundJob(result[1]) end
-- if we didnt find anything return nil
return nil
end

return findNextJob()
-

And there you have it! a “Distributed scheduled queue” implemented on top of Redis.

-

If you are going to implement this with node, I created a little cron format parser you can find here:
JSCron

- - -
- -
- -
- - - -
- -
- - -
- - -

- Using Contractor With Child Processes -

- - -
- -
- -

Contractor is a factory of contracts, that helps with documenting our APIs.
The child_process facility built into node is using EventEmitter (v1), and does not support callbacks, so it is a great fit for Contractor!
Since I needed it for a distributed worker framework I am working on, I added a helper facility called “Lawyer”, it basically reads the contracts and routes them to the provided object.

-

Let us take the parent and child example code from Node’s official documentation (http://nodejs.org/api/child_process.html) and see how simple it would be to add some documentation.

-

Parent

-
1
2
3
4
5
6
7
8
9
var cp = require('child_process');

var n = cp.fork(__dirname + '/sub.js');

n.on('message', function(m) {
console.log('PARENT got message:', m);
});

n.send({ hello: 'world' });
-

Child

-
1
2
3
4
5
process.on('message', function(m) {
console.log('CHILD got message:', m);
});

process.send({ foo: 'bar' });
-

Node’s child_process facility provied us with a very basic means of communication, we simply call n.send({any: "thing"}) and it automagically recieved by the forked child. Let’s spice it up with a more RPC message passing style, with contracts and lawyers

-

contracts.js

-
1
2
3
4
5
6
7
8
9
10
var Contractor = require('contractor').Contractor;
exports.ChildPublish = {
"GreetingResponse" : Contractor.Create("GreetingResponse", Contractor.Required("childs greeting response")).
"StatusResponse" : Contractor.Create("StatusResponse", Contractor.Required("errors count"), Contractor.Required("number of completed jobs"))
}

exports.ChildSubscribe = {
"Greeting": Contractor.Create("Greeting", Contractor.Required("greeting word")),
"StatusQuery" : Contractor.Create("StatusQuery")
}
-

parent.js

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var contracts = require('./contracts');
var Contractor = require('contractor').Contractor;
var Lawyer = require('contractor').Lawyer;
var cp = require('child_process');

var n = cp.fork(__dirname + '/sub.js');

n.on('message', function(m) {
Lawyer.Read(m, {
"GreetingResponse" : function(childResponse){ console.log("child responded:" + childResponse) },
"StatusResponse" : function(errorCount, numCompletedJobs){ console.log("childs status, errors:"+errorCount+" jobs done:" + numCompletedJobs) }
});
});
n.send(contracts.ChildSubscribe.Greeting("this is your father!"));
n.send(contracts.ChildSubscribe.StatusQuery());
-

sub.js

-
1
2
3
4
5
6
7
8
9
var contracts = require('./contracts');
var Contractor = require('contractor').Contractor;
var Lawyer = require('contractor').Lawyer;
process.on('message', function(m) {
Lawyer.Read(m, {
"Greeting" : function(parentGreeting){ process.send( contracts.ChildPublish.GreetingResponse("Hi "+parentGreeting+" can I have 10$?")) },
"StatusQuery" : function(){ process.send( contracts.ChildPublish.StatusResponse(0, 42) ) }
});
});
-

If we were to run these

-
1
2
3
#> node parent.js
child responded:Hi this is your father! can I have 10$?
childs status, errors:0 jobs done:42
-

If you found Contractor / Lawyer interesting and/or useful, I would love to hear about it!

- - -
- -
- -
- - - -
- -
- - -
- - -

- Typing in JavaScript? -

- - -
- -
- -

“Strong typing is for people with weak memories” - not sure who originally said that, but you have got to love the enthusiasm people have for dynamic languges. What is there not to love? powerful basic language constructs such as closure, prototypal inheritance, objects as first class citizens and anonymous functions, mainly, with which you can simulate almost anything static languages have to offer!

-

Proof you say? no problem! here are few implementations that are worthy of noting:

-

Compile time type checking https://developers.google.com/closure/compiler/docs/js-for-compiler#types

-

Classic OO inheritance http://mootools.net/docs/core/Class/Class & http://coffeescript.org/#classes

-

(if you have other interesting examples let me know!)

-

Convention over configuration

The combination of CoffeeScript’s “classical” Class like system and Backbone.JS’s MVC style structure, give (IMHO) the best value in terms of complexity and pure fun writing apps. The challenges I come across while building large complex applications is the inherited nature of a true dynamic language “typelessness“.

-

The ugly monster, that is -a large application written in JavaScript, pushed me into struggling with these two main challenges:

-
    -
  • Many-to-many PubSub communication
  • -
  • 3rd party API’s
  • -
-

Many-to-many PubSub communication

While the PubSub (also known as observer pattern) in JavaScript is a powerful tool to decouple your code, having multiple callers and listeners on the same event makes it really difficult to keep track of the actual common API for these events. Lats try to reflect on this while looking at an actual implementation.

-

(please excuse my CoffeeScript)

-

Pub/Sub implementation in Backbone/Underscore.JS

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
pubsub = _.extend {}, Backbone.Events
pubsub.on 'myEvent', (param1, param2)->
x1 = param1 + param2
# do something with x1

# in some other obscure location in the code
pubsub.on 'myEvent', (param1)->
x2 = param1
# do something with x2

# ...
pubsub.trigger 'myEvent', 5
# ...
pubsub.trigger 'myEvent', 5,10
-

The “pubsub.on(event name, callback)” API is very common amongst different JS pub/sub frameworks. here’s an example of how a generic implementation might look like:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pubsub = {
_listeners : {}
on : (channel, callback)->
@_listeners[ channel ] ?= []
@_listeners[ channel ].push callback

trigger : (channel, params...)->
@_listeners[ channel ]?.forEach (cb)-> cb.apply(null, params)
}

pubsub.on 'myEvent', (param1, param2)->
x1 = param1 + param2
# ... do something with x1..

# in some other obscure location in the code
pubsub.on 'myEvent', (param1)->
x2 = param1
# .... do something with x2

# ...
pubsub.trigger 'myEvent', 5
# ...
pubsub.trigger 'myEvent', 5,10
-

If your using the very popular Socket.IO library, for realtime client/server communication, you are using the exact same API, in which case it makes it even more complicated to keep track of, since the ambiguity is manifested on both the client and the server.

-

3rd party API’s

This is how a type related bug might look like in a dynamic language

-
1
2
3
a = "42" # returned from some API
a == 42
=> false
-

Enter “Contractor” & “Backbone-typed”

Contractor
https://github.com/romansky/Contractor

-

Backbone-typed
https://github.com/romansky/backbone-typed

-

I’m introducing a convention here, that helps keep the API consistent across the application, the approach here is to create wrapper functions, that are used as both the API documentation and run-time validation facility for required and optional arguments.

-

Example:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# declare the events and their APIs
Messages = {
LoginEvent : Contractor.Create( "LoginEvent", Contractor.Required("user ID"), Contractor.Optional("additional info") )
AppErrorEvent : Contractor.Create( "AppErrorEvent", Contractor.Required("user ID"), Contractor.Required("error description"), Contractor.Optional("exception") )
}

# register on event API
pubsub.on Messages.LoginEvent, (userID, info)-> #... do stuff here
pubsub.on Messages.AppErrorEvent, (userID, description, exception)-> #... do stuff here

# trigger the event
pubsub.apply(pubsub, Messages.LoginEvent(112233, "mobile"))
pubsub.apply(pubsub, Messages.LoginEvent(112233))

pubsub.apply(pubsub, Messages.AppErrorEvent(112233, "user did not have access to resource", e))
pubsub.apply(pubsub, Messages.AppErrorEvent(112233)) # since we did not provide the second required argument, this will log an error and return null
-

With NodeJS, I am using the same code on the front and back end, which makes it really easy to manage my Socket.IO communication.

-

If you have been paying attention, there’s still the type issue. Since I work allot with Backbone, it made a lot of sense to write “backbone-typed” which adds optional typing to Backbone-Models.
Example:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Wearing = {
"bracelet" : "bracelet"
"watch" : "watch"
}

class User extends TypedModel
defaults: {
name: null
email: null
lotteryNumber: null
isAwesome: null
}

types: {
name: Types.String
email: Types.String
lotteryNumber: Types.Integer
isAwesome: Types.Boolean
wearing : Types.Enum(Wearing)
}

user1 = new User({name: "foo", email: "foo@bar.com", lotteryNumber: 12345, isAwesome: true, wearing: Wearing.watch})
user1.toJSON() #=> {name: "foo", email: "foo@bar.com", lotteryNumber: 12345, isAwesome: true, wearing: "watch"} - nothing special going on here..

user2 = new User({name: "foo", email: "foo@bar.com", lotteryNumber: "54321", isAwesome: "false", wearing: "thong"})
user2.toJSON() #=> {name: "bar", email: "bar@foo.com", lotteryNumber: 54321, isAwesome: false, wearing: null} - shit happens!

user2.set({wearing: Wearing.bracelet})

if user2.get("lotteryNumber") == 54321 and user2.get("wearing") == Wearing.bracelet then user2.set({isAwesome: "true"})
user2.toJSON() #=> {name: "bar", email: "bar@foo.com", lotteryNumber: 54321, isAwesome: true, wearing: "bracelet"} - awesome for sure..
-

If you want to know a little more about how these work, please visit the respective Git repositories, and let me know what you think!

- - -
- -
- -
- - - -
- -
- - -
- - -

- HLD High Level Design -

- - -
- -
- -

This post is a follow-up on “Bootstrapping A New Project”

-

Previously…

I’ve written about “User Stories”, a tool that helps with expectation setting when working with (mostly) non-technical stakeholders.

-

Designing Toward

    -
  • database (schema, tables, indexes..)
  • -
  • application layout
  • -
  • deployment
  • -
  • modules
  • -
  • internal and external APIs
  • -
  • application infra (frameworks, tools etc..)
  • -
-

Design Tools At Your Disposal

-

Audiences

    -
  • System Architect - mostly interested in the software stack, deployment and the database design
  • -
  • Team Members - mostly interested in the more lower level stuff like modules and API’s
  • -
  • Other Teams - mostly interested in API’s
  • -
-

Feedback on design is great for everyone involved, setup meetings with relevant parties to do DR (design review)

-

UML and Other Vegetables

UML is complex, you don’t need to know it perfectly, start with what makes sense, and slowly pick up tutorials. Remember, its a visual tool that helps you communicate, not everyone who will look at your designs has the time (or cares for) to read throughly a book about UML and its specifics. In Einstein’s words “Make everything as simple as possible, but not simpler.”

-

Disclosure

Design does not last. Nobody is going to update the design as the system grows and evolves. Having said that, can still be used a good reference.

- - -
- -
- -
- - - -
- -
- - -
- - -

- User Stories -

- - -
- -
- -

This post is a follow-up on “Bootstrapping A New Project”

-

“User Story” is an important communication tool, it is used between you (the developer) and who ever is making the requirements (the stakeholder).
A “contract” which (unlike one written by a lawyer) is clear, understood and agreed upon.
On our side (development) user stories embody a work unit that can be further broken down and estimated.

-

Key value points:

-
    -
  • Language - its key that user stories are written in a language everyone can understand (stakeholders, developers etc..)
  • -
  • Perspective - user stories need to be written from the perspective of the “feature user”, so they represent real world value
  • -
  • Estimation - to help you get better and more precise estimation for the project overall
  • -
  • Prioritization - what needs to be delivered soon/later
  • -
-

Lets imagine we just got a call from the product manager, where he is telling us about this new survey tool:

    -
  1. It needs to collect answers from users via a link sent to our users by e-mail
  2. -
  3. It needs to be summarized and sent to … once a month on the 1st
  4. -
  5. Managers need to be able to see past surveys
  6. -
-

Hmm.. OK, thats pretty clear, but suppose this can be written clearer, lats try using the following template: (not my invention;))
As a [role], I want [Requirement / Feature], So that [Justigication/ Reason]

-

The requirements now look like this:

-
    -
  1. As an end user, I will be able to follow a link I got via e-mail, so that I can fill a survey and express my opinion
  2. -
  3. As a manager, I will receive an e-mail with a digest of surveys, so that I can evaluate performance
  4. -
  5. As a manager, I will be able to access old surveys, so that I can compare and evaluate performance over time
  6. -
-

By reading the above I can clearly understand the required functionality and the motivation behind writing it, this gives me a good starting point.
Since I am the one to implement this, I will need to provide estimates, so that product can plan ahead and communicate the product road map onward.
Unfortunately, “The Devil is In The Details” as they say, so giving estimates based on points 1,2,3 is still difficult.
Points 2 & 3 are “huge” stories, also known as “epics”, we need to further break down these stories, while still using the same language and practices:

-
    -
  1. As an end user, I will receive an email with a link as a result of some communication with a company, so that I can follow it and fill a survey
  2. -
  3. As an end user, I get a set of questions to answer, so that my opinion is collected
  4. -
  5. As a manager, I will receive an e-mail with a digest of surveys, so that I can evaluate companies performance
  6. -
  7. As a manager, I will have a survey management portal, so that I can overview previous results and compare to
  8. -
  9. As a manager, I can select a specific survey, so that I can extract meaningful actionable information
  10. -
-

The functionality expected of deliverables from the points above is clear, and now I feel more comfortable estimating the time it will take me to complete each.
Along with the estimation, these can now be prioritized against each other and against other ongoing projects..

-

Summery

The beginning of a new project is the perfect time to introduce user stories, it will help with getting the conversation going, further clarifying the scope and requirements.
While the project scope might change, user stories are (optimally) modular, decoupled and encapsulated, this should help with managing changes and their effect on the overall project.

- - -
- -
- -
- - - -
- -
- - -
- - -

- Setting Up IntelliJ For Android And Scala On Ubuntu -

- - -
- -
- -

Prerequisites

Android SDK

    -
  1. get the latest SDK

    -
  2. -
  3. after extracting, run ./tools/android

    -
  4. -
  5. from the list select the Android version your afer, right now its Android 4.0.3 (API 15) (I also installed Android 2.3.3), click on Install packages to get the installation started

    -
  6. -
  7. setup your env echo "export ANDROID_HOME=~/Applications/android-sdk-linux/" >> ~/.bashrc

    -
  8. -
  9. refresh your currect session source ~/.bashrc

    -
  10. -
-

Adding SBT support to IntelliJ

    -
  1. open File > Settings > Plugin

    -
  2. -
  3. click on Browser repositories

    -
  4. -
  5. find SBT, right click and select Download and install

    -
  6. -
-

Setup SBT

    -
  1. create folder ~/bin/ (if you dont have ~/bin in your PATH, then you can do this: echo "PATH=$PATH:~/bin" >> ~/.bashrc (refresh your currect session source ~/.bashrc)

    -
  2. -
  3. download sbt-launcher.jar and place it under ~/bin

    -
  4. -
  5. create launcher: touch ~/bin/sbt and then echo 'java -Xmx512M -jar `dirname $0`/sbt-launch.jar "$@"' >> ~/bin/sbt

    -
  6. -
  7. make it executable: chmod u+x ~/bin/sbt

    -
  8. -
-

Setup android-plugin for Scala support

    -
  1. install giter8 curl https://raw.github.com/n8han/conscript/master/setup.sh | sh

    -
  2. -
  3. run it ~/bin/cs n8han/giter8

    -
  4. -
-

Setup sbt-idea

    -
  1. create folder (if it doesnt exist) mkdir -p ~/.sbt/plugins/

    -
  2. -
  3. create the file ~/.sbt/plugins/build.sbt and add the following lines to it:

    -
    resolvers += "sbt-idea-repo" at "http://mpeltonen.github.com/maven/"
    -addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.0.0")
    -
  4. -
-

Finally, now that you have all the tools you need, proceed to creating a new project

    -
  1. run the android plugin project setup tool: ~/bin/g8 jberkel/android-app, follow the onscreen questions (or press return for defaults)
    this will create the project folder and create the files needed to build an android project

    -
  2. -
  3. open the new folder cd <project name>

    -
  4. -
  5. setup IntelliJ project support sbt gen-idea

    -
  6. -
  7. open the project in IntelliJ

    -
  8. -
- - -
- -
- -
- - - -
- -
- - -
- - -

- Bootstrapping A New Project -

- - -
- -
- -

My take on: How to give your new project a good starting point

So, you have a new shiny project on your lap, so now what?
Today everyone is talking about “agile”, and how its better then the old “waterfall” methodology of doing things

-

Waterfall

The “Waterfall” approach is notorious in the software community, some highlights of why this is the case:

-
    -
  • It assumes that the initial requirements will not change
  • -
  • There is only one version delivered to the stakeholders, at the end of the process
  • -
  • All the planning and “thinking” happen only in the beginning of the project
  • -
-

Agile

Everyone likes “Agile”, its the “cool” way to manage a project, it asks for less paperwork, less work upfront, built from the ground up to support changes, to name a few conceptions about it.
While I call it “Agile” here, in reality it is a world of sub cultures, some of which are: “XP” (eXtreme Programming), “scrum”, “Kanban” to name a few.
The difference between specific cultures is not always clear, we don’t necessarily have to stick to one, we can mix and match, like I am doing here, thus from this point onward I will just ignorantly call it “Agile” with out specifying the specific methodology.
The main strengths going for the Agile methodology are

-
    -
  • Short upfront work phase (requirements, design ..)
  • -
  • Iterations - project is broken down to deliverables, which is translated to real value delivered very early in the project lifetime
  • -
  • Changes are welcome and expected and are introduced into following iterations (bugs found in previous deliverables, design “inadequacies”)
  • -
-

Doing things in small pieces is great, just ask a WEB developer, how there’s nothing quite like writing a bit of code, refreshing the page and seeing the changes reflected, especially when working on a big complex project. This gives you a feeling of progress and achievement, even though the end goal is far far away..

-

Some Agile approaches talk beyond writing business code and going into proactive bug searching using TDD (Test Driven Development), which turns out to be a good fit to the overall process as it secures quality of delivered pieces of logic.
BDD (Behavior Driven Development) is very similar to TDD, but instead of going after “test coverage” it takes on a higher level, business specification driven approach, where the business embodied user stories are part of the actual test suite.

-

My recipe for success

    -
  • User Stories
  • -
  • Mock-ups (in the case of an app that has UI)
  • -
  • HLD (High Level -system component- Design)
  • -
  • Process Sequence diagram / Workflow diagram (possibly atop HLD)
  • -
-

Tooling

Tools that keep me productive:

-
    -
  • Google Document (User Stories)
  • -
  • Google Drawing (mockups, diagrams, HLD)
  • -
  • Lucidchart (diagrams)
  • -
-

In addition, I use a light weight web based project management tool as a central place to keep reference to important docs and user stories, and see my progress (and others working on the project). A great tool for the job is Trello.com - which free and super simple!

-

REATED UPDATES:

User Stories

- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/categories/code/index.html b/categories/code/index.html deleted file mode 100644 index 920280f..0000000 --- a/categories/code/index.html +++ /dev/null @@ -1,508 +0,0 @@ - - - - - - Category: code | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- Making The Case For Kotlin -

- - -
- -
- -

The Kotlin project page has this to say about Kotlin: “is a statically typed programming language that compiles to JVM byte codes and JavaScript.” project page

-

Kotlin is a very interesting project, I wanted to check it out, so I decided to write this blog post exploring some design aspects behind Kotlin, and specifically how it compares to the leading JVM “hacker” language - Scala.

-

Who Is Behind Kotlin?

Even though there are several decent free IDE’s available today (Eclipse, NetBeans, Visual Studio Express..), it (could be) surprising that JetBrains is able to rack in between 199$ for a personal license and up to 699$ for a commercial license (for companies) including 1 year update subscription (I have indeed bought one personal license for my development needs). If you are developing on top of VisualStudio you are (most) probably familiar with the 250$+ plugin ReSharper.

-

I would argue that this makes them a good candidate, with insight into good and bad language features, but lets not get ahead of ourselves and set the expectations too high.

-

Why Another JVM Language?

JVM is a well known platform, its weaknesses and scalability strategies are available and documented well on the web, its being used as classic Java run-time VM and for some other popular languages like Scala, Groovy, Clojure and others. Many modules have been already written in Java, normally JVM run languages are compatible with the existing Java ecosystem.

-

Why Static Typing?

In the old (and still ongoing) battle of dynamic and static languages, the consensus is that dynamic languages are faster and easier way to get things done, on the static languages side they allow programmers harness the power of intelligent IDEs to scale up and maintain their code. Type-inferred languages try to find a middle ground approach so that you don’t need to write so much boilerplate code (cue JAVA) to support the static typing, while having the benefits of a full compile time type enforcement.

-

Finally, Kotlin Vs. Scala - Fight!

Scala today is the fastest growing JVM language and is probably competing for the same crowd as Kotlin, Kotlin put up a comparison page link so we will simply go over the omissions part and try to make sense of it.

-

OMITTED - Implicit conversions, parameters

What is it? Scala implicits is (IMHO) a single biggest weakness in the language, once they are used, along with the ability to boast symbols for syntax, it quickly stops being human readable and more like a DSL. Which requires many trips to the docs or even worse - the source code.

-

SBT (Scala Build Tool) is the Maven/Ant of the Scala world link, its very difficult to get things done with it without many trips to google/documentation

-
1
2
3
4
5
lazy val pricing = Project (
"pricing",
file ("cdap2-pricing"),
settings = buildSettings ++ Seq (libraryDependencies ++= pricingDeps)
) dependsOn (common, compact, server)
-

Lift Is a popular web framework, here’s a snippet I took from one of its documentation pages link (see the "#result *" #> x part)

-
1
2
3
4
5
6
7
8
9
def render = inputParam match {
case Full(x) =>
println("Input is: "+x)
"#result *" #> x

case _ =>
println("No input present! Rendering input form HTML")
PassThru
}
-

Thoughts - Indeed for many people this is the part that makes Kotlin a better “scalable” language, by not allowing programmers and libraries hinder the code unreadable without an IDE

-

OMITTED - Overridable Type Members

What is it? Type members in Scala allow you to manage local generic types by assign a type to a class/abstract class/trait member

-
1
2
3
abstract class HasTypedMember {
type typeMe
}
-

An extending class will provide a specific type for this type type member

-
1
2
3
class ExtendingClass extends HasTypedMember {
type typeMe = Int
}
-

Thoughts - Frankly, its not really clear why Kotlin decided to omit having typed members, I could be missing something.. Never the less Kotlin usually takes the minimalistic and simplistic route, my guess is that if this is indeed useful, the door is open to add it later..

-

OMITTED - Path-dependent types

What is it? Pointing to a class under another class in Java would mean a specific type (Parent.Child), well not so in Scala. Scala makes the nested class of two same class instances different, much like you would expect two members of two different instances of a single class. Confused? here is some code to explain this better

-

Java:

-
1
2
3
4
5
6
7
8
9
10
11
12
public class Outer {
public class Inner {}
public void useInner(Outer.Inner inner){...}
}
//.. somewhere in the code
o1 = new Outer()
o2 = new Outer()
i1 = new o1.inner()
i2 = new o2.inner()
// this would work fine
o1.useInner(i2) // works
o2.useInner(i1) // works
-

Scala:

1
2
3
4
5
6
7
8
9
10
11
12
class Outer {
class Inner {}
def useInner(inner : Inner){...}
}
//.. somewhere in the code..
val o1 = new Outer
val o2 = new Outer
val i1 = new o1.Inner
val i2 = new o2.Inner
o1.useInner(i1) // works fine
o2.useInner(i2) // works fine
o1.useInner(i2) // THROWS type mismatch exception!

-

Thoughts - It makes some sense that the path to a type that can only exist in an instance is enforced as such.. It would be interesting to know why Kotlin decided to implement this feature in a similar way to Java (conservative?)

-

OMITTED - Existential types

What is it? This is yet another trade-off for Scala to be able to inter-op with Java
Let’s see an example:

-
1
2
3
4
5
// Create a case class with generic type
case class Z[T]( blah:T )
// create an instance and cast it to a type created with existential type annotation
Seq( Z[Int]( 5 ), Z[String]("blah") ) : Seq[Z[X] forSome { type X >: String with Int }]
res1 = Seq[Z[_ >: String with Int]] = List(Z(5), Z(blah))
-

Thoughts - Scala supports type variance to describe generic type bounds, Scala designer (Odersky) chose to add existential types support due to three main reasons link: erasure, raw types (lack of generics) and Java’s “wildcard” types.

-

Kotlin took the more “purist” approach (also known as reified types) of dropping support for erasure, raw types, wildcard types and finally existential types.

-

OMITTED - Complicated logic for initialization of traits

What is it? Traits are similar to abstract classes, except that you may use several traits on the same class at the same time (like interfaces).

-

In Scala, traits are constructed before the extended classes

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
trait Averager {
val first : Int
val second : Int
val avg : Int = first / second
}
// if we were to naively construct a class like so:
class Naive extends Averager {
val first = 20
val second = 10
}
// create instance
> new Naive()
java.lang.ArithmeticException: / by zero
// what happened was that our val "avg" was computed at construction time before seeing the values we put in Naive
-

Alternatively the trait can be used with a pre-initialized anonymous class val res = new { val first = 20 ; val second = 10 } with Averager, also the val which depends on other vals (avg in our case) can be made “lazy” with the (obvious) lazy keyword lazy val avg : Int = first / second.

-

As a side note, using def for “avg” could work, but it would mean evaluating whatever is defined under “avg” every-time this method is called.

-

Thoughts - Traits in Scala can have state which could be made tricky. If you read this blog post link you will see Kotlin developers thoughts and a nice insight into what went into designing the inheritance in Kotlin. (don’t forget to read the comments!)

-

OMITTED - Custom symbolic operations

What is it? Scala does not have a built in handling of symbols (like =,+,/ etc), they are actually method calls, as you can have symbols used for method names.

-

Thoughts - This may sound useful at first, but can and (arguably) will create very confusing looking code, like we saw before in the Lift and SBT examples.

-

OMITTED - Built-in XML

What is it? Exactly as it sounds, Scala built in XML native support, so you can do stuff like this

-
1
val holdsXml = <outer><inner>some text</inner></outer>
-

Thoughts - Having a built in support for XML as a language feature is interesting, and sometimes useful. An interview link with Odersky reveals that he would consider removing the native XML support in Scala if had written Scala now.

-

Bottom Line

Kotlin is an idiomatic language that draws from the many good features in todays leading languages, specifically from Scala which is a leading language on the JVM platform today. We’ve looked into omissions on Kotlin’s part versus Scala

-

I’ve very much enjoyed writing this blog post and learning about the evolution that the design of Java/Scala/Kotlin languages, and the choices the respective authors made along the way.

-

To get a better feeling of Kotlin I might pick it up for a small project, at which point I will surely blog about it some more..

- - -
- -
- -
- - - -
- -
- - -
- - -

- Using Contractor With Child Processes -

- - -
- -
- -

Contractor is a factory of contracts, that helps with documenting our APIs.
The child_process facility built into node is using EventEmitter (v1), and does not support callbacks, so it is a great fit for Contractor!
Since I needed it for a distributed worker framework I am working on, I added a helper facility called “Lawyer”, it basically reads the contracts and routes them to the provided object.

-

Let us take the parent and child example code from Node’s official documentation (http://nodejs.org/api/child_process.html) and see how simple it would be to add some documentation.

-

Parent

-
1
2
3
4
5
6
7
8
9
var cp = require('child_process');

var n = cp.fork(__dirname + '/sub.js');

n.on('message', function(m) {
console.log('PARENT got message:', m);
});

n.send({ hello: 'world' });
-

Child

-
1
2
3
4
5
process.on('message', function(m) {
console.log('CHILD got message:', m);
});

process.send({ foo: 'bar' });
-

Node’s child_process facility provied us with a very basic means of communication, we simply call n.send({any: "thing"}) and it automagically recieved by the forked child. Let’s spice it up with a more RPC message passing style, with contracts and lawyers

-

contracts.js

-
1
2
3
4
5
6
7
8
9
10
var Contractor = require('contractor').Contractor;
exports.ChildPublish = {
"GreetingResponse" : Contractor.Create("GreetingResponse", Contractor.Required("childs greeting response")).
"StatusResponse" : Contractor.Create("StatusResponse", Contractor.Required("errors count"), Contractor.Required("number of completed jobs"))
}

exports.ChildSubscribe = {
"Greeting": Contractor.Create("Greeting", Contractor.Required("greeting word")),
"StatusQuery" : Contractor.Create("StatusQuery")
}
-

parent.js

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var contracts = require('./contracts');
var Contractor = require('contractor').Contractor;
var Lawyer = require('contractor').Lawyer;
var cp = require('child_process');

var n = cp.fork(__dirname + '/sub.js');

n.on('message', function(m) {
Lawyer.Read(m, {
"GreetingResponse" : function(childResponse){ console.log("child responded:" + childResponse) },
"StatusResponse" : function(errorCount, numCompletedJobs){ console.log("childs status, errors:"+errorCount+" jobs done:" + numCompletedJobs) }
});
});
n.send(contracts.ChildSubscribe.Greeting("this is your father!"));
n.send(contracts.ChildSubscribe.StatusQuery());
-

sub.js

-
1
2
3
4
5
6
7
8
9
var contracts = require('./contracts');
var Contractor = require('contractor').Contractor;
var Lawyer = require('contractor').Lawyer;
process.on('message', function(m) {
Lawyer.Read(m, {
"Greeting" : function(parentGreeting){ process.send( contracts.ChildPublish.GreetingResponse("Hi "+parentGreeting+" can I have 10$?")) },
"StatusQuery" : function(){ process.send( contracts.ChildPublish.StatusResponse(0, 42) ) }
});
});
-

If we were to run these

-
1
2
3
#> node parent.js
child responded:Hi this is your father! can I have 10$?
childs status, errors:0 jobs done:42
-

If you found Contractor / Lawyer interesting and/or useful, I would love to hear about it!

- - -
- -
- -
- - - -
- -
- - -
- - -

- Typing in JavaScript? -

- - -
- -
- -

“Strong typing is for people with weak memories” - not sure who originally said that, but you have got to love the enthusiasm people have for dynamic languges. What is there not to love? powerful basic language constructs such as closure, prototypal inheritance, objects as first class citizens and anonymous functions, mainly, with which you can simulate almost anything static languages have to offer!

-

Proof you say? no problem! here are few implementations that are worthy of noting:

-

Compile time type checking https://developers.google.com/closure/compiler/docs/js-for-compiler#types

-

Classic OO inheritance http://mootools.net/docs/core/Class/Class & http://coffeescript.org/#classes

-

(if you have other interesting examples let me know!)

-

Convention over configuration

The combination of CoffeeScript’s “classical” Class like system and Backbone.JS’s MVC style structure, give (IMHO) the best value in terms of complexity and pure fun writing apps. The challenges I come across while building large complex applications is the inherited nature of a true dynamic language “typelessness“.

-

The ugly monster, that is -a large application written in JavaScript, pushed me into struggling with these two main challenges:

-
    -
  • Many-to-many PubSub communication
  • -
  • 3rd party API’s
  • -
-

Many-to-many PubSub communication

While the PubSub (also known as observer pattern) in JavaScript is a powerful tool to decouple your code, having multiple callers and listeners on the same event makes it really difficult to keep track of the actual common API for these events. Lats try to reflect on this while looking at an actual implementation.

-

(please excuse my CoffeeScript)

-

Pub/Sub implementation in Backbone/Underscore.JS

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
pubsub = _.extend {}, Backbone.Events
pubsub.on 'myEvent', (param1, param2)->
x1 = param1 + param2
# do something with x1

# in some other obscure location in the code
pubsub.on 'myEvent', (param1)->
x2 = param1
# do something with x2

# ...
pubsub.trigger 'myEvent', 5
# ...
pubsub.trigger 'myEvent', 5,10
-

The “pubsub.on(event name, callback)” API is very common amongst different JS pub/sub frameworks. here’s an example of how a generic implementation might look like:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pubsub = {
_listeners : {}
on : (channel, callback)->
@_listeners[ channel ] ?= []
@_listeners[ channel ].push callback

trigger : (channel, params...)->
@_listeners[ channel ]?.forEach (cb)-> cb.apply(null, params)
}

pubsub.on 'myEvent', (param1, param2)->
x1 = param1 + param2
# ... do something with x1..

# in some other obscure location in the code
pubsub.on 'myEvent', (param1)->
x2 = param1
# .... do something with x2

# ...
pubsub.trigger 'myEvent', 5
# ...
pubsub.trigger 'myEvent', 5,10
-

If your using the very popular Socket.IO library, for realtime client/server communication, you are using the exact same API, in which case it makes it even more complicated to keep track of, since the ambiguity is manifested on both the client and the server.

-

3rd party API’s

This is how a type related bug might look like in a dynamic language

-
1
2
3
a = "42" # returned from some API
a == 42
=> false
-

Enter “Contractor” & “Backbone-typed”

Contractor
https://github.com/romansky/Contractor

-

Backbone-typed
https://github.com/romansky/backbone-typed

-

I’m introducing a convention here, that helps keep the API consistent across the application, the approach here is to create wrapper functions, that are used as both the API documentation and run-time validation facility for required and optional arguments.

-

Example:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# declare the events and their APIs
Messages = {
LoginEvent : Contractor.Create( "LoginEvent", Contractor.Required("user ID"), Contractor.Optional("additional info") )
AppErrorEvent : Contractor.Create( "AppErrorEvent", Contractor.Required("user ID"), Contractor.Required("error description"), Contractor.Optional("exception") )
}

# register on event API
pubsub.on Messages.LoginEvent, (userID, info)-> #... do stuff here
pubsub.on Messages.AppErrorEvent, (userID, description, exception)-> #... do stuff here

# trigger the event
pubsub.apply(pubsub, Messages.LoginEvent(112233, "mobile"))
pubsub.apply(pubsub, Messages.LoginEvent(112233))

pubsub.apply(pubsub, Messages.AppErrorEvent(112233, "user did not have access to resource", e))
pubsub.apply(pubsub, Messages.AppErrorEvent(112233)) # since we did not provide the second required argument, this will log an error and return null
-

With NodeJS, I am using the same code on the front and back end, which makes it really easy to manage my Socket.IO communication.

-

If you have been paying attention, there’s still the type issue. Since I work allot with Backbone, it made a lot of sense to write “backbone-typed” which adds optional typing to Backbone-Models.
Example:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Wearing = {
"bracelet" : "bracelet"
"watch" : "watch"
}

class User extends TypedModel
defaults: {
name: null
email: null
lotteryNumber: null
isAwesome: null
}

types: {
name: Types.String
email: Types.String
lotteryNumber: Types.Integer
isAwesome: Types.Boolean
wearing : Types.Enum(Wearing)
}

user1 = new User({name: "foo", email: "foo@bar.com", lotteryNumber: 12345, isAwesome: true, wearing: Wearing.watch})
user1.toJSON() #=> {name: "foo", email: "foo@bar.com", lotteryNumber: 12345, isAwesome: true, wearing: "watch"} - nothing special going on here..

user2 = new User({name: "foo", email: "foo@bar.com", lotteryNumber: "54321", isAwesome: "false", wearing: "thong"})
user2.toJSON() #=> {name: "bar", email: "bar@foo.com", lotteryNumber: 54321, isAwesome: false, wearing: null} - shit happens!

user2.set({wearing: Wearing.bracelet})

if user2.get("lotteryNumber") == 54321 and user2.get("wearing") == Wearing.bracelet then user2.set({isAwesome: "true"})
user2.toJSON() #=> {name: "bar", email: "bar@foo.com", lotteryNumber: 54321, isAwesome: true, wearing: "bracelet"} - awesome for sure..
-

If you want to know a little more about how these work, please visit the respective Git repositories, and let me know what you think!

- - -
- -
- -
- - - -
- -
- - -
- - -

- Setting Up IntelliJ For Android And Scala On Ubuntu -

- - -
- -
- -

Prerequisites

Android SDK

    -
  1. get the latest SDK

    -
  2. -
  3. after extracting, run ./tools/android

    -
  4. -
  5. from the list select the Android version your afer, right now its Android 4.0.3 (API 15) (I also installed Android 2.3.3), click on Install packages to get the installation started

    -
  6. -
  7. setup your env echo "export ANDROID_HOME=~/Applications/android-sdk-linux/" >> ~/.bashrc

    -
  8. -
  9. refresh your currect session source ~/.bashrc

    -
  10. -
-

Adding SBT support to IntelliJ

    -
  1. open File > Settings > Plugin

    -
  2. -
  3. click on Browser repositories

    -
  4. -
  5. find SBT, right click and select Download and install

    -
  6. -
-

Setup SBT

    -
  1. create folder ~/bin/ (if you dont have ~/bin in your PATH, then you can do this: echo "PATH=$PATH:~/bin" >> ~/.bashrc (refresh your currect session source ~/.bashrc)

    -
  2. -
  3. download sbt-launcher.jar and place it under ~/bin

    -
  4. -
  5. create launcher: touch ~/bin/sbt and then echo 'java -Xmx512M -jar `dirname $0`/sbt-launch.jar "$@"' >> ~/bin/sbt

    -
  6. -
  7. make it executable: chmod u+x ~/bin/sbt

    -
  8. -
-

Setup android-plugin for Scala support

    -
  1. install giter8 curl https://raw.github.com/n8han/conscript/master/setup.sh | sh

    -
  2. -
  3. run it ~/bin/cs n8han/giter8

    -
  4. -
-

Setup sbt-idea

    -
  1. create folder (if it doesnt exist) mkdir -p ~/.sbt/plugins/

    -
  2. -
  3. create the file ~/.sbt/plugins/build.sbt and add the following lines to it:

    -
    resolvers += "sbt-idea-repo" at "http://mpeltonen.github.com/maven/"
    -addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.0.0")
    -
  4. -
-

Finally, now that you have all the tools you need, proceed to creating a new project

    -
  1. run the android plugin project setup tool: ~/bin/g8 jberkel/android-app, follow the onscreen questions (or press return for defaults)
    this will create the project folder and create the files needed to build an android project

    -
  2. -
  3. open the new folder cd <project name>

    -
  4. -
  5. setup IntelliJ project support sbt gen-idea

    -
  6. -
  7. open the project in IntelliJ

    -
  8. -
- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/categories/design/index.html b/categories/design/index.html deleted file mode 100644 index 3f70bd6..0000000 --- a/categories/design/index.html +++ /dev/null @@ -1,407 +0,0 @@ - - - - - - Category: design | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- HLD High Level Design -

- - -
- -
- -

This post is a follow-up on “Bootstrapping A New Project”

-

Previously…

I’ve written about “User Stories”, a tool that helps with expectation setting when working with (mostly) non-technical stakeholders.

-

Designing Toward

    -
  • database (schema, tables, indexes..)
  • -
  • application layout
  • -
  • deployment
  • -
  • modules
  • -
  • internal and external APIs
  • -
  • application infra (frameworks, tools etc..)
  • -
-

Design Tools At Your Disposal

-

Audiences

    -
  • System Architect - mostly interested in the software stack, deployment and the database design
  • -
  • Team Members - mostly interested in the more lower level stuff like modules and API’s
  • -
  • Other Teams - mostly interested in API’s
  • -
-

Feedback on design is great for everyone involved, setup meetings with relevant parties to do DR (design review)

-

UML and Other Vegetables

UML is complex, you don’t need to know it perfectly, start with what makes sense, and slowly pick up tutorials. Remember, its a visual tool that helps you communicate, not everyone who will look at your designs has the time (or cares for) to read throughly a book about UML and its specifics. In Einstein’s words “Make everything as simple as possible, but not simpler.”

-

Disclosure

Design does not last. Nobody is going to update the design as the system grows and evolves. Having said that, can still be used a good reference.

- - -
- -
- -
- - - -
- -
- - -
- - -

- User Stories -

- - -
- -
- -

This post is a follow-up on “Bootstrapping A New Project”

-

“User Story” is an important communication tool, it is used between you (the developer) and who ever is making the requirements (the stakeholder).
A “contract” which (unlike one written by a lawyer) is clear, understood and agreed upon.
On our side (development) user stories embody a work unit that can be further broken down and estimated.

-

Key value points:

-
    -
  • Language - its key that user stories are written in a language everyone can understand (stakeholders, developers etc..)
  • -
  • Perspective - user stories need to be written from the perspective of the “feature user”, so they represent real world value
  • -
  • Estimation - to help you get better and more precise estimation for the project overall
  • -
  • Prioritization - what needs to be delivered soon/later
  • -
-

Lets imagine we just got a call from the product manager, where he is telling us about this new survey tool:

    -
  1. It needs to collect answers from users via a link sent to our users by e-mail
  2. -
  3. It needs to be summarized and sent to … once a month on the 1st
  4. -
  5. Managers need to be able to see past surveys
  6. -
-

Hmm.. OK, thats pretty clear, but suppose this can be written clearer, lats try using the following template: (not my invention;))
As a [role], I want [Requirement / Feature], So that [Justigication/ Reason]

-

The requirements now look like this:

-
    -
  1. As an end user, I will be able to follow a link I got via e-mail, so that I can fill a survey and express my opinion
  2. -
  3. As a manager, I will receive an e-mail with a digest of surveys, so that I can evaluate performance
  4. -
  5. As a manager, I will be able to access old surveys, so that I can compare and evaluate performance over time
  6. -
-

By reading the above I can clearly understand the required functionality and the motivation behind writing it, this gives me a good starting point.
Since I am the one to implement this, I will need to provide estimates, so that product can plan ahead and communicate the product road map onward.
Unfortunately, “The Devil is In The Details” as they say, so giving estimates based on points 1,2,3 is still difficult.
Points 2 & 3 are “huge” stories, also known as “epics”, we need to further break down these stories, while still using the same language and practices:

-
    -
  1. As an end user, I will receive an email with a link as a result of some communication with a company, so that I can follow it and fill a survey
  2. -
  3. As an end user, I get a set of questions to answer, so that my opinion is collected
  4. -
  5. As a manager, I will receive an e-mail with a digest of surveys, so that I can evaluate companies performance
  6. -
  7. As a manager, I will have a survey management portal, so that I can overview previous results and compare to
  8. -
  9. As a manager, I can select a specific survey, so that I can extract meaningful actionable information
  10. -
-

The functionality expected of deliverables from the points above is clear, and now I feel more comfortable estimating the time it will take me to complete each.
Along with the estimation, these can now be prioritized against each other and against other ongoing projects..

-

Summery

The beginning of a new project is the perfect time to introduce user stories, it will help with getting the conversation going, further clarifying the scope and requirements.
While the project scope might change, user stories are (optimally) modular, decoupled and encapsulated, this should help with managing changes and their effect on the overall project.

- - -
- -
- -
- - - -
- -
- - -
- - -

- Bootstrapping A New Project -

- - -
- -
- -

My take on: How to give your new project a good starting point

So, you have a new shiny project on your lap, so now what?
Today everyone is talking about “agile”, and how its better then the old “waterfall” methodology of doing things

-

Waterfall

The “Waterfall” approach is notorious in the software community, some highlights of why this is the case:

-
    -
  • It assumes that the initial requirements will not change
  • -
  • There is only one version delivered to the stakeholders, at the end of the process
  • -
  • All the planning and “thinking” happen only in the beginning of the project
  • -
-

Agile

Everyone likes “Agile”, its the “cool” way to manage a project, it asks for less paperwork, less work upfront, built from the ground up to support changes, to name a few conceptions about it.
While I call it “Agile” here, in reality it is a world of sub cultures, some of which are: “XP” (eXtreme Programming), “scrum”, “Kanban” to name a few.
The difference between specific cultures is not always clear, we don’t necessarily have to stick to one, we can mix and match, like I am doing here, thus from this point onward I will just ignorantly call it “Agile” with out specifying the specific methodology.
The main strengths going for the Agile methodology are

-
    -
  • Short upfront work phase (requirements, design ..)
  • -
  • Iterations - project is broken down to deliverables, which is translated to real value delivered very early in the project lifetime
  • -
  • Changes are welcome and expected and are introduced into following iterations (bugs found in previous deliverables, design “inadequacies”)
  • -
-

Doing things in small pieces is great, just ask a WEB developer, how there’s nothing quite like writing a bit of code, refreshing the page and seeing the changes reflected, especially when working on a big complex project. This gives you a feeling of progress and achievement, even though the end goal is far far away..

-

Some Agile approaches talk beyond writing business code and going into proactive bug searching using TDD (Test Driven Development), which turns out to be a good fit to the overall process as it secures quality of delivered pieces of logic.
BDD (Behavior Driven Development) is very similar to TDD, but instead of going after “test coverage” it takes on a higher level, business specification driven approach, where the business embodied user stories are part of the actual test suite.

-

My recipe for success

    -
  • User Stories
  • -
  • Mock-ups (in the case of an app that has UI)
  • -
  • HLD (High Level -system component- Design)
  • -
  • Process Sequence diagram / Workflow diagram (possibly atop HLD)
  • -
-

Tooling

Tools that keep me productive:

-
    -
  • Google Document (User Stories)
  • -
  • Google Drawing (mockups, diagrams, HLD)
  • -
  • Lucidchart (diagrams)
  • -
-

In addition, I use a light weight web based project management tool as a central place to keep reference to important docs and user stories, and see my progress (and others working on the project). A great tool for the job is Trello.com - which free and super simple!

-

REATED UPDATES:

User Stories

- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/css/style.css b/css/style.css deleted file mode 100644 index 25930e1..0000000 --- a/css/style.css +++ /dev/null @@ -1,1403 +0,0 @@ -body { - zoom: 1; - width: 100%; -} -body:before, -body:after { - content: ""; - display: table; -} -body:after { - clear: both; -} -html, -body, -div, -span, -applet, -object, -iframe, -h1, -h2, -h3, -h4, -h5, -h6, -p, -blockquote, -pre, -a, -abbr, -acronym, -address, -big, -cite, -code, -del, -dfn, -em, -img, -ins, -kbd, -q, -s, -samp, -small, -strike, -strong, -sub, -sup, -tt, -var, -dl, -dt, -dd, -ol, -ul, -li, -fieldset, -form, -label, -legend, -table, -caption, -tbody, -tfoot, -thead, -tr, -th, -td { - margin: 0; - padding: 0; - border: 0; - outline: 0; - font-weight: inherit; - font-style: inherit; - font-family: inherit; - font-size: 100%; - vertical-align: baseline; -} -body { - line-height: 1; - color: #000; - background: #fff; -} -ol, -ul { - list-style: none; -} -table { - border-collapse: separate; - border-spacing: 0; - vertical-align: middle; -} -caption, -th, -td { - text-align: left; - font-weight: normal; - vertical-align: middle; -} -a img { - border: none; -} -input, -button { - margin: 0; - padding: 0; -} -input::-moz-focus-inner, -button::-moz-focus-inner { - border: 0; - padding: 0; -} -@font-face { - font-family: FontAwesome; - font-style: normal; - font-weight: normal; - src: url("fonts/fontawesome-webfont.eot?v=#4.0.3"); - src: url("fonts/fontawesome-webfont.eot?#iefix&v=#4.0.3") format("embedded-opentype"), url("fonts/fontawesome-webfont.woff?v=#4.0.3") format("woff"), url("fonts/fontawesome-webfont.ttf?v=#4.0.3") format("truetype"), url("fonts/fontawesome-webfont.svg#fontawesomeregular?v=#4.0.3") format("svg"); -} -html, -body, -#container { - height: 100%; -} -body { - background: #eee; - font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif; - -webkit-text-size-adjust: 100%; -} -.outer { - zoom: 1; - max-width: 1220px; - margin: 0 auto; - padding: 0 20px; -} -.outer:before, -.outer:after { - content: ""; - display: table; -} -.outer:after { - clear: both; -} -.inner { - display: inline; - float: left; - width: 98.33333333333333%; - margin: 0 0.833333333333333%; -} -.left, -.alignleft { - float: left; -} -.right, -.alignright { - float: right; -} -.clear { - clear: both; -} -#container { - position: relative; -} -.mobile-nav-on { - overflow: hidden; -} -#wrap { - height: 100%; - width: 100%; - position: absolute; - top: 0; - left: 0; - -webkit-transition: 0.2s ease-out; - -moz-transition: 0.2s ease-out; - -o-transition: 0.2s ease-out; - -ms-transition: 0.2s ease-out; - transition: 0.2s ease-out; - z-index: 1; - background: #eee; -} -.mobile-nav-on #wrap { - left: 280px; -} -@media screen and (min-width: 768px) { - #main { - display: inline; - float: left; - width: 73.33333333333333%; - margin: 0 0.833333333333333%; - } -} -.article-date, -.article-category-link, -.archive-year, -.widget-title { - text-decoration: none; - text-transform: uppercase; - letter-spacing: 2px; - color: #999; - margin-bottom: 1em; - margin-left: 5px; - line-height: 1em; - text-shadow: 0 1px #fff; - font-weight: bold; -} -.article-inner, -.archive-article-inner { - background: #fff; - -webkit-box-shadow: 1px 2px 3px #ddd; - box-shadow: 1px 2px 3px #ddd; - border: 1px solid #ddd; - -webkit-border-radius: 3px; - border-radius: 3px; -} -.article-entry h1, -.widget h1 { - font-size: 2em; -} -.article-entry h2, -.widget h2 { - font-size: 1.5em; -} -.article-entry h3, -.widget h3 { - font-size: 1.3em; -} -.article-entry h4, -.widget h4 { - font-size: 1.2em; -} -.article-entry h5, -.widget h5 { - font-size: 1em; -} -.article-entry h6, -.widget h6 { - font-size: 1em; - color: #999; -} -.article-entry hr, -.widget hr { - border: 1px dashed #ddd; -} -.article-entry strong, -.widget strong { - font-weight: bold; -} -.article-entry em, -.widget em, -.article-entry cite, -.widget cite { - font-style: italic; -} -.article-entry sup, -.widget sup, -.article-entry sub, -.widget sub { - font-size: 0.75em; - line-height: 0; - position: relative; - vertical-align: baseline; -} -.article-entry sup, -.widget sup { - top: -0.5em; -} -.article-entry sub, -.widget sub { - bottom: -0.2em; -} -.article-entry small, -.widget small { - font-size: 0.85em; -} -.article-entry acronym, -.widget acronym, -.article-entry abbr, -.widget abbr { - border-bottom: 1px dotted; -} -.article-entry ul, -.widget ul, -.article-entry ol, -.widget ol, -.article-entry dl, -.widget dl { - margin: 0 20px; - line-height: 1.6em; -} -.article-entry ul ul, -.widget ul ul, -.article-entry ol ul, -.widget ol ul, -.article-entry ul ol, -.widget ul ol, -.article-entry ol ol, -.widget ol ol { - margin-top: 0; - margin-bottom: 0; -} -.article-entry ul, -.widget ul { - list-style: disc; -} -.article-entry ol, -.widget ol { - list-style: decimal; -} -.article-entry dt, -.widget dt { - font-weight: bold; -} -#header { - height: banner-height; - position: relative; - border-bottom: 1px solid #ddd; -} -#header:before, -#header:after { - content: ""; - position: absolute; - left: 0; - right: 0; - height: 0px; -} -#header:before { - top: 0; - background: -webkit-linear-gradient(rgba(0,0,0,0.2), transparent); - background: -moz-linear-gradient(rgba(0,0,0,0.2), transparent); - background: -o-linear-gradient(rgba(0,0,0,0.2), transparent); - background: -ms-linear-gradient(rgba(0,0,0,0.2), transparent); - background: linear-gradient(rgba(0,0,0,0.2), transparent); -} -#header:after { - bottom: 0; - background: -webkit-linear-gradient(transparent, rgba(0,0,0,0.2)); - background: -moz-linear-gradient(transparent, rgba(0,0,0,0.2)); - background: -o-linear-gradient(transparent, rgba(0,0,0,0.2)); - background: -ms-linear-gradient(transparent, rgba(0,0,0,0.2)); - background: linear-gradient(transparent, rgba(0,0,0,0.2)); -} -#header-outer { - height: 100%; - position: relative; -} -#header-inner { - position: relative; - overflow: hidden; -} -#banner { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: #0b1b1c; - -webkit-background-size: cover; - -moz-background-size: cover; - background-size: cover; - z-index: -1; -} -#header-title { - text-align: center; - height: 40px; - position: absolute; - top: 50%; - left: 0; - margin-top: -20px; -} -#logo, -#subtitle { - text-decoration: none; - color: #fff; - font-weight: 300; - text-shadow: 0 1px 4px rgba(0,0,0,0.3); -} -#logo { - font-size: 40px; - line-height: 40px; - letter-spacing: 2px; -} -#subtitle { - font-size: 16px; - line-height: 16px; - letter-spacing: 1px; -} -#subtitle-wrap { - margin-top: 16px; -} -#main-nav { - float: left; - margin-left: -15px; -} -.nav-icon, -.main-nav-link { - float: left; - color: #fff; - opacity: 0.6; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; - filter: alpha(opacity=60); - text-decoration: none; - text-shadow: 0 1px rgba(0,0,0,0.2); - -webkit-transition: opacity 0.2s; - -moz-transition: opacity 0.2s; - -o-transition: opacity 0.2s; - -ms-transition: opacity 0.2s; - transition: opacity 0.2s; - display: block; - padding: 20px 15px; -} -.nav-icon:hover, -.main-nav-link:hover { - opacity: 1; - -ms-filter: none; - filter: none; -} -.nav-icon { - font-family: FontAwesome; - text-align: center; - font-size: 14px; - width: 14px; - height: 14px; - padding: 20px 15px; - position: relative; - cursor: pointer; -} -.main-nav-link { - font-weight: 300; - letter-spacing: 1px; -} -@media screen and (max-width: 479px) { - .main-nav-link { - display: none; - } -} -#main-nav-toggle { - display: none; -} -#main-nav-toggle:before { - content: "\f0c9"; -} -@media screen and (max-width: 479px) { - #main-nav-toggle { - display: block; - } -} -#sub-nav { - float: right; - margin-right: -15px; -} -#nav-rss-link:before { - content: "\f09e"; -} -#nav-search-btn:before { - content: "\f002"; -} -#search-form-wrap { - position: absolute; - top: 15px; - width: 150px; - height: 30px; - right: -150px; - opacity: 0; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; - filter: alpha(opacity=0); - -webkit-transition: 0.2s ease-out; - -moz-transition: 0.2s ease-out; - -o-transition: 0.2s ease-out; - -ms-transition: 0.2s ease-out; - transition: 0.2s ease-out; -} -#search-form-wrap.on { - opacity: 1; - -ms-filter: none; - filter: none; - right: 0; -} -@media screen and (max-width: 479px) { - #search-form-wrap { - width: 100%; - right: -100%; - } -} -.search-form { - position: absolute; - top: 0; - left: 0; - right: 0; - background: #fff; - padding: 5px 15px; - -webkit-border-radius: 15px; - border-radius: 15px; - -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.3); - box-shadow: 0 0 10px rgba(0,0,0,0.3); -} -.search-form-input { - border: none; - background: none; - color: #555; - width: 100%; - font: 13px "Helvetica Neue", Helvetica, Arial, sans-serif; - outline: none; -} -.search-form-input::-webkit-search-results-decoration, -.search-form-input::-webkit-search-cancel-button { - -webkit-appearance: none; -} -.search-form-submit { - position: absolute; - top: 50%; - right: 10px; - margin-top: -7px; - font: 13px FontAwesome; - border: none; - background: none; - color: #bbb; - cursor: pointer; -} -.search-form-submit:hover, -.search-form-submit:focus { - color: #777; -} -.article { - margin: 50px 0; -} -.article-inner { - overflow: hidden; -} -.article-meta { - zoom: 1; -} -.article-meta:before, -.article-meta:after { - content: ""; - display: table; -} -.article-meta:after { - clear: both; -} -.article-date { - float: left; -} -.article-category { - float: left; - line-height: 1em; - color: #ccc; - text-shadow: 0 1px #fff; - margin-left: 8px; -} -.article-category:before { - content: "\2022"; -} -.article-category-link { - margin: 0 12px 1em; -} -.article-header { - padding: 20px 20px 0; -} -.article-title { - text-decoration: none; - font-size: 2em; - font-weight: bold; - color: #555; - line-height: 1.1em; - -webkit-transition: color 0.2s; - -moz-transition: color 0.2s; - -o-transition: color 0.2s; - -ms-transition: color 0.2s; - transition: color 0.2s; -} -a.article-title:hover { - color: #258fb8; -} -.article-entry { - zoom: 1; - color: #555; - padding: 0 20px; -} -.article-entry:before, -.article-entry:after { - content: ""; - display: table; -} -.article-entry:after { - clear: both; -} -.article-entry p, -.article-entry table { - line-height: 1.6em; - margin: 1.6em 0; -} -.article-entry h1, -.article-entry h2, -.article-entry h3, -.article-entry h4, -.article-entry h5, -.article-entry h6 { - font-weight: bold; -} -.article-entry h1, -.article-entry h2, -.article-entry h3, -.article-entry h4, -.article-entry h5, -.article-entry h6 { - line-height: 1.1em; - margin: 1.1em 0; -} -.article-entry a { - color: #258fb8; - text-decoration: none; -} -.article-entry a:hover { - text-decoration: underline; -} -.article-entry ul, -.article-entry ol, -.article-entry dl { - margin-top: 1.6em; - margin-bottom: 1.6em; -} -.article-entry img, -.article-entry video { - max-width: 100%; - height: auto; - display: block; - margin: auto; -} -.article-entry iframe { - border: none; -} -.article-entry table { - width: 100%; - border-collapse: collapse; - border-spacing: 0; -} -.article-entry th { - font-weight: bold; - border-bottom: 3px solid #ddd; - padding-bottom: 0.5em; -} -.article-entry td { - border-bottom: 1px solid #ddd; - padding: 10px 0; -} -.article-entry blockquote { - font-family: Georgia, "Times New Roman", serif; - font-size: 1.4em; - margin: 1.6em 20px; - text-align: center; -} -.article-entry blockquote footer { - font-size: 14px; - margin: 1.6em 0; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; -} -.article-entry blockquote footer cite:before { - content: "—"; - padding: 0 0.5em; -} -.article-entry .pullquote { - text-align: left; - width: 45%; - margin: 0; -} -.article-entry .pullquote.left { - margin-left: 0.5em; - margin-right: 1em; -} -.article-entry .pullquote.right { - margin-right: 0.5em; - margin-left: 1em; -} -.article-entry .caption { - color: #999; - display: block; - font-size: 0.9em; - margin-top: 0.5em; - position: relative; - text-align: center; -} -.article-entry .video-container { - position: relative; - padding-top: 56.25%; - height: 0; - overflow: hidden; -} -.article-entry .video-container iframe, -.article-entry .video-container object, -.article-entry .video-container embed { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - margin-top: 0; -} -.article-more-link a { - display: inline-block; - line-height: 1em; - padding: 6px 15px; - -webkit-border-radius: 15px; - border-radius: 15px; - background: #eee; - color: #999; - text-shadow: 0 1px #fff; - text-decoration: none; -} -.article-more-link a:hover { - background: #258fb8; - color: #fff; - text-decoration: none; - text-shadow: 0 1px #1e7293; -} -.article-footer { - zoom: 1; - font-size: 0.85em; - line-height: 1.6em; - border-top: 1px solid #ddd; - padding-top: 1.6em; - margin: 0 20px 20px; -} -.article-footer:before, -.article-footer:after { - content: ""; - display: table; -} -.article-footer:after { - clear: both; -} -.article-footer a { - color: #999; - text-decoration: none; -} -.article-footer a:hover { - color: #555; -} -.article-tag-list-item { - float: left; - margin-right: 10px; -} -.article-tag-list-link:before { - content: "#"; -} -.article-comment-link { - float: right; -} -.article-comment-link:before { - content: "\f075"; - font-family: FontAwesome; - padding-right: 8px; -} -.article-share-link { - cursor: pointer; - float: right; - margin-left: 20px; -} -.article-share-link:before { - content: "\f064"; - font-family: FontAwesome; - padding-right: 6px; -} -#article-nav { - zoom: 1; - position: relative; -} -#article-nav:before, -#article-nav:after { - content: ""; - display: table; -} -#article-nav:after { - clear: both; -} -@media screen and (min-width: 768px) { - #article-nav { - margin: 50px 0; - } - #article-nav:before { - width: 8px; - height: 8px; - position: absolute; - top: 50%; - left: 50%; - margin-top: -4px; - margin-left: -4px; - content: ""; - -webkit-border-radius: 50%; - border-radius: 50%; - background: #ddd; - -webkit-box-shadow: 0 1px 2px #fff; - box-shadow: 0 1px 2px #fff; - } -} -.article-nav-link-wrap { - text-decoration: none; - text-shadow: 0 1px #fff; - color: #999; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - margin-top: 50px; - text-align: center; - display: block; -} -.article-nav-link-wrap:hover { - color: #555; -} -@media screen and (min-width: 768px) { - .article-nav-link-wrap { - width: 50%; - margin-top: 0; - } -} -@media screen and (min-width: 768px) { - #article-nav-newer { - float: left; - text-align: right; - padding-right: 20px; - } -} -@media screen and (min-width: 768px) { - #article-nav-older { - float: right; - text-align: left; - padding-left: 20px; - } -} -.article-nav-caption { - text-transform: uppercase; - letter-spacing: 2px; - color: #ddd; - line-height: 1em; - font-weight: bold; -} -#article-nav-newer .article-nav-caption { - margin-right: -2px; -} -.article-nav-title { - font-size: 0.85em; - line-height: 1.6em; - margin-top: 0.5em; -} -.article-share-box { - position: absolute; - display: none; - background: #fff; - -webkit-box-shadow: 1px 2px 10px rgba(0,0,0,0.2); - box-shadow: 1px 2px 10px rgba(0,0,0,0.2); - -webkit-border-radius: 3px; - border-radius: 3px; - margin-left: -145px; - overflow: hidden; - z-index: 1; -} -.article-share-box.on { - display: block; -} -.article-share-input { - width: 100%; - background: none; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif; - padding: 0 15px; - color: #555; - outline: none; - border: 1px solid #ddd; - -webkit-border-radius: 3px 3px 0 0; - border-radius: 3px 3px 0 0; - height: 36px; - line-height: 36px; -} -.article-share-links { - zoom: 1; - background: #eee; -} -.article-share-links:before, -.article-share-links:after { - content: ""; - display: table; -} -.article-share-links:after { - clear: both; -} -.article-share-twitter, -.article-share-facebook, -.article-share-pinterest, -.article-share-google { - width: 50px; - height: 36px; - display: block; - float: left; - position: relative; - color: #999; - text-shadow: 0 1px #fff; -} -.article-share-twitter:before, -.article-share-facebook:before, -.article-share-pinterest:before, -.article-share-google:before { - font-size: 20px; - font-family: FontAwesome; - width: 20px; - height: 20px; - position: absolute; - top: 50%; - left: 50%; - margin-top: -10px; - margin-left: -10px; - text-align: center; -} -.article-share-twitter:hover, -.article-share-facebook:hover, -.article-share-pinterest:hover, -.article-share-google:hover { - color: #fff; -} -.article-share-twitter:before { - content: "\f099"; -} -.article-share-twitter:hover { - background: #00aced; - text-shadow: 0 1px #008abe; -} -.article-share-facebook:before { - content: "\f09a"; -} -.article-share-facebook:hover { - background: #3b5998; - text-shadow: 0 1px #2f477a; -} -.article-share-pinterest:before { - content: "\f0d2"; -} -.article-share-pinterest:hover { - background: #cb2027; - text-shadow: 0 1px #a21a1f; -} -.article-share-google:before { - content: "\f0d5"; -} -.article-share-google:hover { - background: #dd4b39; - text-shadow: 0 1px #be3221; -} -.article-gallery { - background: #000; - position: relative; -} -.article-gallery-photos { - position: relative; - overflow: hidden; -} -.article-gallery-img { - display: none; - max-width: 100%; -} -.article-gallery-img:first-child { - display: block; -} -.article-gallery-img.loaded { - position: absolute; - display: block; -} -.article-gallery-img img { - display: block; - max-width: 100%; - margin: 0 auto; -} -#comments { - background: #fff; - -webkit-box-shadow: 1px 2px 3px #ddd; - box-shadow: 1px 2px 3px #ddd; - padding: 20px; - border: 1px solid #ddd; - -webkit-border-radius: 3px; - border-radius: 3px; - margin: 50px 0; -} -#comments a { - color: #258fb8; -} -.archives-wrap { - margin: 50px 0; -} -.archives { - zoom: 1; -} -.archives:before, -.archives:after { - content: ""; - display: table; -} -.archives:after { - clear: both; -} -.archive-year-wrap { - margin-bottom: 1em; -} -.archives { - -webkit-column-gap: 10px; - -moz-column-gap: 10px; - column-gap: 10px; -} -@media screen and (min-width: 480px) and (max-width: 767px) { - .archives { - -webkit-column-count: 2; - -moz-column-count: 2; - column-count: 2; - } -} -@media screen and (min-width: 768px) { - .archives { - -webkit-column-count: 3; - -moz-column-count: 3; - column-count: 3; - } -} -.archive-article { - -webkit-column-break-inside: avoid; - page-break-inside: avoid; - overflow: hidden; - break-inside: avoid-column; -} -.archive-article-inner { - padding: 10px; - margin-bottom: 15px; -} -.archive-article-title { - text-decoration: none; - font-weight: bold; - color: #555; - -webkit-transition: color 0.2s; - -moz-transition: color 0.2s; - -o-transition: color 0.2s; - -ms-transition: color 0.2s; - transition: color 0.2s; - line-height: 1.6em; -} -.archive-article-title:hover { - color: #258fb8; -} -.archive-article-footer { - margin-top: 1em; -} -.archive-article-date { - color: #999; - text-decoration: none; - font-size: 0.85em; - line-height: 1em; - margin-bottom: 0.5em; - display: block; -} -#page-nav { - zoom: 1; - margin: 50px auto; - background: #fff; - -webkit-box-shadow: 1px 2px 3px #ddd; - box-shadow: 1px 2px 3px #ddd; - border: 1px solid #ddd; - -webkit-border-radius: 3px; - border-radius: 3px; - text-align: center; - color: #999; - overflow: hidden; -} -#page-nav:before, -#page-nav:after { - content: ""; - display: table; -} -#page-nav:after { - clear: both; -} -#page-nav a, -#page-nav span { - padding: 10px 20px; -} -#page-nav a { - color: #999; - text-decoration: none; -} -#page-nav a:hover { - background: #999; - color: #fff; -} -#page-nav .prev { - float: left; -} -#page-nav .next { - float: right; -} -#page-nav .page-number { - display: inline-block; -} -@media screen and (max-width: 479px) { - #page-nav .page-number { - display: none; - } -} -#page-nav .current { - color: #555; - font-weight: bold; -} -#page-nav .space { - color: #ddd; -} -#footer { - background: #262a30; - padding: 50px 0; - border-top: 1px solid #ddd; - color: #999; -} -#footer a { - color: #258fb8; - text-decoration: none; -} -#footer a:hover { - text-decoration: underline; -} -#footer-info { - line-height: 1.6em; - font-size: 0.85em; -} -.article-entry pre, -.article-entry .highlight { - background: #2d2d2d; - margin: 0 -20px; - padding: 15px 20px; - border-style: solid; - border-color: #ddd; - border-width: 1px 0; - overflow: auto; - color: #ccc; - line-height: 22.400000000000002px; -} -.article-entry .highlight .gutter pre, -.article-entry .gist .gist-file .gist-data .line-numbers { - color: #666; - font-size: 0.85em; -} -.article-entry pre, -.article-entry code { - font-family: "Source Code Pro", Consolas, Monaco, Menlo, Consolas, monospace; -} -.article-entry code { - background: #eee; - text-shadow: 0 1px #fff; - padding: 0 0.3em; -} -.article-entry pre code { - background: none; - text-shadow: none; - padding: 0; -} -.article-entry .highlight pre { - border: none; - margin: 0; - padding: 0; -} -.article-entry .highlight table { - margin: 0; - width: auto; -} -.article-entry .highlight td { - border: none; - padding: 0; -} -.article-entry .highlight figcaption { - zoom: 1; - font-size: 0.85em; - color: #999; - line-height: 1em; - margin-bottom: 1em; -} -.article-entry .highlight figcaption:before, -.article-entry .highlight figcaption:after { - content: ""; - display: table; -} -.article-entry .highlight figcaption:after { - clear: both; -} -.article-entry .highlight figcaption a { - float: right; -} -.article-entry .highlight .gutter pre { - text-align: right; - padding-right: 20px; -} -.article-entry .gist { - margin: 0 -20px; - border-style: solid; - border-color: #ddd; - border-width: 1px 0; - background: #2d2d2d; - padding: 15px 20px 15px 0; -} -.article-entry .gist .gist-file { - border: none; - font-family: "Source Code Pro", Consolas, Monaco, Menlo, Consolas, monospace; - margin: 0; -} -.article-entry .gist .gist-file .gist-data { - background: none; - border: none; -} -.article-entry .gist .gist-file .gist-data .line-numbers { - background: none; - border: none; - padding: 0 20px 0 0; -} -.article-entry .gist .gist-file .gist-data .line-data { - padding: 0 !important; -} -.article-entry .gist .gist-file .highlight { - margin: 0; - padding: 0; - border: none; -} -.article-entry .gist .gist-file .gist-meta { - background: #2d2d2d; - color: #999; - font: 0.85em "Helvetica Neue", Helvetica, Arial, sans-serif; - text-shadow: 0 0; - padding: 0; - margin-top: 1em; - margin-left: 20px; -} -.article-entry .gist .gist-file .gist-meta a { - color: #258fb8; - font-weight: normal; -} -.article-entry .gist .gist-file .gist-meta a:hover { - text-decoration: underline; -} -pre .comment, -pre .title { - color: #999; -} -pre .variable, -pre .attribute, -pre .tag, -pre .regexp, -pre .ruby .constant, -pre .xml .tag .title, -pre .xml .pi, -pre .xml .doctype, -pre .html .doctype, -pre .css .id, -pre .css .class, -pre .css .pseudo { - color: #f2777a; -} -pre .number, -pre .preprocessor, -pre .built_in, -pre .literal, -pre .params, -pre .constant { - color: #f99157; -} -pre .class, -pre .ruby .class .title, -pre .css .rules .attribute { - color: #9c9; -} -pre .string, -pre .value, -pre .inheritance, -pre .header, -pre .ruby .symbol, -pre .xml .cdata { - color: #9c9; -} -pre .css .hexcolor { - color: #6cc; -} -pre .function, -pre .python .decorator, -pre .python .title, -pre .ruby .function .title, -pre .ruby .title .keyword, -pre .perl .sub, -pre .javascript .title, -pre .coffeescript .title { - color: #69c; -} -pre .keyword, -pre .javascript .function { - color: #c9c; -} -@media screen and (max-width: 479px) { - #mobile-nav { - position: absolute; - top: 0; - left: 0; - width: 280px; - height: 100%; - background: #191919; - border-right: 1px solid #fff; - } -} -@media screen and (max-width: 479px) { - .mobile-nav-link { - display: block; - color: #999; - text-decoration: none; - padding: 15px 20px; - font-weight: bold; - } - .mobile-nav-link:hover { - color: #fff; - } -} -@media screen and (min-width: 768px) { - #sidebar { - display: inline; - float: left; - width: 23.333333333333332%; - margin: 0 0.833333333333333%; - } -} -.widget-wrap { - margin: 50px 0; -} -.widget { - color: #777; - text-shadow: 0 1px #fff; - background: #ddd; - -webkit-box-shadow: 0 -1px 4px #ccc inset; - box-shadow: 0 -1px 4px #ccc inset; - border: 1px solid #ccc; - padding: 15px; - -webkit-border-radius: 3px; - border-radius: 3px; -} -.widget a { - color: #258fb8; - text-decoration: none; -} -.widget a:hover { - text-decoration: underline; -} -.widget ul ul, -.widget ol ul, -.widget dl ul, -.widget ul ol, -.widget ol ol, -.widget dl ol, -.widget ul dl, -.widget ol dl, -.widget dl dl { - margin-left: 15px; - list-style: disc; -} -.widget { - line-height: 1.6em; - word-wrap: break-word; - font-size: 0.9em; -} -.widget ul, -.widget ol { - list-style: none; - margin: 0; -} -.widget ul ul, -.widget ol ul, -.widget ul ol, -.widget ol ol { - margin: 0 20px; -} -.widget ul ul, -.widget ol ul { - list-style: disc; -} -.widget ul ol, -.widget ol ol { - list-style: decimal; -} -.category-list-count, -.tag-list-count, -.archive-list-count { - padding-left: 5px; - color: #999; - font-size: 0.85em; -} -.category-list-count:before, -.tag-list-count:before, -.archive-list-count:before { - content: "("; -} -.category-list-count:after, -.tag-list-count:after, -.archive-list-count:after { - content: ")"; -} -.tagcloud a { - margin-right: 5px; -} diff --git a/index.html b/index.html index 8f4f70a..eba2d81 100644 --- a/index.html +++ b/index.html @@ -1,848 +1 @@ - - - - - - Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- Using Spray to mock 3rd party APIs in your tests -

- - -
- -
- -

If you need to test/mock 3rd party API’s / a crawler / external web service, and you dont want to use some fancy DI framework (I hate the magic in DI frameworks, I use constructor DI instead..), anyway, here’s a helper class I put togather to that end

-

If you haven’t got a chance to try out Spray, you totally should, its a high performance web server built on top of Netty and Akka

-

Spray has a very nice HTTP API. Few things I liked:

-
    -
  • HTTP method mapping spray.http.HttpMethods - GET, POST etc..
  • -
  • HttpRequest and HttpResopnse are basically wrappers around data you would expect to have in a request and response data
  • -
  • spray.http.Uri wrapper class around what you would expect to find in the URI
  • -
  • it really is just an Akka Actor, with just the right amount of control exposed and the rest well hidden behind its nice APIs
  • -
-

So, for testing/mocking all I really need is:

-
    -
  • HTTP method
  • -
  • URI
  • -
  • body(content)
  • -
-

Here’s an implementation of the helper class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import akka.actor._
import akka.io.IO
import java.util.concurrent.atomic.AtomicInteger
import spray.can.Http
import spray.http.{Uri, HttpMethod, HttpResponse, HttpRequest}

object Helpers {

lazy implicit val testSystem = ActorSystem("helpers-test-system")
// used to track the last assigned port
val lastPort = new AtomicInteger(20000)
// a single expected triplet of method+uri+body
type Replay = (HttpMethod,Uri.Path,String)

// factory method to get a replater to play around with
def webReplayer(replay : List[Replay]) : (ActorRef,Int) = {
// everytime the factory is used a new port is assigned,
// so many instances can co-exist and tests can run in parallel without getting
// on eachother's toes
val newPort = lastPort.addAndGet(1)
// instantiate a new Spray server. Yes, its that simple!
val newServer = testSystem.actorOf(Props(new Helpers.ReplayerActor(replay)))
IO(Http) ! Http.Bind(newServer, interface = "127.0.0.1", port = newPort)
// return a pair of the server instance and the assigned port
// the newPort should be used by client code to construct the remote URI with
(newServer,newPort)
}

// object to use to tell the server to shutdown..
object ShutTestServer

// a concrete implementation for a Spray Actor to listen to incoming HttpRequest(..) messages
// provided a list of messages to be matched against arrived and content to be sent back
class ReplayerActor(var replay :List[Replay]) extends Actor {
var ioServer : ActorRef = null
override def receive: Receive = {
case _ : Http.Connected =>
sender ! Http.Register(self)
ioServer = sender()
// this is basically a "catch all" HttpRequest pattern
case HttpRequest(method, uri,_,_,_) =>
// a test to see that this is indeed expected to happen at this point
if (!replay.isEmpty && replay.head._1 == method && uri.path == replay.head._2){
sender ! HttpResponse(entity = replay.head._3)
replay = replay.tail
} else {
sender ! HttpResponse(status = 400, entity = "was expecting a different request at this point")
}
case ShutTestServer => ioServer ! PoisonPill
case _ => //ignore
}
}
}
-

And now a cocrete client code example to use this tool

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// some random content I would like to receive in this scenario (or test..)
val content =
s"""
|<html>
|<head>
|<title>sometitle</title>
|</head>
|<body>some body</body>
|</html>
""".stripMargin

// we use the factory method to get an instance of "replayer" to play around with
val (_testWebServer,port) = Helpers.webReplayer(
List(
(GET,Uri.Path("/somesite"),content)
)
)

// and where ever in your actual code, use the port we got back above and have it "hit" the server
val whatEver = YourHTTPGetter.fetch(s"http://127.0.0.1:$port/somesite")
assert(whatEver == content)
-

Profit!

- - -
- -
- -
- - - -
- -
- - -
- - -

- Making The Case For Kotlin -

- - -
- -
- -

The Kotlin project page has this to say about Kotlin: “is a statically typed programming language that compiles to JVM byte codes and JavaScript.” project page

-

Kotlin is a very interesting project, I wanted to check it out, so I decided to write this blog post exploring some design aspects behind Kotlin, and specifically how it compares to the leading JVM “hacker” language - Scala.

-

Who Is Behind Kotlin?

Even though there are several decent free IDE’s available today (Eclipse, NetBeans, Visual Studio Express..), it (could be) surprising that JetBrains is able to rack in between 199$ for a personal license and up to 699$ for a commercial license (for companies) including 1 year update subscription (I have indeed bought one personal license for my development needs). If you are developing on top of VisualStudio you are (most) probably familiar with the 250$+ plugin ReSharper.

-

I would argue that this makes them a good candidate, with insight into good and bad language features, but lets not get ahead of ourselves and set the expectations too high.

-

Why Another JVM Language?

JVM is a well known platform, its weaknesses and scalability strategies are available and documented well on the web, its being used as classic Java run-time VM and for some other popular languages like Scala, Groovy, Clojure and others. Many modules have been already written in Java, normally JVM run languages are compatible with the existing Java ecosystem.

-

Why Static Typing?

In the old (and still ongoing) battle of dynamic and static languages, the consensus is that dynamic languages are faster and easier way to get things done, on the static languages side they allow programmers harness the power of intelligent IDEs to scale up and maintain their code. Type-inferred languages try to find a middle ground approach so that you don’t need to write so much boilerplate code (cue JAVA) to support the static typing, while having the benefits of a full compile time type enforcement.

-

Finally, Kotlin Vs. Scala - Fight!

Scala today is the fastest growing JVM language and is probably competing for the same crowd as Kotlin, Kotlin put up a comparison page link so we will simply go over the omissions part and try to make sense of it.

-

OMITTED - Implicit conversions, parameters

What is it? Scala implicits is (IMHO) a single biggest weakness in the language, once they are used, along with the ability to boast symbols for syntax, it quickly stops being human readable and more like a DSL. Which requires many trips to the docs or even worse - the source code.

-

SBT (Scala Build Tool) is the Maven/Ant of the Scala world link, its very difficult to get things done with it without many trips to google/documentation

-
1
2
3
4
5
lazy val pricing = Project (
"pricing",
file ("cdap2-pricing"),
settings = buildSettings ++ Seq (libraryDependencies ++= pricingDeps)
) dependsOn (common, compact, server)
-

Lift Is a popular web framework, here’s a snippet I took from one of its documentation pages link (see the "#result *" #> x part)

-
1
2
3
4
5
6
7
8
9
def render = inputParam match {
case Full(x) =>
println("Input is: "+x)
"#result *" #> x

case _ =>
println("No input present! Rendering input form HTML")
PassThru
}
-

Thoughts - Indeed for many people this is the part that makes Kotlin a better “scalable” language, by not allowing programmers and libraries hinder the code unreadable without an IDE

-

OMITTED - Overridable Type Members

What is it? Type members in Scala allow you to manage local generic types by assign a type to a class/abstract class/trait member

-
1
2
3
abstract class HasTypedMember {
type typeMe
}
-

An extending class will provide a specific type for this type type member

-
1
2
3
class ExtendingClass extends HasTypedMember {
type typeMe = Int
}
-

Thoughts - Frankly, its not really clear why Kotlin decided to omit having typed members, I could be missing something.. Never the less Kotlin usually takes the minimalistic and simplistic route, my guess is that if this is indeed useful, the door is open to add it later..

-

OMITTED - Path-dependent types

What is it? Pointing to a class under another class in Java would mean a specific type (Parent.Child), well not so in Scala. Scala makes the nested class of two same class instances different, much like you would expect two members of two different instances of a single class. Confused? here is some code to explain this better

-

Java:

-
1
2
3
4
5
6
7
8
9
10
11
12
public class Outer {
public class Inner {}
public void useInner(Outer.Inner inner){...}
}
//.. somewhere in the code
o1 = new Outer()
o2 = new Outer()
i1 = new o1.inner()
i2 = new o2.inner()
// this would work fine
o1.useInner(i2) // works
o2.useInner(i1) // works
-

Scala:

1
2
3
4
5
6
7
8
9
10
11
12
class Outer {
class Inner {}
def useInner(inner : Inner){...}
}
//.. somewhere in the code..
val o1 = new Outer
val o2 = new Outer
val i1 = new o1.Inner
val i2 = new o2.Inner
o1.useInner(i1) // works fine
o2.useInner(i2) // works fine
o1.useInner(i2) // THROWS type mismatch exception!

-

Thoughts - It makes some sense that the path to a type that can only exist in an instance is enforced as such.. It would be interesting to know why Kotlin decided to implement this feature in a similar way to Java (conservative?)

-

OMITTED - Existential types

What is it? This is yet another trade-off for Scala to be able to inter-op with Java
Let’s see an example:

-
1
2
3
4
5
// Create a case class with generic type
case class Z[T]( blah:T )
// create an instance and cast it to a type created with existential type annotation
Seq( Z[Int]( 5 ), Z[String]("blah") ) : Seq[Z[X] forSome { type X >: String with Int }]
res1 = Seq[Z[_ >: String with Int]] = List(Z(5), Z(blah))
-

Thoughts - Scala supports type variance to describe generic type bounds, Scala designer (Odersky) chose to add existential types support due to three main reasons link: erasure, raw types (lack of generics) and Java’s “wildcard” types.

-

Kotlin took the more “purist” approach (also known as reified types) of dropping support for erasure, raw types, wildcard types and finally existential types.

-

OMITTED - Complicated logic for initialization of traits

What is it? Traits are similar to abstract classes, except that you may use several traits on the same class at the same time (like interfaces).

-

In Scala, traits are constructed before the extended classes

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
trait Averager {
val first : Int
val second : Int
val avg : Int = first / second
}
// if we were to naively construct a class like so:
class Naive extends Averager {
val first = 20
val second = 10
}
// create instance
> new Naive()
java.lang.ArithmeticException: / by zero
// what happened was that our val "avg" was computed at construction time before seeing the values we put in Naive
-

Alternatively the trait can be used with a pre-initialized anonymous class val res = new { val first = 20 ; val second = 10 } with Averager, also the val which depends on other vals (avg in our case) can be made “lazy” with the (obvious) lazy keyword lazy val avg : Int = first / second.

-

As a side note, using def for “avg” could work, but it would mean evaluating whatever is defined under “avg” every-time this method is called.

-

Thoughts - Traits in Scala can have state which could be made tricky. If you read this blog post link you will see Kotlin developers thoughts and a nice insight into what went into designing the inheritance in Kotlin. (don’t forget to read the comments!)

-

OMITTED - Custom symbolic operations

What is it? Scala does not have a built in handling of symbols (like =,+,/ etc), they are actually method calls, as you can have symbols used for method names.

-

Thoughts - This may sound useful at first, but can and (arguably) will create very confusing looking code, like we saw before in the Lift and SBT examples.

-

OMITTED - Built-in XML

What is it? Exactly as it sounds, Scala built in XML native support, so you can do stuff like this

-
1
val holdsXml = <outer><inner>some text</inner></outer>
-

Thoughts - Having a built in support for XML as a language feature is interesting, and sometimes useful. An interview link with Odersky reveals that he would consider removing the native XML support in Scala if had written Scala now.

-

Bottom Line

Kotlin is an idiomatic language that draws from the many good features in todays leading languages, specifically from Scala which is a leading language on the JVM platform today. We’ve looked into omissions on Kotlin’s part versus Scala

-

I’ve very much enjoyed writing this blog post and learning about the evolution that the design of Java/Scala/Kotlin languages, and the choices the respective authors made along the way.

-

To get a better feeling of Kotlin I might pick it up for a small project, at which point I will surely blog about it some more..

- - -
- -
- -
- - - -
- -
- - -
- - -

- Distributed Scheduled Queue With Redis -

- - -
- -
- -

Unlike the classic FIFO queue, jobs are scheduled for execution in the future, possibly on a specific day, week, month year.. similar to “cron” in a Unix OS. Unlike the normal Unix “cron” implementation, we want it to be distributed, so that several machines(workers) can query and then execute these jobs.

-

Implementing

    -
  • Planning - The standart Unix cron format is very flexible and has been time tested for describing schedules
  • -
  • Managing/Coordinating - The queues are stored in Redis Sorted Sets, Lua will be used for atomic execution
  • -
  • Execution - Getting the pieces together
  • -
-

Planning

“Unix Time” format, is the number of milliseconds since (UTC) 1 January 1970
Example (* the actual unix time number is in UTC, while the formatted date is localized)

-
1350745504313 == Sat Oct 20 2012 17:05:04 GMT+0200 (IST)
-

Using this format will make it possible to slice the queue with specific point in time in mind.

-

Managing/Coordinating

Redis’st will be holding the job IDs as members scored by their corresponding execution times(in unix time format).

-

-

Redis commands that will come in handy

-
    -
  • Scheduling job run: ZADD cron:queue 1350745504313 1000
  • -
  • Finding a job to run: ZRANGEBYSCORE cron:queue 1350745504314 LIMIT 0 1
  • -
-

Execution

Without going into any platform specific implementation details, this is what the process looks like

-
    -
  1. When a new schedule is added, jobs are created for upcoming execution times, which are calculated for the next two hours and added to our cron queue
  2. -
  3. A special job is added to be run every hour, it will run over all the schedules, calculate and add their execution time to the scheduled queue, two hours into the future
  4. -
  5. Workers will be polling the database every second to find jobs
  6. -
  7. Once an entry comes up, it is removed from the queue and added to another Sorted Set queue, so we dont “forget” any jobs..
  8. -
-

As for items 3 and 4 above, it is not possible to implement this logic in the client, because for example when we find a job that needs to be executed, another worker might grab it, or worse they both “grabbed” it and are now working on the same job. To work around this, we need to write a little Lua script, since Lua scripts are executed serially, just like any other Redis command

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
local currentTimeMS = KEYS[1]
local workingQueue = "scheduler:working"
local scheduleQueue = "schedule"
-- for easier reading, epoch time is in milliseconds
local second = 1000

local function foundJob(jobID)
-- implement removing the job from all other queues
-- implement updating job status where the actual job object is stored
end

local function findNextJob()
local result
-- check if there are any "stale" jobs
result = redis.call( 'ZRANGEBYSCORE', workingQueue, 0, currentTimeMS-60*second, 'LIMIT',0,1 )
if result[1] ~= nil then return foundJob(result[1]) end
-- check the normal queue
result = redis.call( 'ZRANGEBYSCORE', scheduleQueue, 0, currentTimeMS, 'LIMIT',0,1 )
if result[1] ~= nil then return foundJob(result[1]) end
-- if we didnt find anything return nil
return nil
end

return findNextJob()
-

And there you have it! a “Distributed scheduled queue” implemented on top of Redis.

-

If you are going to implement this with node, I created a little cron format parser you can find here:
JSCron

- - -
- -
- -
- - - -
- -
- - -
- - -

- Using Contractor With Child Processes -

- - -
- -
- -

Contractor is a factory of contracts, that helps with documenting our APIs.
The child_process facility built into node is using EventEmitter (v1), and does not support callbacks, so it is a great fit for Contractor!
Since I needed it for a distributed worker framework I am working on, I added a helper facility called “Lawyer”, it basically reads the contracts and routes them to the provided object.

-

Let us take the parent and child example code from Node’s official documentation (http://nodejs.org/api/child_process.html) and see how simple it would be to add some documentation.

-

Parent

-
1
2
3
4
5
6
7
8
9
var cp = require('child_process');

var n = cp.fork(__dirname + '/sub.js');

n.on('message', function(m) {
console.log('PARENT got message:', m);
});

n.send({ hello: 'world' });
-

Child

-
1
2
3
4
5
process.on('message', function(m) {
console.log('CHILD got message:', m);
});

process.send({ foo: 'bar' });
-

Node’s child_process facility provied us with a very basic means of communication, we simply call n.send({any: "thing"}) and it automagically recieved by the forked child. Let’s spice it up with a more RPC message passing style, with contracts and lawyers

-

contracts.js

-
1
2
3
4
5
6
7
8
9
10
var Contractor = require('contractor').Contractor;
exports.ChildPublish = {
"GreetingResponse" : Contractor.Create("GreetingResponse", Contractor.Required("childs greeting response")).
"StatusResponse" : Contractor.Create("StatusResponse", Contractor.Required("errors count"), Contractor.Required("number of completed jobs"))
}

exports.ChildSubscribe = {
"Greeting": Contractor.Create("Greeting", Contractor.Required("greeting word")),
"StatusQuery" : Contractor.Create("StatusQuery")
}
-

parent.js

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var contracts = require('./contracts');
var Contractor = require('contractor').Contractor;
var Lawyer = require('contractor').Lawyer;
var cp = require('child_process');

var n = cp.fork(__dirname + '/sub.js');

n.on('message', function(m) {
Lawyer.Read(m, {
"GreetingResponse" : function(childResponse){ console.log("child responded:" + childResponse) },
"StatusResponse" : function(errorCount, numCompletedJobs){ console.log("childs status, errors:"+errorCount+" jobs done:" + numCompletedJobs) }
});
});
n.send(contracts.ChildSubscribe.Greeting("this is your father!"));
n.send(contracts.ChildSubscribe.StatusQuery());
-

sub.js

-
1
2
3
4
5
6
7
8
9
var contracts = require('./contracts');
var Contractor = require('contractor').Contractor;
var Lawyer = require('contractor').Lawyer;
process.on('message', function(m) {
Lawyer.Read(m, {
"Greeting" : function(parentGreeting){ process.send( contracts.ChildPublish.GreetingResponse("Hi "+parentGreeting+" can I have 10$?")) },
"StatusQuery" : function(){ process.send( contracts.ChildPublish.StatusResponse(0, 42) ) }
});
});
-

If we were to run these

-
1
2
3
#> node parent.js
child responded:Hi this is your father! can I have 10$?
childs status, errors:0 jobs done:42
-

If you found Contractor / Lawyer interesting and/or useful, I would love to hear about it!

- - -
- -
- -
- - - -
- -
- - -
- - -

- Typing in JavaScript? -

- - -
- -
- -

“Strong typing is for people with weak memories” - not sure who originally said that, but you have got to love the enthusiasm people have for dynamic languges. What is there not to love? powerful basic language constructs such as closure, prototypal inheritance, objects as first class citizens and anonymous functions, mainly, with which you can simulate almost anything static languages have to offer!

-

Proof you say? no problem! here are few implementations that are worthy of noting:

-

Compile time type checking https://developers.google.com/closure/compiler/docs/js-for-compiler#types

-

Classic OO inheritance http://mootools.net/docs/core/Class/Class & http://coffeescript.org/#classes

-

(if you have other interesting examples let me know!)

-

Convention over configuration

The combination of CoffeeScript’s “classical” Class like system and Backbone.JS’s MVC style structure, give (IMHO) the best value in terms of complexity and pure fun writing apps. The challenges I come across while building large complex applications is the inherited nature of a true dynamic language “typelessness“.

-

The ugly monster, that is -a large application written in JavaScript, pushed me into struggling with these two main challenges:

-
    -
  • Many-to-many PubSub communication
  • -
  • 3rd party API’s
  • -
-

Many-to-many PubSub communication

While the PubSub (also known as observer pattern) in JavaScript is a powerful tool to decouple your code, having multiple callers and listeners on the same event makes it really difficult to keep track of the actual common API for these events. Lats try to reflect on this while looking at an actual implementation.

-

(please excuse my CoffeeScript)

-

Pub/Sub implementation in Backbone/Underscore.JS

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
pubsub = _.extend {}, Backbone.Events
pubsub.on 'myEvent', (param1, param2)->
x1 = param1 + param2
# do something with x1

# in some other obscure location in the code
pubsub.on 'myEvent', (param1)->
x2 = param1
# do something with x2

# ...
pubsub.trigger 'myEvent', 5
# ...
pubsub.trigger 'myEvent', 5,10
-

The “pubsub.on(event name, callback)” API is very common amongst different JS pub/sub frameworks. here’s an example of how a generic implementation might look like:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pubsub = {
_listeners : {}
on : (channel, callback)->
@_listeners[ channel ] ?= []
@_listeners[ channel ].push callback

trigger : (channel, params...)->
@_listeners[ channel ]?.forEach (cb)-> cb.apply(null, params)
}

pubsub.on 'myEvent', (param1, param2)->
x1 = param1 + param2
# ... do something with x1..

# in some other obscure location in the code
pubsub.on 'myEvent', (param1)->
x2 = param1
# .... do something with x2

# ...
pubsub.trigger 'myEvent', 5
# ...
pubsub.trigger 'myEvent', 5,10
-

If your using the very popular Socket.IO library, for realtime client/server communication, you are using the exact same API, in which case it makes it even more complicated to keep track of, since the ambiguity is manifested on both the client and the server.

-

3rd party API’s

This is how a type related bug might look like in a dynamic language

-
1
2
3
a = "42" # returned from some API
a == 42
=> false
-

Enter “Contractor” & “Backbone-typed”

Contractor
https://github.com/romansky/Contractor

-

Backbone-typed
https://github.com/romansky/backbone-typed

-

I’m introducing a convention here, that helps keep the API consistent across the application, the approach here is to create wrapper functions, that are used as both the API documentation and run-time validation facility for required and optional arguments.

-

Example:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# declare the events and their APIs
Messages = {
LoginEvent : Contractor.Create( "LoginEvent", Contractor.Required("user ID"), Contractor.Optional("additional info") )
AppErrorEvent : Contractor.Create( "AppErrorEvent", Contractor.Required("user ID"), Contractor.Required("error description"), Contractor.Optional("exception") )
}

# register on event API
pubsub.on Messages.LoginEvent, (userID, info)-> #... do stuff here
pubsub.on Messages.AppErrorEvent, (userID, description, exception)-> #... do stuff here

# trigger the event
pubsub.apply(pubsub, Messages.LoginEvent(112233, "mobile"))
pubsub.apply(pubsub, Messages.LoginEvent(112233))

pubsub.apply(pubsub, Messages.AppErrorEvent(112233, "user did not have access to resource", e))
pubsub.apply(pubsub, Messages.AppErrorEvent(112233)) # since we did not provide the second required argument, this will log an error and return null
-

With NodeJS, I am using the same code on the front and back end, which makes it really easy to manage my Socket.IO communication.

-

If you have been paying attention, there’s still the type issue. Since I work allot with Backbone, it made a lot of sense to write “backbone-typed” which adds optional typing to Backbone-Models.
Example:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Wearing = {
"bracelet" : "bracelet"
"watch" : "watch"
}

class User extends TypedModel
defaults: {
name: null
email: null
lotteryNumber: null
isAwesome: null
}

types: {
name: Types.String
email: Types.String
lotteryNumber: Types.Integer
isAwesome: Types.Boolean
wearing : Types.Enum(Wearing)
}

user1 = new User({name: "foo", email: "foo@bar.com", lotteryNumber: 12345, isAwesome: true, wearing: Wearing.watch})
user1.toJSON() #=> {name: "foo", email: "foo@bar.com", lotteryNumber: 12345, isAwesome: true, wearing: "watch"} - nothing special going on here..

user2 = new User({name: "foo", email: "foo@bar.com", lotteryNumber: "54321", isAwesome: "false", wearing: "thong"})
user2.toJSON() #=> {name: "bar", email: "bar@foo.com", lotteryNumber: 54321, isAwesome: false, wearing: null} - shit happens!

user2.set({wearing: Wearing.bracelet})

if user2.get("lotteryNumber") == 54321 and user2.get("wearing") == Wearing.bracelet then user2.set({isAwesome: "true"})
user2.toJSON() #=> {name: "bar", email: "bar@foo.com", lotteryNumber: 54321, isAwesome: true, wearing: "bracelet"} - awesome for sure..
-

If you want to know a little more about how these work, please visit the respective Git repositories, and let me know what you think!

- - -
- -
- -
- - - -
- -
- - -
- - -

- HLD High Level Design -

- - -
- -
- -

This post is a follow-up on “Bootstrapping A New Project”

-

Previously…

I’ve written about “User Stories”, a tool that helps with expectation setting when working with (mostly) non-technical stakeholders.

-

Designing Toward

    -
  • database (schema, tables, indexes..)
  • -
  • application layout
  • -
  • deployment
  • -
  • modules
  • -
  • internal and external APIs
  • -
  • application infra (frameworks, tools etc..)
  • -
-

Design Tools At Your Disposal

-

Audiences

    -
  • System Architect - mostly interested in the software stack, deployment and the database design
  • -
  • Team Members - mostly interested in the more lower level stuff like modules and API’s
  • -
  • Other Teams - mostly interested in API’s
  • -
-

Feedback on design is great for everyone involved, setup meetings with relevant parties to do DR (design review)

-

UML and Other Vegetables

UML is complex, you don’t need to know it perfectly, start with what makes sense, and slowly pick up tutorials. Remember, its a visual tool that helps you communicate, not everyone who will look at your designs has the time (or cares for) to read throughly a book about UML and its specifics. In Einstein’s words “Make everything as simple as possible, but not simpler.”

-

Disclosure

Design does not last. Nobody is going to update the design as the system grows and evolves. Having said that, can still be used a good reference.

- - -
- -
- -
- - - -
- -
- - -
- - -

- User Stories -

- - -
- -
- -

This post is a follow-up on “Bootstrapping A New Project”

-

“User Story” is an important communication tool, it is used between you (the developer) and who ever is making the requirements (the stakeholder).
A “contract” which (unlike one written by a lawyer) is clear, understood and agreed upon.
On our side (development) user stories embody a work unit that can be further broken down and estimated.

-

Key value points:

-
    -
  • Language - its key that user stories are written in a language everyone can understand (stakeholders, developers etc..)
  • -
  • Perspective - user stories need to be written from the perspective of the “feature user”, so they represent real world value
  • -
  • Estimation - to help you get better and more precise estimation for the project overall
  • -
  • Prioritization - what needs to be delivered soon/later
  • -
-

Lets imagine we just got a call from the product manager, where he is telling us about this new survey tool:

    -
  1. It needs to collect answers from users via a link sent to our users by e-mail
  2. -
  3. It needs to be summarized and sent to … once a month on the 1st
  4. -
  5. Managers need to be able to see past surveys
  6. -
-

Hmm.. OK, thats pretty clear, but suppose this can be written clearer, lats try using the following template: (not my invention;))
As a [role], I want [Requirement / Feature], So that [Justigication/ Reason]

-

The requirements now look like this:

-
    -
  1. As an end user, I will be able to follow a link I got via e-mail, so that I can fill a survey and express my opinion
  2. -
  3. As a manager, I will receive an e-mail with a digest of surveys, so that I can evaluate performance
  4. -
  5. As a manager, I will be able to access old surveys, so that I can compare and evaluate performance over time
  6. -
-

By reading the above I can clearly understand the required functionality and the motivation behind writing it, this gives me a good starting point.
Since I am the one to implement this, I will need to provide estimates, so that product can plan ahead and communicate the product road map onward.
Unfortunately, “The Devil is In The Details” as they say, so giving estimates based on points 1,2,3 is still difficult.
Points 2 & 3 are “huge” stories, also known as “epics”, we need to further break down these stories, while still using the same language and practices:

-
    -
  1. As an end user, I will receive an email with a link as a result of some communication with a company, so that I can follow it and fill a survey
  2. -
  3. As an end user, I get a set of questions to answer, so that my opinion is collected
  4. -
  5. As a manager, I will receive an e-mail with a digest of surveys, so that I can evaluate companies performance
  6. -
  7. As a manager, I will have a survey management portal, so that I can overview previous results and compare to
  8. -
  9. As a manager, I can select a specific survey, so that I can extract meaningful actionable information
  10. -
-

The functionality expected of deliverables from the points above is clear, and now I feel more comfortable estimating the time it will take me to complete each.
Along with the estimation, these can now be prioritized against each other and against other ongoing projects..

-

Summery

The beginning of a new project is the perfect time to introduce user stories, it will help with getting the conversation going, further clarifying the scope and requirements.
While the project scope might change, user stories are (optimally) modular, decoupled and encapsulated, this should help with managing changes and their effect on the overall project.

- - -
- -
- -
- - - -
- -
- - -
- - -

- Setting Up IntelliJ For Android And Scala On Ubuntu -

- - -
- -
- -

Prerequisites

Android SDK

    -
  1. get the latest SDK

    -
  2. -
  3. after extracting, run ./tools/android

    -
  4. -
  5. from the list select the Android version your afer, right now its Android 4.0.3 (API 15) (I also installed Android 2.3.3), click on Install packages to get the installation started

    -
  6. -
  7. setup your env echo "export ANDROID_HOME=~/Applications/android-sdk-linux/" >> ~/.bashrc

    -
  8. -
  9. refresh your currect session source ~/.bashrc

    -
  10. -
-

Adding SBT support to IntelliJ

    -
  1. open File > Settings > Plugin

    -
  2. -
  3. click on Browser repositories

    -
  4. -
  5. find SBT, right click and select Download and install

    -
  6. -
-

Setup SBT

    -
  1. create folder ~/bin/ (if you dont have ~/bin in your PATH, then you can do this: echo "PATH=$PATH:~/bin" >> ~/.bashrc (refresh your currect session source ~/.bashrc)

    -
  2. -
  3. download sbt-launcher.jar and place it under ~/bin

    -
  4. -
  5. create launcher: touch ~/bin/sbt and then echo 'java -Xmx512M -jar `dirname $0`/sbt-launch.jar "$@"' >> ~/bin/sbt

    -
  6. -
  7. make it executable: chmod u+x ~/bin/sbt

    -
  8. -
-

Setup android-plugin for Scala support

    -
  1. install giter8 curl https://raw.github.com/n8han/conscript/master/setup.sh | sh

    -
  2. -
  3. run it ~/bin/cs n8han/giter8

    -
  4. -
-

Setup sbt-idea

    -
  1. create folder (if it doesnt exist) mkdir -p ~/.sbt/plugins/

    -
  2. -
  3. create the file ~/.sbt/plugins/build.sbt and add the following lines to it:

    -
    resolvers += "sbt-idea-repo" at "http://mpeltonen.github.com/maven/"
    -addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.0.0")
    -
  4. -
-

Finally, now that you have all the tools you need, proceed to creating a new project

    -
  1. run the android plugin project setup tool: ~/bin/g8 jberkel/android-app, follow the onscreen questions (or press return for defaults)
    this will create the project folder and create the files needed to build an android project

    -
  2. -
  3. open the new folder cd <project name>

    -
  4. -
  5. setup IntelliJ project support sbt gen-idea

    -
  6. -
  7. open the project in IntelliJ

    -
  8. -
- - -
- -
- -
- - - -
- -
- - -
- - -

- Bootstrapping A New Project -

- - -
- -
- -

My take on: How to give your new project a good starting point

So, you have a new shiny project on your lap, so now what?
Today everyone is talking about “agile”, and how its better then the old “waterfall” methodology of doing things

-

Waterfall

The “Waterfall” approach is notorious in the software community, some highlights of why this is the case:

-
    -
  • It assumes that the initial requirements will not change
  • -
  • There is only one version delivered to the stakeholders, at the end of the process
  • -
  • All the planning and “thinking” happen only in the beginning of the project
  • -
-

Agile

Everyone likes “Agile”, its the “cool” way to manage a project, it asks for less paperwork, less work upfront, built from the ground up to support changes, to name a few conceptions about it.
While I call it “Agile” here, in reality it is a world of sub cultures, some of which are: “XP” (eXtreme Programming), “scrum”, “Kanban” to name a few.
The difference between specific cultures is not always clear, we don’t necessarily have to stick to one, we can mix and match, like I am doing here, thus from this point onward I will just ignorantly call it “Agile” with out specifying the specific methodology.
The main strengths going for the Agile methodology are

-
    -
  • Short upfront work phase (requirements, design ..)
  • -
  • Iterations - project is broken down to deliverables, which is translated to real value delivered very early in the project lifetime
  • -
  • Changes are welcome and expected and are introduced into following iterations (bugs found in previous deliverables, design “inadequacies”)
  • -
-

Doing things in small pieces is great, just ask a WEB developer, how there’s nothing quite like writing a bit of code, refreshing the page and seeing the changes reflected, especially when working on a big complex project. This gives you a feeling of progress and achievement, even though the end goal is far far away..

-

Some Agile approaches talk beyond writing business code and going into proactive bug searching using TDD (Test Driven Development), which turns out to be a good fit to the overall process as it secures quality of delivered pieces of logic.
BDD (Behavior Driven Development) is very similar to TDD, but instead of going after “test coverage” it takes on a higher level, business specification driven approach, where the business embodied user stories are part of the actual test suite.

-

My recipe for success

    -
  • User Stories
  • -
  • Mock-ups (in the case of an app that has UI)
  • -
  • HLD (High Level -system component- Design)
  • -
  • Process Sequence diagram / Workflow diagram (possibly atop HLD)
  • -
-

Tooling

Tools that keep me productive:

-
    -
  • Google Document (User Stories)
  • -
  • Google Drawing (mockups, diagrams, HLD)
  • -
  • Lucidchart (diagrams)
  • -
-

In addition, I use a light weight web based project management tool as a central place to keep reference to important docs and user stories, and see my progress (and others working on the project). A great tool for the job is Trello.com - which free and super simple!

-

REATED UPDATES:

User Stories

- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file +

test

diff --git a/package.json b/package.json new file mode 100644 index 0000000..809afdd --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "hexo-site", + "version": "0.0.0", + "private": true, + "hexo": { + "version": "3.2.0" + }, + "dependencies": { + "hexo": "^3.1.1", + "hexo-deployer-git": "^0.1.0", + "hexo-generator-archive": "^0.1.2", + "hexo-generator-category": "^0.1.2", + "hexo-generator-index": "^0.1.2", + "hexo-generator-tag": "^0.1.1", + "hexo-renderer-ejs": "^0.1.0", + "hexo-renderer-marked": "^0.2.4", + "hexo-renderer-stylus": "^0.3.0", + "hexo-server": "^0.1.2" + } +} diff --git a/scaffolds/draft.md b/scaffolds/draft.md new file mode 100644 index 0000000..45b1bb7 --- /dev/null +++ b/scaffolds/draft.md @@ -0,0 +1,3 @@ +title: {{ title }} +tags: +--- diff --git a/scaffolds/page.md b/scaffolds/page.md new file mode 100644 index 0000000..f484b76 --- /dev/null +++ b/scaffolds/page.md @@ -0,0 +1,3 @@ +title: {{ title }} +date: {{ date }} +--- diff --git a/scaffolds/photo.md b/scaffolds/photo.md new file mode 100644 index 0000000..5aba0db --- /dev/null +++ b/scaffolds/photo.md @@ -0,0 +1,5 @@ +layout: {{ layout }} +title: {{ title }} +date: {{ date }} +tags: +--- diff --git a/scaffolds/post.md b/scaffolds/post.md new file mode 100644 index 0000000..c590d7a --- /dev/null +++ b/scaffolds/post.md @@ -0,0 +1,4 @@ +title: {{ title }} +date: {{ date }} +tags: +--- diff --git a/source/_posts/Using-Spray-to-mock-3rd-party-APIs-in-your-tests.md b/source/_posts/Using-Spray-to-mock-3rd-party-APIs-in-your-tests.md new file mode 100644 index 0000000..2c3003a --- /dev/null +++ b/source/_posts/Using-Spray-to-mock-3rd-party-APIs-in-your-tests.md @@ -0,0 +1,105 @@ +title: 'Using Spray to mock 3rd party APIs in your tests' +date: 2014-06-14 20:26:44 +tags: ["scala","spray","testing"] +--- + +If you need to test/mock 3rd party API's / a crawler / external web service, and you dont want to use some fancy DI framework (I hate the magic in DI frameworks, I use constructor DI instead..), anyway, here's a helper class I put togather to that end + +If you haven't got a chance to try out Spray, you totally should, its a high performance web server built on top of Netty and Akka + +Spray has a very nice HTTP API. Few things I liked: + - HTTP method mapping `spray.http.HttpMethods` - `GET`, `POST` etc.. + - `HttpRequest` and `HttpResopnse` are basically wrappers around data you would expect to have in a request and response data + - `spray.http.Uri` wrapper class around what you would expect to find in the URI + - it really is just an Akka `Actor`, with just the right amount of control exposed and the rest well hidden behind its nice APIs + +So, for testing/mocking all I really need is: + + - HTTP method + - URI + - body(content) + +### Here's an implementation of the helper class + +```scala +import akka.actor._ +import akka.io.IO +import java.util.concurrent.atomic.AtomicInteger +import spray.can.Http +import spray.http.{Uri, HttpMethod, HttpResponse, HttpRequest} + +object Helpers { + + lazy implicit val testSystem = ActorSystem("helpers-test-system") + // used to track the last assigned port + val lastPort = new AtomicInteger(20000) + // a single expected triplet of method+uri+body + type Replay = (HttpMethod,Uri.Path,String) + + // factory method to get a replater to play around with + def webReplayer(replay : List[Replay]) : (ActorRef,Int) = { + // everytime the factory is used a new port is assigned, + // so many instances can co-exist and tests can run in parallel without getting + // on eachother's toes + val newPort = lastPort.addAndGet(1) + // instantiate a new Spray server. Yes, its that simple! + val newServer = testSystem.actorOf(Props(new Helpers.ReplayerActor(replay))) + IO(Http) ! Http.Bind(newServer, interface = "127.0.0.1", port = newPort) + // return a pair of the server instance and the assigned port + // the newPort should be used by client code to construct the remote URI with + (newServer,newPort) + } + + // object to use to tell the server to shutdown.. + object ShutTestServer + + // a concrete implementation for a Spray Actor to listen to incoming HttpRequest(..) messages + // provided a list of messages to be matched against arrived and content to be sent back + class ReplayerActor(var replay :List[Replay]) extends Actor { + var ioServer : ActorRef = null + override def receive: Receive = { + case _ : Http.Connected => + sender ! Http.Register(self) + ioServer = sender() + // this is basically a "catch all" HttpRequest pattern + case HttpRequest(method, uri,_,_,_) => + // a test to see that this is indeed expected to happen at this point + if (!replay.isEmpty && replay.head._1 == method && uri.path == replay.head._2){ + sender ! HttpResponse(entity = replay.head._3) + replay = replay.tail + } else { + sender ! HttpResponse(status = 400, entity = "was expecting a different request at this point") + } + case ShutTestServer => ioServer ! PoisonPill + case _ => //ignore + } + } +} +``` + +### And now a cocrete client code example to use this tool + +```scala +// some random content I would like to receive in this scenario (or test..) +val content = +s""" + | + | + |sometitle + | + |some body + | +""".stripMargin + +// we use the factory method to get an instance of "replayer" to play around with +val (_testWebServer,port) = Helpers.webReplayer( + List( + (GET,Uri.Path("/somesite"),content) + ) +) + +// and where ever in your actual code, use the port we got back above and have it "hit" the server +val whatEver = YourHTTPGetter.fetch(s"http://127.0.0.1:$port/somesite") +assert(whatEver == content) +``` +Profit! diff --git a/source/_posts/bootstrapping-a-new-project.md b/source/_posts/bootstrapping-a-new-project.md new file mode 100644 index 0000000..c861414 --- /dev/null +++ b/source/_posts/bootstrapping-a-new-project.md @@ -0,0 +1,53 @@ +--- +title: "Bootstrapping A New Project" +categories: ["design"] +tags: [] +date : 2012/02/07 +--- + +### My take on: How to give your new project a good starting point + +So, you have a new shiny project on your lap, so now what? +Today everyone is talking about "agile", and how its better then the old "waterfall" methodology of doing things + +#### Waterfall +The "Waterfall" approach is notorious in the software community, some highlights of why this is the case: + + * It assumes that the initial requirements will not change + * There is only one version delivered to the stakeholders, at the end of the process + * All the planning and "thinking" happen only in the beginning of the project + +#### Agile +Everyone likes "Agile", its the "cool" way to manage a project, it asks for less paperwork, less work upfront, built from the ground up to support changes, to name a few conceptions about it. +While I call it "Agile" here, in reality it is a world of sub cultures, some of which are: "XP" (eXtreme Programming), "scrum", "Kanban" to name a few. +The difference between specific cultures is not always clear, we don't necessarily have to stick to one, we can mix and match, like I am doing here, thus from this point onward I will just ignorantly call it "Agile" with out specifying the specific methodology. +The main strengths going for the Agile methodology are + + * Short upfront work phase (requirements, design ..) + * Iterations - project is broken down to deliverables, which is translated to real value delivered very early in the project lifetime + * Changes are welcome and expected and are introduced into following iterations (bugs found in previous deliverables, design "inadequacies") + +Doing things in small pieces is great, just ask a WEB developer, how there's nothing quite like writing a bit of code, refreshing the page and seeing the changes reflected, especially when working on a big complex project. This gives you a feeling of progress and achievement, even though the end goal is far far away.. + +Some Agile approaches talk beyond writing business code and going into proactive bug searching using TDD (Test Driven Development), which turns out to be a good fit to the overall process as it secures quality of delivered pieces of logic. +BDD (Behavior Driven Development) is very similar to TDD, but instead of going after "test coverage" it takes on a higher level, business specification driven approach, where the business embodied user stories are part of the actual test suite. + +#### My recipe for success + + * User Stories + * Mock-ups (in the case of an app that has UI) + * HLD (High Level -system component- Design) + * Process Sequence diagram / Workflow diagram (possibly atop HLD) + +#### Tooling + +Tools that keep me productive: + + * Google Document (User Stories) + * Google Drawing (mockups, diagrams, HLD) + * Lucidchart (diagrams) + +In addition, I use a light weight web based project management tool as a central place to keep reference to important docs and user stories, and see my progress (and others working on the project). A great tool for the job is [Trello.com](http://www.trello.com) - which free and super simple! + +#### REATED UPDATES: +[User Stories](/code/2012/02/25/user-stories/) diff --git a/source/_posts/distributed-scheduled-queue-with-redis.md b/source/_posts/distributed-scheduled-queue-with-redis.md new file mode 100644 index 0000000..635fa0e --- /dev/null +++ b/source/_posts/distributed-scheduled-queue-with-redis.md @@ -0,0 +1,86 @@ +--- +layout: post +title: "Distributed Scheduled Queue With Redis" +description: "" +category: +tags: ["redis","queue","distributed"] +date: 2012/10/20 +--- + +Unlike the classic FIFO queue, jobs are scheduled for execution in the future, possibly on a specific day, week, month year.. similar to "cron" in a Unix OS. Unlike the normal Unix "cron" implementation, we want it to be distributed, so that several machines(workers) can query and then execute these jobs. + +### Implementing + + * _Planning_ - The standart Unix cron format is very flexible and has been time tested for describing schedules + * _Managing/Coordinating_ - The queues are stored in Redis Sorted Sets, Lua will be used for atomic execution + * _Execution_ - Getting the pieces together + +#### Planning + +"Unix Time" format, is the number of milliseconds since (*UTC*) 1 January 1970 +Example (* the actual unix time number is in UTC, while the formatted date is localized) + + 1350745504313 == Sat Oct 20 2012 17:05:04 GMT+0200 (IST) + +Using this format will make it possible to slice the queue with specific point in time in mind. + +#### Managing/Coordinating + +Redis'st will be holding the job IDs as members scored by their corresponding execution times(in unix time format).
+ + + +Redis commands that will come in handy + + * Scheduling job run: *ZADD cron:queue 1350745504313 1000* + * Finding a job to run: *ZRANGEBYSCORE cron:queue 1350745504314 LIMIT 0 1* + +#### Execution + +Without going into any platform specific implementation details, this is what the process looks like + +1. When a new schedule is added, jobs are created for upcoming execution times, which are calculated for the next two hours and added to our cron queue +2. A special job is added to be run every hour, it will run over all the schedules, calculate and add their execution time to the scheduled queue, two hours into the future +3. Workers will be polling the database every second to find jobs +4. Once an entry comes up, it is removed from the queue and added to another Sorted Set queue, so we dont "forget" any jobs.. + +As for items 3 and 4 above, it is not possible to implement this logic in the client, because for example when we find a job that needs to be executed, another worker might grab it, or worse they both "grabbed" it and are now working on the same job. To work around this, we need to write a little Lua script, since Lua scripts are executed serially, just like any other Redis command + +```lua +local currentTimeMS = KEYS[1] +local workingQueue = "scheduler:working" +local scheduleQueue = "schedule" +-- for easier reading, epoch time is in milliseconds +local second = 1000 + +local function foundJob(jobID) + -- implement removing the job from all other queues + -- implement updating job status where the actual job object is stored +end + +local function findNextJob() + local result + -- check if there are any "stale" jobs + result = redis.call( 'ZRANGEBYSCORE', workingQueue, 0, currentTimeMS-60*second, 'LIMIT',0,1 ) + if result[1] ~= nil then return foundJob(result[1]) end + -- check the normal queue + result = redis.call( 'ZRANGEBYSCORE', scheduleQueue, 0, currentTimeMS, 'LIMIT',0,1 ) + if result[1] ~= nil then return foundJob(result[1]) end + -- if we didnt find anything return nil + return nil +end + +return findNextJob() +``` + + +And there you have it! a "Distributed scheduled queue" implemented on top of Redis. + +If you are going to implement this with node, I created a little cron format parser you can find here: +[JSCron](https://github.com/romansky/JsCron) + + + + + + diff --git a/source/_posts/hld-high-level-design.md b/source/_posts/hld-high-level-design.md new file mode 100644 index 0000000..d1edf2f --- /dev/null +++ b/source/_posts/hld-high-level-design.md @@ -0,0 +1,52 @@ +--- +title: "HLD High Level Design" +categories: ['design'] +tags: ["design"] +date: 2012/03/07 +--- + + +This post is a follow-up on ["Bootstrapping A New Project"](/code/2012/02/07/bootstrapping-a-new-project/) + +#### Previously... + +I've written about ["User Stories"](/code/2012/02/25/user-stories/), a tool that helps with expectation setting when working with (mostly) non-technical stakeholders. + +#### Designing Toward + + - **database** (schema, tables, indexes..) + - **application layout** + - **deployment** + - **modules** + - **internal and external APIs** + - **application infra** (frameworks, tools etc..) + +#### Design Tools At Your Disposal + + - **system architecture** ([example-Lucidchart](http://www.lucidchart.com/publicSegments/view/4f6e3aaf-0798-4bce-97ca-386a0a5a8951/image.png)) - big pieces that make up the system + - **activity diagram** ([more info](http://www.agilemodeling.com/artifacts/activityDiagram.htm)) - communication between components in the system + - **sequence diagram** ([example-Lucidchart](http://www.lucidchart.com/publicSegments/view/4f6e3ae9-8544-46f2-a469-1daf0ac9c29f/image.png), [more info](http://www.agilemodeling.com/artifacts/sequenceDiagram.htm)) - algorithms / processes high level implementation + - **state diagram** ([more info](http://www.agilemodeling.com/artifacts/stateMachineDiagram.htm)) - state minded processes / algorithms + - **mock ups / wire frames** ([example-Google Draw](https://docs.google.com/drawings/pub?id=1y-GFJZeIcqSIk2hFcf9SWjWnpemvaBU-ffdPjv1u4hs&w=960&h=720)) - for projects with GUIs + +#### Audiences + + - **System Architect** - mostly interested in the software stack, deployment and the database design + - **Team Members** - mostly interested in the more lower level stuff like modules and API's + - **Other Teams** - mostly interested in API's + +Feedback on design is great for everyone involved, setup meetings with relevant parties to do DR (design review) + +#### UML and Other Vegetables + +UML is complex, you don't need to know it perfectly, start with what makes sense, and slowly pick up tutorials. Remember, its a visual tool that helps you communicate, not everyone who will look at your designs has the time (or cares for) to read throughly a book about UML and its specifics. In Einstein's words "Make everything as simple as possible, but not simpler." + +#### Disclosure + +Design does *not* last. Nobody is going to update the design as the system grows and evolves. Having said that, can still be used a good reference. + + + + + + diff --git a/source/_posts/making-the-case-for-kotlin.md b/source/_posts/making-the-case-for-kotlin.md new file mode 100644 index 0000000..4535091 --- /dev/null +++ b/source/_posts/making-the-case-for-kotlin.md @@ -0,0 +1,189 @@ +--- +title: "Making The Case For Kotlin" +categories: ["code"] +tags: ["kotlin","scala"] +date : 2012/11/14 +--- + +The Kotlin project page has this to say about Kotlin: "is a statically typed programming language that compiles to JVM byte codes and JavaScript." [project page](http://kotlin.jetbrains.org/) + +Kotlin is a very interesting project, I wanted to check it out, so I decided to write this blog post exploring some design aspects behind Kotlin, and specifically how it compares to the leading JVM "hacker" language - Scala. + +## Who Is Behind Kotlin? + +Even though there are several decent free IDE's available today (Eclipse, NetBeans, Visual Studio Express..), it (could be) surprising that JetBrains is able to rack in between 199$ for a personal license and up to 699$ for a commercial license (for companies) including 1 year update subscription (I have indeed bought one personal license for my development needs). If you are developing on top of VisualStudio you are (most) probably familiar with the 250$+ plugin ReSharper. + +I would argue that this makes them a good candidate, with insight into good and bad language features, but lets not get ahead of ourselves and set the expectations too high. + +## Why Another JVM Language? + +JVM is a well known platform, its weaknesses and scalability strategies are available and documented well on the web, its being used as classic Java run-time VM and for some other popular languages like Scala, Groovy, Clojure and others. Many modules have been already written in Java, normally JVM run languages are compatible with the existing Java ecosystem. + +## Why Static Typing? + +In the old (and still ongoing) battle of dynamic and static languages, the consensus is that dynamic languages are faster and easier way to get things done, on the static languages side they allow programmers harness the power of intelligent IDEs to scale up and maintain their code. Type-inferred languages try to find a middle ground approach so that you don't need to write so much boilerplate code (cue JAVA) to support the static typing, while having the benefits of a full compile time type enforcement. + +## Finally, Kotlin Vs. Scala - Fight! + +Scala today is the fastest growing JVM language and is probably competing for the same crowd as Kotlin, Kotlin put up a comparison page [link](http://confluence.jetbrains.net/display/Kotlin/Comparison+to+Scala) so we will simply go over the omissions part and try to make sense of it. + +### OMITTED - Implicit conversions, parameters + +*What is it?* Scala implicits is (IMHO) a single biggest weakness in the language, once they are used, along with the ability to boast symbols for syntax, it quickly stops being human readable and more like a DSL. Which requires many trips to the docs or even worse - the source code. + +`SBT` (Scala Build Tool) is the Maven/Ant of the Scala world [link](http://www.scala-sbt.org/release/docs/Examples/Full-Configuration-Example.html), its very difficult to get things done with it without many trips to google/documentation + +```scala +lazy val pricing = Project ( + "pricing", + file ("cdap2-pricing"), + settings = buildSettings ++ Seq (libraryDependencies ++= pricingDeps) + ) dependsOn (common, compact, server) +``` + +`Lift` Is a popular web framework, here's a snippet I took from one of its documentation pages [link](http://cookbook.liftweb.net/Plain+old+form+processing.html) (see the `"#result *" #> x` part) + +```scala +def render = inputParam match { + case Full(x) => + println("Input is: "+x) + "#result *" #> x + + case _ => + println("No input present! Rendering input form HTML") + PassThru +} +``` + + +*Thoughts - * Indeed for many people this is the part that makes Kotlin a better "scalable" language, by not allowing programmers and libraries hinder the code unreadable without an IDE + +### OMITTED - Overridable Type Members + +*What is it?* Type members in Scala allow you to manage local generic types by assign a type to a class/abstract class/trait member + +```scala +abstract class HasTypedMember { + type typeMe +} +``` + +An extending class will provide a specific type for this type type member + +```scala +class ExtendingClass extends HasTypedMember { + type typeMe = Int +} +``` + +*Thoughts - * Frankly, its not really clear why Kotlin decided to omit having typed members, I could be missing something.. Never the less Kotlin usually takes the minimalistic and simplistic route, my guess is that if this is indeed useful, the door is open to add it later.. + +### OMITTED - Path-dependent types + +*What is it?* Pointing to a class under another class in Java would mean a specific type (Parent.Child), well not so in Scala. Scala makes the nested class of two same class instances different, much like you would expect two members of two different instances of a single class. Confused? here is some code to explain this better + +Java: + +```java +public class Outer { + public class Inner {} + public void useInner(Outer.Inner inner){...} +} +//.. somewhere in the code +o1 = new Outer() +o2 = new Outer() +i1 = new o1.inner() +i2 = new o2.inner() +// this would work fine +o1.useInner(i2) // works +o2.useInner(i1) // works +``` + +Scala: +```scala +class Outer { + class Inner {} + def useInner(inner : Inner){...} +} +//.. somewhere in the code.. +val o1 = new Outer +val o2 = new Outer +val i1 = new o1.Inner +val i2 = new o2.Inner +o1.useInner(i1) // works fine +o2.useInner(i2) // works fine +o1.useInner(i2) // THROWS type mismatch exception! +``` + +*Thoughts - * It makes some sense that the path to a type that can only exist in an instance is enforced as such.. It would be interesting to know why Kotlin decided to implement this feature in a similar way to Java (conservative?) + +### OMITTED - Existential types + +*What is it?* This is yet another trade-off for Scala to be able to inter-op with Java +Let's see an example: + +```scala +// Create a case class with generic type +case class Z[T]( blah:T ) +// create an instance and cast it to a type created with existential type annotation +Seq( Z[Int]( 5 ), Z[String]("blah") ) : Seq[Z[X] forSome { type X >: String with Int }] +res1 = Seq[Z[_ >: String with Int]] = List(Z(5), Z(blah)) +``` + +*Thoughts - * Scala supports type variance to describe generic type bounds, Scala designer (Odersky) chose to add existential types support due to three main reasons [link](http://www.artima.com/scalazine/articles/scalas_type_system.html): erasure, raw types (lack of generics) and Java's "wildcard" types. + +Kotlin took the more "purist" approach (also known as reified types) of dropping support for erasure, raw types, wildcard types and finally existential types. + +### OMITTED - Complicated logic for initialization of traits + +*What is it?* Traits are similar to abstract classes, except that you may use several traits on the same class at the same time (like interfaces). + +In Scala, traits are constructed before the extended classes + +```scala +trait Averager { + val first : Int + val second : Int + val avg : Int = first / second +} +// if we were to naively construct a class like so: +class Naive extends Averager { + val first = 20 + val second = 10 +} +// create instance +> new Naive() +java.lang.ArithmeticException: / by zero +// what happened was that our val "avg" was computed at construction time before seeing the values we put in Naive +``` + +Alternatively the trait can be used with a pre-initialized anonymous class `val res = new { val first = 20 ; val second = 10 } with Averager`, also the val which depends on other vals (avg in our case) can be made "lazy" with the (obvious) lazy keyword `lazy val avg : Int = first / second`. + +As a side note, using def for "avg" could work, but it would mean evaluating whatever is defined under "avg" every-time this method is called. + +*Thoughts - * Traits in Scala can have state which could be made tricky. If you read this blog post [link](http://blog.jetbrains.com/kotlin/2011/08/multiple-inheritance-part-2-possible-directions/) you will see Kotlin developers thoughts and a nice insight into what went into designing the inheritance in Kotlin. (don't forget to read the comments!) + +### *OMITTED* - Custom symbolic operations + +*What is it?* Scala does not have a built in handling of symbols (like =,+,/ etc), they are actually method calls, as you can have symbols used for method names. + +*Thoughts - * This may sound useful at first, but can and (arguably) will create very confusing looking code, like we saw before in the `Lift` and `SBT` examples. + +### *OMITTED* - Built-in XML + +*What is it?* Exactly as it sounds, Scala built in XML native support, so you can do stuff like this + +```scala +val holdsXml = some text +``` + +*Thoughts - * Having a built in support for XML as a language feature is interesting, and sometimes useful. An interview [link](http://www.techworld.com.au/article/397692/a-z_programming_languages_from_pizza_scala/) with Odersky reveals that he would consider removing the native XML support in Scala if had written Scala now. + + +## Bottom Line + +Kotlin is an idiomatic language that draws from the many good features in todays leading languages, specifically from Scala which is a leading language on the JVM platform today. We've looked into omissions on Kotlin's part versus Scala + +I've very much enjoyed writing this blog post and learning about the evolution that the design of Java/Scala/Kotlin languages, and the choices the respective authors made along the way. + +To get a better feeling of Kotlin I might pick it up for a small project, at which point I will surely blog about it some more.. diff --git a/source/_posts/setting-up-intellij-for-android-and-scala.md b/source/_posts/setting-up-intellij-for-android-and-scala.md new file mode 100644 index 0000000..46b8fb9 --- /dev/null +++ b/source/_posts/setting-up-intellij-for-android-and-scala.md @@ -0,0 +1,65 @@ +--- +title: "Setting Up IntelliJ For Android And Scala On Ubuntu" +categories: ["code"] +tags: ["intellij","android","scala"] +date: 2012/02/10 +--- + +### Prerequisites + +#### Android SDK + + 1. get the latest [SDK](https://developer.android.com/sdk/index.html) + + 2. after extracting, run `./tools/android` + + 3. from the list select the Android version your afer, right now its **Android 4.0.3 (API 15)** (I also installed Android 2.3.3), click on **Install packages** to get the installation started + + 4. setup your env ``echo "export ANDROID_HOME=~/Applications/android-sdk-linux/" >> ~/.bashrc`` + + 5. refresh your currect session `source ~/.bashrc` + +#### Adding SBT support to IntelliJ + + 1. open **File** > **Settings** > **Plugin** + + 2. click on **Browser repositories** + + 3. find **SBT**, right click and select **Download and install** + +#### Setup [SBT](https://github.com/harrah/xsbt/wiki/Getting-Started-Setup) + + 1. create folder `~/bin/` (if you dont have `~/bin` in your PATH, then you can do this: `echo "PATH=$PATH:~/bin" >> ~/.bashrc` (refresh your currect session `source ~/.bashrc`) + + 2. [download](https://github.com/harrah/xsbt/wiki/Getting-Started-Setup) `sbt-launcher.jar` and place it under `~/bin` + + 3. create launcher: `touch ~/bin/sbt` and then ``echo 'java -Xmx512M -jar `dirname $0`/sbt-launch.jar "$@"' >> ~/bin/sbt`` + + 4. make it executable: `chmod u+x ~/bin/sbt` + +#### Setup [android-plugin](https://github.com/jberkel/android-plugin/wiki/Getting-started) for Scala support + + 1. install giter8 `curl https://raw.github.com/n8han/conscript/master/setup.sh | sh` + + 2. run it `~/bin/cs n8han/giter8` + +#### Setup [sbt-idea](https://github.com/mpeltonen/sbt-idea) + + 1. create folder (if it doesnt exist) `mkdir -p ~/.sbt/plugins/` + + 2. create the file `~/.sbt/plugins/build.sbt` and add the following lines to it: + + resolvers += "sbt-idea-repo" at "http://mpeltonen.github.com/maven/" + addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.0.0") + +### Finally, now that you have all the tools you need, proceed to creating a new project + + 1. run the android plugin project setup tool: `~/bin/g8 jberkel/android-app`, follow the onscreen questions (or press return for defaults) + this will create the project folder and create the files needed to build an android project + + 2. open the new folder `cd ` + + 3. setup IntelliJ project support `sbt gen-idea` + + 4. open the project in IntelliJ + diff --git a/source/_posts/typing-in-javascript.md b/source/_posts/typing-in-javascript.md new file mode 100644 index 0000000..c2d3fbc --- /dev/null +++ b/source/_posts/typing-in-javascript.md @@ -0,0 +1,165 @@ +--- +title: "Typing in JavaScript?" +categories : ['code'] +tags: ["nodejs","javascript"] +date: 2012/09/22 +--- + +"Strong typing is for people with weak memories" - not sure who originally said that, but you have got to love the enthusiasm people have for dynamic languges. What is there not to love? powerful basic language constructs such as closure, prototypal inheritance, objects as first class citizens and anonymous functions, mainly, with which you can simulate almost anything static languages have to offer! + +Proof you say? no problem! here are few implementations that are worthy of noting: + +**Compile time type checking** https://developers.google.com/closure/compiler/docs/js-for-compiler#types + +**Classic OO inheritance** http://mootools.net/docs/core/Class/Class & http://coffeescript.org/#classes + +(if you have other interesting examples let me know!) + +### Convention over configuration + +The combination of CoffeeScript's "classical" Class like system and Backbone.JS's MVC style structure, give (IMHO) the best value in terms of complexity and pure fun writing apps. The challenges I come across while building large complex applications is the inherited nature of a true dynamic language "type_lessness_". + +The ugly monster, that is -a large application written in JavaScript, pushed me into struggling with these two main challenges: + + * Many-to-many PubSub communication + * 3rd party API's + +### Many-to-many PubSub communication + +While the PubSub (also known as observer pattern) in JavaScript is a powerful tool to decouple your code, having multiple callers and listeners on the same event makes it really difficult to keep track of the actual common API for these events. Lats try to reflect on this while looking at an actual implementation. + +(please excuse my CoffeeScript) + + +**Pub/Sub implementation in Backbone/Underscore.JS** + +```coffee +pubsub = _.extend {}, Backbone.Events +pubsub.on 'myEvent', (param1, param2)-> + x1 = param1 + param2 + # do something with x1 + +# in some other obscure location in the code +pubsub.on 'myEvent', (param1)-> + x2 = param1 + # do something with x2 + +# ... +pubsub.trigger 'myEvent', 5 +# ... +pubsub.trigger 'myEvent', 5,10 +``` + +The "pubsub.on(event name, callback)" API is very common amongst different JS pub/sub frameworks. here's an example of how a generic implementation might look like: + + +```coffee +pubsub = { + _listeners : {} + on : (channel, callback)-> + @_listeners[ channel ] ?= [] + @_listeners[ channel ].push callback + + trigger : (channel, params...)-> + @_listeners[ channel ]?.forEach (cb)-> cb.apply(null, params) +} + +pubsub.on 'myEvent', (param1, param2)-> + x1 = param1 + param2 + # ... do something with x1.. + +# in some other obscure location in the code +pubsub.on 'myEvent', (param1)-> + x2 = param1 + # .... do something with x2 + +# ... +pubsub.trigger 'myEvent', 5 +# ... +pubsub.trigger 'myEvent', 5,10 +``` + + +If your using the very popular Socket.IO library, for realtime client/server communication, you are using the exact same API, in which case it makes it even more complicated to keep track of, since the ambiguity is manifested on both the client and the server. + +### 3rd party API's + +This is how a type related bug might look like in a dynamic language + +```coffee +a = "42" # returned from some API +a == 42 +=> false +``` + +## Enter "Contractor" & "Backbone-typed" + +Contractor +https://github.com/romansky/Contractor + +Backbone-typed +https://github.com/romansky/backbone-typed + +I'm introducing a convention here, that helps keep the API consistent across the application, the approach here is to create wrapper functions, that are used as both the API documentation and run-time validation facility for required and optional arguments. + +Example: + +```coffee +# declare the events and their APIs +Messages = { + LoginEvent : Contractor.Create( "LoginEvent", Contractor.Required("user ID"), Contractor.Optional("additional info") ) + AppErrorEvent : Contractor.Create( "AppErrorEvent", Contractor.Required("user ID"), Contractor.Required("error description"), Contractor.Optional("exception") ) +} + +# register on event API +pubsub.on Messages.LoginEvent, (userID, info)-> #... do stuff here +pubsub.on Messages.AppErrorEvent, (userID, description, exception)-> #... do stuff here + +# trigger the event +pubsub.apply(pubsub, Messages.LoginEvent(112233, "mobile")) +pubsub.apply(pubsub, Messages.LoginEvent(112233)) + +pubsub.apply(pubsub, Messages.AppErrorEvent(112233, "user did not have access to resource", e)) +pubsub.apply(pubsub, Messages.AppErrorEvent(112233)) # since we did not provide the second required argument, this will log an error and return null +``` + +With NodeJS, I am using the same code on the front and back end, which makes it really easy to manage my Socket.IO communication. + +If you have been paying attention, there's still the type issue. Since I work allot with Backbone, it made a lot of sense to write "backbone-typed" which adds optional typing to Backbone-Models. +Example: + +```coffee +Wearing = { + "bracelet" : "bracelet" + "watch" : "watch" +} + +class User extends TypedModel + defaults: { + name: null + email: null + lotteryNumber: null + isAwesome: null + } + + types: { + name: Types.String + email: Types.String + lotteryNumber: Types.Integer + isAwesome: Types.Boolean + wearing : Types.Enum(Wearing) + } + +user1 = new User({name: "foo", email: "foo@bar.com", lotteryNumber: 12345, isAwesome: true, wearing: Wearing.watch}) +user1.toJSON() #=> {name: "foo", email: "foo@bar.com", lotteryNumber: 12345, isAwesome: true, wearing: "watch"} - nothing special going on here.. + +user2 = new User({name: "foo", email: "foo@bar.com", lotteryNumber: "54321", isAwesome: "false", wearing: "thong"}) +user2.toJSON() #=> {name: "bar", email: "bar@foo.com", lotteryNumber: 54321, isAwesome: false, wearing: null} - shit happens! + +user2.set({wearing: Wearing.bracelet}) + +if user2.get("lotteryNumber") == 54321 and user2.get("wearing") == Wearing.bracelet then user2.set({isAwesome: "true"}) +user2.toJSON() #=> {name: "bar", email: "bar@foo.com", lotteryNumber: 54321, isAwesome: true, wearing: "bracelet"} - awesome for sure.. +``` + +If you want to know a little more about how these work, please visit the respective Git repositories, and let me know what you think! diff --git a/source/_posts/user-stories.md b/source/_posts/user-stories.md new file mode 100644 index 0000000..f2e8301 --- /dev/null +++ b/source/_posts/user-stories.md @@ -0,0 +1,54 @@ +--- +title: "User Stories" +categories: ["design"] +tags: ["design"] +date: 2012/02/25 +--- + +This post is a follow-up on ["Bootstrapping A New Project"](/code/2012/02/07/bootstrapping-a-new-project/) + +"User Story" is an important communication tool, it is used between you (the developer) and who ever is making the requirements (the stakeholder). +A "contract" which (unlike one written by a lawyer) is clear, understood and agreed upon. +On our side (development) user stories embody a work unit that can be further broken down and estimated. + +Key value points: + + * Language - its key that user stories are written in a language everyone can understand (stakeholders, developers etc..) + * Perspective - user stories need to be written from the perspective of the "feature user", so they represent real world value + * Estimation - to help you get better and more precise estimation for the project overall + * Prioritization - what needs to be delivered soon/later + +#### Lets imagine we just got a call from the product manager, where he is telling us about this new survey tool: + + 1. It needs to collect answers from users via a link sent to our users by e-mail + 2. It needs to be summarized and sent to ... once a month on the 1st + 3. Managers need to be able to see past surveys + +Hmm.. OK, thats pretty clear, but suppose this can be written clearer, lats try using the following template: (not my invention;)) +``As a [role], I want [Requirement / Feature], So that [Justigication/ Reason]`` + +The requirements now look like this: + + 1. As an end user, I will be able to follow a link I got via e-mail, so that I can fill a survey and express my opinion + 2. As a manager, I will receive an e-mail with a digest of surveys, so that I can evaluate performance + 3. As a manager, I will be able to access old surveys, so that I can compare and evaluate performance over time + +By reading the above I can clearly understand the required functionality and the **motivation** behind writing it, this gives me a good **starting point**. +Since I am the one to implement this, I will need to provide estimates, so that product can plan ahead and communicate the product road map onward. +Unfortunately, "The Devil is In The Details" as they say, so giving estimates based on points 1,2,3 is still difficult. +Points 2 & 3 are "huge" stories, also known as "epics", we need to further break down these stories, while still using the same language and practices: + + 1. As an end user, I will receive an email with a link as a result of some communication with a company, so that I can follow it and fill a survey + 2. As an end user, I get a set of questions to answer, so that my opinion is collected + 3. As a manager, I will receive an e-mail with a digest of surveys, so that I can evaluate companies performance + 4. As a manager, I will have a survey management portal, so that I can overview previous results and compare to + 5. As a manager, I can select a specific survey, so that I can extract meaningful actionable information + +The functionality expected of deliverables from the points above is clear, and now I feel more comfortable estimating the time it will take me to complete each. +Along with the estimation, these can now be prioritized against each other and against other ongoing projects.. + +### Summery +The beginning of a new project is the perfect time to introduce user stories, it will help with getting the conversation going, further clarifying the scope and requirements. +While the project scope might change, user stories are (optimally) modular, decoupled and encapsulated, this should help with managing changes and their effect on the overall project. + + diff --git a/source/_posts/using-contractor-with-child-processes.md b/source/_posts/using-contractor-with-child-processes.md new file mode 100644 index 0000000..5062199 --- /dev/null +++ b/source/_posts/using-contractor-with-child-processes.md @@ -0,0 +1,99 @@ +--- +title: "Using Contractor With Child Processes" +category: ["code"] +tags: ["nodejs"] +date: 2012/10/06 +--- + +`Contractor` is a factory of contracts, that helps with documenting our APIs. +The child_process facility built into node is using `EventEmitter` (v1), and does not support callbacks, so it is a great fit for `Contractor`! +Since I needed it for a distributed worker framework I am working on, I added a helper facility called "Lawyer", it basically reads the contracts and routes them to the provided object. + +__Let us take the parent and child example code from Node's official documentation (http://nodejs.org/api/child_process.html) and see how simple it would be to add some documentation.__ + +Parent + +```javascript +var cp = require('child_process'); + +var n = cp.fork(__dirname + '/sub.js'); + +n.on('message', function(m) { + console.log('PARENT got message:', m); +}); + +n.send({ hello: 'world' }); +``` + +Child + +```javascript +process.on('message', function(m) { + console.log('CHILD got message:', m); +}); + +process.send({ foo: 'bar' }); +``` + +__Node's child_process facility provied us with a very basic means of communication, we simply call `n.send({any: "thing"})` and it automagically recieved by the forked child. Let's spice it up with a more RPC message passing style, with contracts and lawyers__ + +contracts.js + +```javascript +var Contractor = require('contractor').Contractor; +exports.ChildPublish = { + "GreetingResponse" : Contractor.Create("GreetingResponse", Contractor.Required("childs greeting response")). + "StatusResponse" : Contractor.Create("StatusResponse", Contractor.Required("errors count"), Contractor.Required("number of completed jobs")) +} + +exports.ChildSubscribe = { + "Greeting": Contractor.Create("Greeting", Contractor.Required("greeting word")), + "StatusQuery" : Contractor.Create("StatusQuery") +} + +``` + + +parent.js + +```javascript +var contracts = require('./contracts'); +var Contractor = require('contractor').Contractor; +var Lawyer = require('contractor').Lawyer; +var cp = require('child_process'); + +var n = cp.fork(__dirname + '/sub.js'); + +n.on('message', function(m) { + Lawyer.Read(m, { + "GreetingResponse" : function(childResponse){ console.log("child responded:" + childResponse) }, + "StatusResponse" : function(errorCount, numCompletedJobs){ console.log("childs status, errors:"+errorCount+" jobs done:" + numCompletedJobs) } + }); +}); +n.send(contracts.ChildSubscribe.Greeting("this is your father!")); +n.send(contracts.ChildSubscribe.StatusQuery()); +``` + +sub.js + +```javascript +var contracts = require('./contracts'); +var Contractor = require('contractor').Contractor; +var Lawyer = require('contractor').Lawyer; +process.on('message', function(m) { + Lawyer.Read(m, { + "Greeting" : function(parentGreeting){ process.send( contracts.ChildPublish.GreetingResponse("Hi "+parentGreeting+" can I have 10$?")) }, + "StatusQuery" : function(){ process.send( contracts.ChildPublish.StatusResponse(0, 42) ) } + }); +}); +``` + +If we were to run these + +```javascript +#> node parent.js +child responded:Hi this is your father! can I have 10$? +childs status, errors:0 jobs done:42 +``` + +If you found Contractor / Lawyer interesting and/or useful, I would love to hear about it! diff --git a/tags/android/index.html b/tags/android/index.html deleted file mode 100644 index 049e8a3..0000000 --- a/tags/android/index.html +++ /dev/null @@ -1,290 +0,0 @@ - - - - - - Tag: android | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- Setting Up IntelliJ For Android And Scala On Ubuntu -

- - -
- -
- -

Prerequisites

Android SDK

    -
  1. get the latest SDK

    -
  2. -
  3. after extracting, run ./tools/android

    -
  4. -
  5. from the list select the Android version your afer, right now its Android 4.0.3 (API 15) (I also installed Android 2.3.3), click on Install packages to get the installation started

    -
  6. -
  7. setup your env echo "export ANDROID_HOME=~/Applications/android-sdk-linux/" >> ~/.bashrc

    -
  8. -
  9. refresh your currect session source ~/.bashrc

    -
  10. -
-

Adding SBT support to IntelliJ

    -
  1. open File > Settings > Plugin

    -
  2. -
  3. click on Browser repositories

    -
  4. -
  5. find SBT, right click and select Download and install

    -
  6. -
-

Setup SBT

    -
  1. create folder ~/bin/ (if you dont have ~/bin in your PATH, then you can do this: echo "PATH=$PATH:~/bin" >> ~/.bashrc (refresh your currect session source ~/.bashrc)

    -
  2. -
  3. download sbt-launcher.jar and place it under ~/bin

    -
  4. -
  5. create launcher: touch ~/bin/sbt and then echo 'java -Xmx512M -jar `dirname $0`/sbt-launch.jar "$@"' >> ~/bin/sbt

    -
  6. -
  7. make it executable: chmod u+x ~/bin/sbt

    -
  8. -
-

Setup android-plugin for Scala support

    -
  1. install giter8 curl https://raw.github.com/n8han/conscript/master/setup.sh | sh

    -
  2. -
  3. run it ~/bin/cs n8han/giter8

    -
  4. -
-

Setup sbt-idea

    -
  1. create folder (if it doesnt exist) mkdir -p ~/.sbt/plugins/

    -
  2. -
  3. create the file ~/.sbt/plugins/build.sbt and add the following lines to it:

    -
    resolvers += "sbt-idea-repo" at "http://mpeltonen.github.com/maven/"
    -addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.0.0")
    -
  4. -
-

Finally, now that you have all the tools you need, proceed to creating a new project

    -
  1. run the android plugin project setup tool: ~/bin/g8 jberkel/android-app, follow the onscreen questions (or press return for defaults)
    this will create the project folder and create the files needed to build an android project

    -
  2. -
  3. open the new folder cd <project name>

    -
  4. -
  5. setup IntelliJ project support sbt gen-idea

    -
  6. -
  7. open the project in IntelliJ

    -
  8. -
- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/tags/design/index.html b/tags/design/index.html deleted file mode 100644 index 576a68d..0000000 --- a/tags/design/index.html +++ /dev/null @@ -1,336 +0,0 @@ - - - - - - Tag: design | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- HLD High Level Design -

- - -
- -
- -

This post is a follow-up on “Bootstrapping A New Project”

-

Previously…

I’ve written about “User Stories”, a tool that helps with expectation setting when working with (mostly) non-technical stakeholders.

-

Designing Toward

    -
  • database (schema, tables, indexes..)
  • -
  • application layout
  • -
  • deployment
  • -
  • modules
  • -
  • internal and external APIs
  • -
  • application infra (frameworks, tools etc..)
  • -
-

Design Tools At Your Disposal

-

Audiences

    -
  • System Architect - mostly interested in the software stack, deployment and the database design
  • -
  • Team Members - mostly interested in the more lower level stuff like modules and API’s
  • -
  • Other Teams - mostly interested in API’s
  • -
-

Feedback on design is great for everyone involved, setup meetings with relevant parties to do DR (design review)

-

UML and Other Vegetables

UML is complex, you don’t need to know it perfectly, start with what makes sense, and slowly pick up tutorials. Remember, its a visual tool that helps you communicate, not everyone who will look at your designs has the time (or cares for) to read throughly a book about UML and its specifics. In Einstein’s words “Make everything as simple as possible, but not simpler.”

-

Disclosure

Design does not last. Nobody is going to update the design as the system grows and evolves. Having said that, can still be used a good reference.

- - -
- -
- -
- - - -
- -
- - -
- - -

- User Stories -

- - -
- -
- -

This post is a follow-up on “Bootstrapping A New Project”

-

“User Story” is an important communication tool, it is used between you (the developer) and who ever is making the requirements (the stakeholder).
A “contract” which (unlike one written by a lawyer) is clear, understood and agreed upon.
On our side (development) user stories embody a work unit that can be further broken down and estimated.

-

Key value points:

-
    -
  • Language - its key that user stories are written in a language everyone can understand (stakeholders, developers etc..)
  • -
  • Perspective - user stories need to be written from the perspective of the “feature user”, so they represent real world value
  • -
  • Estimation - to help you get better and more precise estimation for the project overall
  • -
  • Prioritization - what needs to be delivered soon/later
  • -
-

Lets imagine we just got a call from the product manager, where he is telling us about this new survey tool:

    -
  1. It needs to collect answers from users via a link sent to our users by e-mail
  2. -
  3. It needs to be summarized and sent to … once a month on the 1st
  4. -
  5. Managers need to be able to see past surveys
  6. -
-

Hmm.. OK, thats pretty clear, but suppose this can be written clearer, lats try using the following template: (not my invention;))
As a [role], I want [Requirement / Feature], So that [Justigication/ Reason]

-

The requirements now look like this:

-
    -
  1. As an end user, I will be able to follow a link I got via e-mail, so that I can fill a survey and express my opinion
  2. -
  3. As a manager, I will receive an e-mail with a digest of surveys, so that I can evaluate performance
  4. -
  5. As a manager, I will be able to access old surveys, so that I can compare and evaluate performance over time
  6. -
-

By reading the above I can clearly understand the required functionality and the motivation behind writing it, this gives me a good starting point.
Since I am the one to implement this, I will need to provide estimates, so that product can plan ahead and communicate the product road map onward.
Unfortunately, “The Devil is In The Details” as they say, so giving estimates based on points 1,2,3 is still difficult.
Points 2 & 3 are “huge” stories, also known as “epics”, we need to further break down these stories, while still using the same language and practices:

-
    -
  1. As an end user, I will receive an email with a link as a result of some communication with a company, so that I can follow it and fill a survey
  2. -
  3. As an end user, I get a set of questions to answer, so that my opinion is collected
  4. -
  5. As a manager, I will receive an e-mail with a digest of surveys, so that I can evaluate companies performance
  6. -
  7. As a manager, I will have a survey management portal, so that I can overview previous results and compare to
  8. -
  9. As a manager, I can select a specific survey, so that I can extract meaningful actionable information
  10. -
-

The functionality expected of deliverables from the points above is clear, and now I feel more comfortable estimating the time it will take me to complete each.
Along with the estimation, these can now be prioritized against each other and against other ongoing projects..

-

Summery

The beginning of a new project is the perfect time to introduce user stories, it will help with getting the conversation going, further clarifying the scope and requirements.
While the project scope might change, user stories are (optimally) modular, decoupled and encapsulated, this should help with managing changes and their effect on the overall project.

- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/tags/distributed/index.html b/tags/distributed/index.html deleted file mode 100644 index 7cf78e6..0000000 --- a/tags/distributed/index.html +++ /dev/null @@ -1,259 +0,0 @@ - - - - - - Tag: distributed | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- Distributed Scheduled Queue With Redis -

- - -
- -
- -

Unlike the classic FIFO queue, jobs are scheduled for execution in the future, possibly on a specific day, week, month year.. similar to “cron” in a Unix OS. Unlike the normal Unix “cron” implementation, we want it to be distributed, so that several machines(workers) can query and then execute these jobs.

-

Implementing

    -
  • Planning - The standart Unix cron format is very flexible and has been time tested for describing schedules
  • -
  • Managing/Coordinating - The queues are stored in Redis Sorted Sets, Lua will be used for atomic execution
  • -
  • Execution - Getting the pieces together
  • -
-

Planning

“Unix Time” format, is the number of milliseconds since (UTC) 1 January 1970
Example (* the actual unix time number is in UTC, while the formatted date is localized)

-
1350745504313 == Sat Oct 20 2012 17:05:04 GMT+0200 (IST)
-

Using this format will make it possible to slice the queue with specific point in time in mind.

-

Managing/Coordinating

Redis’st will be holding the job IDs as members scored by their corresponding execution times(in unix time format).

-

-

Redis commands that will come in handy

-
    -
  • Scheduling job run: ZADD cron:queue 1350745504313 1000
  • -
  • Finding a job to run: ZRANGEBYSCORE cron:queue 1350745504314 LIMIT 0 1
  • -
-

Execution

Without going into any platform specific implementation details, this is what the process looks like

-
    -
  1. When a new schedule is added, jobs are created for upcoming execution times, which are calculated for the next two hours and added to our cron queue
  2. -
  3. A special job is added to be run every hour, it will run over all the schedules, calculate and add their execution time to the scheduled queue, two hours into the future
  4. -
  5. Workers will be polling the database every second to find jobs
  6. -
  7. Once an entry comes up, it is removed from the queue and added to another Sorted Set queue, so we dont “forget” any jobs..
  8. -
-

As for items 3 and 4 above, it is not possible to implement this logic in the client, because for example when we find a job that needs to be executed, another worker might grab it, or worse they both “grabbed” it and are now working on the same job. To work around this, we need to write a little Lua script, since Lua scripts are executed serially, just like any other Redis command

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
local currentTimeMS = KEYS[1]
local workingQueue = "scheduler:working"
local scheduleQueue = "schedule"
-- for easier reading, epoch time is in milliseconds
local second = 1000

local function foundJob(jobID)
-- implement removing the job from all other queues
-- implement updating job status where the actual job object is stored
end

local function findNextJob()
local result
-- check if there are any "stale" jobs
result = redis.call( 'ZRANGEBYSCORE', workingQueue, 0, currentTimeMS-60*second, 'LIMIT',0,1 )
if result[1] ~= nil then return foundJob(result[1]) end
-- check the normal queue
result = redis.call( 'ZRANGEBYSCORE', scheduleQueue, 0, currentTimeMS, 'LIMIT',0,1 )
if result[1] ~= nil then return foundJob(result[1]) end
-- if we didnt find anything return nil
return nil
end

return findNextJob()
-

And there you have it! a “Distributed scheduled queue” implemented on top of Redis.

-

If you are going to implement this with node, I created a little cron format parser you can find here:
JSCron

- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/tags/intellij/index.html b/tags/intellij/index.html deleted file mode 100644 index e8b621e..0000000 --- a/tags/intellij/index.html +++ /dev/null @@ -1,290 +0,0 @@ - - - - - - Tag: intellij | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- Setting Up IntelliJ For Android And Scala On Ubuntu -

- - -
- -
- -

Prerequisites

Android SDK

    -
  1. get the latest SDK

    -
  2. -
  3. after extracting, run ./tools/android

    -
  4. -
  5. from the list select the Android version your afer, right now its Android 4.0.3 (API 15) (I also installed Android 2.3.3), click on Install packages to get the installation started

    -
  6. -
  7. setup your env echo "export ANDROID_HOME=~/Applications/android-sdk-linux/" >> ~/.bashrc

    -
  8. -
  9. refresh your currect session source ~/.bashrc

    -
  10. -
-

Adding SBT support to IntelliJ

    -
  1. open File > Settings > Plugin

    -
  2. -
  3. click on Browser repositories

    -
  4. -
  5. find SBT, right click and select Download and install

    -
  6. -
-

Setup SBT

    -
  1. create folder ~/bin/ (if you dont have ~/bin in your PATH, then you can do this: echo "PATH=$PATH:~/bin" >> ~/.bashrc (refresh your currect session source ~/.bashrc)

    -
  2. -
  3. download sbt-launcher.jar and place it under ~/bin

    -
  4. -
  5. create launcher: touch ~/bin/sbt and then echo 'java -Xmx512M -jar `dirname $0`/sbt-launch.jar "$@"' >> ~/bin/sbt

    -
  6. -
  7. make it executable: chmod u+x ~/bin/sbt

    -
  8. -
-

Setup android-plugin for Scala support

    -
  1. install giter8 curl https://raw.github.com/n8han/conscript/master/setup.sh | sh

    -
  2. -
  3. run it ~/bin/cs n8han/giter8

    -
  4. -
-

Setup sbt-idea

    -
  1. create folder (if it doesnt exist) mkdir -p ~/.sbt/plugins/

    -
  2. -
  3. create the file ~/.sbt/plugins/build.sbt and add the following lines to it:

    -
    resolvers += "sbt-idea-repo" at "http://mpeltonen.github.com/maven/"
    -addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.0.0")
    -
  4. -
-

Finally, now that you have all the tools you need, proceed to creating a new project

    -
  1. run the android plugin project setup tool: ~/bin/g8 jberkel/android-app, follow the onscreen questions (or press return for defaults)
    this will create the project folder and create the files needed to build an android project

    -
  2. -
  3. open the new folder cd <project name>

    -
  4. -
  5. setup IntelliJ project support sbt gen-idea

    -
  6. -
  7. open the project in IntelliJ

    -
  8. -
- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/tags/javascript/index.html b/tags/javascript/index.html deleted file mode 100644 index 7d28d0d..0000000 --- a/tags/javascript/index.html +++ /dev/null @@ -1,265 +0,0 @@ - - - - - - Tag: javascript | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- Typing in JavaScript? -

- - -
- -
- -

“Strong typing is for people with weak memories” - not sure who originally said that, but you have got to love the enthusiasm people have for dynamic languges. What is there not to love? powerful basic language constructs such as closure, prototypal inheritance, objects as first class citizens and anonymous functions, mainly, with which you can simulate almost anything static languages have to offer!

-

Proof you say? no problem! here are few implementations that are worthy of noting:

-

Compile time type checking https://developers.google.com/closure/compiler/docs/js-for-compiler#types

-

Classic OO inheritance http://mootools.net/docs/core/Class/Class & http://coffeescript.org/#classes

-

(if you have other interesting examples let me know!)

-

Convention over configuration

The combination of CoffeeScript’s “classical” Class like system and Backbone.JS’s MVC style structure, give (IMHO) the best value in terms of complexity and pure fun writing apps. The challenges I come across while building large complex applications is the inherited nature of a true dynamic language “typelessness“.

-

The ugly monster, that is -a large application written in JavaScript, pushed me into struggling with these two main challenges:

-
    -
  • Many-to-many PubSub communication
  • -
  • 3rd party API’s
  • -
-

Many-to-many PubSub communication

While the PubSub (also known as observer pattern) in JavaScript is a powerful tool to decouple your code, having multiple callers and listeners on the same event makes it really difficult to keep track of the actual common API for these events. Lats try to reflect on this while looking at an actual implementation.

-

(please excuse my CoffeeScript)

-

Pub/Sub implementation in Backbone/Underscore.JS

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
pubsub = _.extend {}, Backbone.Events
pubsub.on 'myEvent', (param1, param2)->
x1 = param1 + param2
# do something with x1

# in some other obscure location in the code
pubsub.on 'myEvent', (param1)->
x2 = param1
# do something with x2

# ...
pubsub.trigger 'myEvent', 5
# ...
pubsub.trigger 'myEvent', 5,10
-

The “pubsub.on(event name, callback)” API is very common amongst different JS pub/sub frameworks. here’s an example of how a generic implementation might look like:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pubsub = {
_listeners : {}
on : (channel, callback)->
@_listeners[ channel ] ?= []
@_listeners[ channel ].push callback

trigger : (channel, params...)->
@_listeners[ channel ]?.forEach (cb)-> cb.apply(null, params)
}

pubsub.on 'myEvent', (param1, param2)->
x1 = param1 + param2
# ... do something with x1..

# in some other obscure location in the code
pubsub.on 'myEvent', (param1)->
x2 = param1
# .... do something with x2

# ...
pubsub.trigger 'myEvent', 5
# ...
pubsub.trigger 'myEvent', 5,10
-

If your using the very popular Socket.IO library, for realtime client/server communication, you are using the exact same API, in which case it makes it even more complicated to keep track of, since the ambiguity is manifested on both the client and the server.

-

3rd party API’s

This is how a type related bug might look like in a dynamic language

-
1
2
3
a = "42" # returned from some API
a == 42
=> false
-

Enter “Contractor” & “Backbone-typed”

Contractor
https://github.com/romansky/Contractor

-

Backbone-typed
https://github.com/romansky/backbone-typed

-

I’m introducing a convention here, that helps keep the API consistent across the application, the approach here is to create wrapper functions, that are used as both the API documentation and run-time validation facility for required and optional arguments.

-

Example:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# declare the events and their APIs
Messages = {
LoginEvent : Contractor.Create( "LoginEvent", Contractor.Required("user ID"), Contractor.Optional("additional info") )
AppErrorEvent : Contractor.Create( "AppErrorEvent", Contractor.Required("user ID"), Contractor.Required("error description"), Contractor.Optional("exception") )
}

# register on event API
pubsub.on Messages.LoginEvent, (userID, info)-> #... do stuff here
pubsub.on Messages.AppErrorEvent, (userID, description, exception)-> #... do stuff here

# trigger the event
pubsub.apply(pubsub, Messages.LoginEvent(112233, "mobile"))
pubsub.apply(pubsub, Messages.LoginEvent(112233))

pubsub.apply(pubsub, Messages.AppErrorEvent(112233, "user did not have access to resource", e))
pubsub.apply(pubsub, Messages.AppErrorEvent(112233)) # since we did not provide the second required argument, this will log an error and return null
-

With NodeJS, I am using the same code on the front and back end, which makes it really easy to manage my Socket.IO communication.

-

If you have been paying attention, there’s still the type issue. Since I work allot with Backbone, it made a lot of sense to write “backbone-typed” which adds optional typing to Backbone-Models.
Example:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Wearing = {
"bracelet" : "bracelet"
"watch" : "watch"
}

class User extends TypedModel
defaults: {
name: null
email: null
lotteryNumber: null
isAwesome: null
}

types: {
name: Types.String
email: Types.String
lotteryNumber: Types.Integer
isAwesome: Types.Boolean
wearing : Types.Enum(Wearing)
}

user1 = new User({name: "foo", email: "foo@bar.com", lotteryNumber: 12345, isAwesome: true, wearing: Wearing.watch})
user1.toJSON() #=> {name: "foo", email: "foo@bar.com", lotteryNumber: 12345, isAwesome: true, wearing: "watch"} - nothing special going on here..

user2 = new User({name: "foo", email: "foo@bar.com", lotteryNumber: "54321", isAwesome: "false", wearing: "thong"})
user2.toJSON() #=> {name: "bar", email: "bar@foo.com", lotteryNumber: 54321, isAwesome: false, wearing: null} - shit happens!

user2.set({wearing: Wearing.bracelet})

if user2.get("lotteryNumber") == 54321 and user2.get("wearing") == Wearing.bracelet then user2.set({isAwesome: "true"})
user2.toJSON() #=> {name: "bar", email: "bar@foo.com", lotteryNumber: 54321, isAwesome: true, wearing: "bracelet"} - awesome for sure..
-

If you want to know a little more about how these work, please visit the respective Git repositories, and let me know what you think!

- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/tags/kotlin/index.html b/tags/kotlin/index.html deleted file mode 100644 index 9d4dc62..0000000 --- a/tags/kotlin/index.html +++ /dev/null @@ -1,277 +0,0 @@ - - - - - - Tag: kotlin | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- Making The Case For Kotlin -

- - -
- -
- -

The Kotlin project page has this to say about Kotlin: “is a statically typed programming language that compiles to JVM byte codes and JavaScript.” project page

-

Kotlin is a very interesting project, I wanted to check it out, so I decided to write this blog post exploring some design aspects behind Kotlin, and specifically how it compares to the leading JVM “hacker” language - Scala.

-

Who Is Behind Kotlin?

Even though there are several decent free IDE’s available today (Eclipse, NetBeans, Visual Studio Express..), it (could be) surprising that JetBrains is able to rack in between 199$ for a personal license and up to 699$ for a commercial license (for companies) including 1 year update subscription (I have indeed bought one personal license for my development needs). If you are developing on top of VisualStudio you are (most) probably familiar with the 250$+ plugin ReSharper.

-

I would argue that this makes them a good candidate, with insight into good and bad language features, but lets not get ahead of ourselves and set the expectations too high.

-

Why Another JVM Language?

JVM is a well known platform, its weaknesses and scalability strategies are available and documented well on the web, its being used as classic Java run-time VM and for some other popular languages like Scala, Groovy, Clojure and others. Many modules have been already written in Java, normally JVM run languages are compatible with the existing Java ecosystem.

-

Why Static Typing?

In the old (and still ongoing) battle of dynamic and static languages, the consensus is that dynamic languages are faster and easier way to get things done, on the static languages side they allow programmers harness the power of intelligent IDEs to scale up and maintain their code. Type-inferred languages try to find a middle ground approach so that you don’t need to write so much boilerplate code (cue JAVA) to support the static typing, while having the benefits of a full compile time type enforcement.

-

Finally, Kotlin Vs. Scala - Fight!

Scala today is the fastest growing JVM language and is probably competing for the same crowd as Kotlin, Kotlin put up a comparison page link so we will simply go over the omissions part and try to make sense of it.

-

OMITTED - Implicit conversions, parameters

What is it? Scala implicits is (IMHO) a single biggest weakness in the language, once they are used, along with the ability to boast symbols for syntax, it quickly stops being human readable and more like a DSL. Which requires many trips to the docs or even worse - the source code.

-

SBT (Scala Build Tool) is the Maven/Ant of the Scala world link, its very difficult to get things done with it without many trips to google/documentation

-
1
2
3
4
5
lazy val pricing = Project (
"pricing",
file ("cdap2-pricing"),
settings = buildSettings ++ Seq (libraryDependencies ++= pricingDeps)
) dependsOn (common, compact, server)
-

Lift Is a popular web framework, here’s a snippet I took from one of its documentation pages link (see the "#result *" #> x part)

-
1
2
3
4
5
6
7
8
9
def render = inputParam match {
case Full(x) =>
println("Input is: "+x)
"#result *" #> x

case _ =>
println("No input present! Rendering input form HTML")
PassThru
}
-

Thoughts - Indeed for many people this is the part that makes Kotlin a better “scalable” language, by not allowing programmers and libraries hinder the code unreadable without an IDE

-

OMITTED - Overridable Type Members

What is it? Type members in Scala allow you to manage local generic types by assign a type to a class/abstract class/trait member

-
1
2
3
abstract class HasTypedMember {
type typeMe
}
-

An extending class will provide a specific type for this type type member

-
1
2
3
class ExtendingClass extends HasTypedMember {
type typeMe = Int
}
-

Thoughts - Frankly, its not really clear why Kotlin decided to omit having typed members, I could be missing something.. Never the less Kotlin usually takes the minimalistic and simplistic route, my guess is that if this is indeed useful, the door is open to add it later..

-

OMITTED - Path-dependent types

What is it? Pointing to a class under another class in Java would mean a specific type (Parent.Child), well not so in Scala. Scala makes the nested class of two same class instances different, much like you would expect two members of two different instances of a single class. Confused? here is some code to explain this better

-

Java:

-
1
2
3
4
5
6
7
8
9
10
11
12
public class Outer {
public class Inner {}
public void useInner(Outer.Inner inner){...}
}
//.. somewhere in the code
o1 = new Outer()
o2 = new Outer()
i1 = new o1.inner()
i2 = new o2.inner()
// this would work fine
o1.useInner(i2) // works
o2.useInner(i1) // works
-

Scala:

1
2
3
4
5
6
7
8
9
10
11
12
class Outer {
class Inner {}
def useInner(inner : Inner){...}
}
//.. somewhere in the code..
val o1 = new Outer
val o2 = new Outer
val i1 = new o1.Inner
val i2 = new o2.Inner
o1.useInner(i1) // works fine
o2.useInner(i2) // works fine
o1.useInner(i2) // THROWS type mismatch exception!

-

Thoughts - It makes some sense that the path to a type that can only exist in an instance is enforced as such.. It would be interesting to know why Kotlin decided to implement this feature in a similar way to Java (conservative?)

-

OMITTED - Existential types

What is it? This is yet another trade-off for Scala to be able to inter-op with Java
Let’s see an example:

-
1
2
3
4
5
// Create a case class with generic type
case class Z[T]( blah:T )
// create an instance and cast it to a type created with existential type annotation
Seq( Z[Int]( 5 ), Z[String]("blah") ) : Seq[Z[X] forSome { type X >: String with Int }]
res1 = Seq[Z[_ >: String with Int]] = List(Z(5), Z(blah))
-

Thoughts - Scala supports type variance to describe generic type bounds, Scala designer (Odersky) chose to add existential types support due to three main reasons link: erasure, raw types (lack of generics) and Java’s “wildcard” types.

-

Kotlin took the more “purist” approach (also known as reified types) of dropping support for erasure, raw types, wildcard types and finally existential types.

-

OMITTED - Complicated logic for initialization of traits

What is it? Traits are similar to abstract classes, except that you may use several traits on the same class at the same time (like interfaces).

-

In Scala, traits are constructed before the extended classes

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
trait Averager {
val first : Int
val second : Int
val avg : Int = first / second
}
// if we were to naively construct a class like so:
class Naive extends Averager {
val first = 20
val second = 10
}
// create instance
> new Naive()
java.lang.ArithmeticException: / by zero
// what happened was that our val "avg" was computed at construction time before seeing the values we put in Naive
-

Alternatively the trait can be used with a pre-initialized anonymous class val res = new { val first = 20 ; val second = 10 } with Averager, also the val which depends on other vals (avg in our case) can be made “lazy” with the (obvious) lazy keyword lazy val avg : Int = first / second.

-

As a side note, using def for “avg” could work, but it would mean evaluating whatever is defined under “avg” every-time this method is called.

-

Thoughts - Traits in Scala can have state which could be made tricky. If you read this blog post link you will see Kotlin developers thoughts and a nice insight into what went into designing the inheritance in Kotlin. (don’t forget to read the comments!)

-

OMITTED - Custom symbolic operations

What is it? Scala does not have a built in handling of symbols (like =,+,/ etc), they are actually method calls, as you can have symbols used for method names.

-

Thoughts - This may sound useful at first, but can and (arguably) will create very confusing looking code, like we saw before in the Lift and SBT examples.

-

OMITTED - Built-in XML

What is it? Exactly as it sounds, Scala built in XML native support, so you can do stuff like this

-
1
val holdsXml = <outer><inner>some text</inner></outer>
-

Thoughts - Having a built in support for XML as a language feature is interesting, and sometimes useful. An interview link with Odersky reveals that he would consider removing the native XML support in Scala if had written Scala now.

-

Bottom Line

Kotlin is an idiomatic language that draws from the many good features in todays leading languages, specifically from Scala which is a leading language on the JVM platform today. We’ve looked into omissions on Kotlin’s part versus Scala

-

I’ve very much enjoyed writing this blog post and learning about the evolution that the design of Java/Scala/Kotlin languages, and the choices the respective authors made along the way.

-

To get a better feeling of Kotlin I might pick it up for a small project, at which point I will surely blog about it some more..

- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/tags/nodejs/index.html b/tags/nodejs/index.html deleted file mode 100644 index 1b277c6..0000000 --- a/tags/nodejs/index.html +++ /dev/null @@ -1,325 +0,0 @@ - - - - - - Tag: nodejs | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- Using Contractor With Child Processes -

- - -
- -
- -

Contractor is a factory of contracts, that helps with documenting our APIs.
The child_process facility built into node is using EventEmitter (v1), and does not support callbacks, so it is a great fit for Contractor!
Since I needed it for a distributed worker framework I am working on, I added a helper facility called “Lawyer”, it basically reads the contracts and routes them to the provided object.

-

Let us take the parent and child example code from Node’s official documentation (http://nodejs.org/api/child_process.html) and see how simple it would be to add some documentation.

-

Parent

-
1
2
3
4
5
6
7
8
9
var cp = require('child_process');

var n = cp.fork(__dirname + '/sub.js');

n.on('message', function(m) {
console.log('PARENT got message:', m);
});

n.send({ hello: 'world' });
-

Child

-
1
2
3
4
5
process.on('message', function(m) {
console.log('CHILD got message:', m);
});

process.send({ foo: 'bar' });
-

Node’s child_process facility provied us with a very basic means of communication, we simply call n.send({any: "thing"}) and it automagically recieved by the forked child. Let’s spice it up with a more RPC message passing style, with contracts and lawyers

-

contracts.js

-
1
2
3
4
5
6
7
8
9
10
var Contractor = require('contractor').Contractor;
exports.ChildPublish = {
"GreetingResponse" : Contractor.Create("GreetingResponse", Contractor.Required("childs greeting response")).
"StatusResponse" : Contractor.Create("StatusResponse", Contractor.Required("errors count"), Contractor.Required("number of completed jobs"))
}

exports.ChildSubscribe = {
"Greeting": Contractor.Create("Greeting", Contractor.Required("greeting word")),
"StatusQuery" : Contractor.Create("StatusQuery")
}
-

parent.js

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var contracts = require('./contracts');
var Contractor = require('contractor').Contractor;
var Lawyer = require('contractor').Lawyer;
var cp = require('child_process');

var n = cp.fork(__dirname + '/sub.js');

n.on('message', function(m) {
Lawyer.Read(m, {
"GreetingResponse" : function(childResponse){ console.log("child responded:" + childResponse) },
"StatusResponse" : function(errorCount, numCompletedJobs){ console.log("childs status, errors:"+errorCount+" jobs done:" + numCompletedJobs) }
});
});
n.send(contracts.ChildSubscribe.Greeting("this is your father!"));
n.send(contracts.ChildSubscribe.StatusQuery());
-

sub.js

-
1
2
3
4
5
6
7
8
9
var contracts = require('./contracts');
var Contractor = require('contractor').Contractor;
var Lawyer = require('contractor').Lawyer;
process.on('message', function(m) {
Lawyer.Read(m, {
"Greeting" : function(parentGreeting){ process.send( contracts.ChildPublish.GreetingResponse("Hi "+parentGreeting+" can I have 10$?")) },
"StatusQuery" : function(){ process.send( contracts.ChildPublish.StatusResponse(0, 42) ) }
});
});
-

If we were to run these

-
1
2
3
#> node parent.js
child responded:Hi this is your father! can I have 10$?
childs status, errors:0 jobs done:42
-

If you found Contractor / Lawyer interesting and/or useful, I would love to hear about it!

- - -
- -
- -
- - - -
- -
- - -
- - -

- Typing in JavaScript? -

- - -
- -
- -

“Strong typing is for people with weak memories” - not sure who originally said that, but you have got to love the enthusiasm people have for dynamic languges. What is there not to love? powerful basic language constructs such as closure, prototypal inheritance, objects as first class citizens and anonymous functions, mainly, with which you can simulate almost anything static languages have to offer!

-

Proof you say? no problem! here are few implementations that are worthy of noting:

-

Compile time type checking https://developers.google.com/closure/compiler/docs/js-for-compiler#types

-

Classic OO inheritance http://mootools.net/docs/core/Class/Class & http://coffeescript.org/#classes

-

(if you have other interesting examples let me know!)

-

Convention over configuration

The combination of CoffeeScript’s “classical” Class like system and Backbone.JS’s MVC style structure, give (IMHO) the best value in terms of complexity and pure fun writing apps. The challenges I come across while building large complex applications is the inherited nature of a true dynamic language “typelessness“.

-

The ugly monster, that is -a large application written in JavaScript, pushed me into struggling with these two main challenges:

-
    -
  • Many-to-many PubSub communication
  • -
  • 3rd party API’s
  • -
-

Many-to-many PubSub communication

While the PubSub (also known as observer pattern) in JavaScript is a powerful tool to decouple your code, having multiple callers and listeners on the same event makes it really difficult to keep track of the actual common API for these events. Lats try to reflect on this while looking at an actual implementation.

-

(please excuse my CoffeeScript)

-

Pub/Sub implementation in Backbone/Underscore.JS

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
pubsub = _.extend {}, Backbone.Events
pubsub.on 'myEvent', (param1, param2)->
x1 = param1 + param2
# do something with x1

# in some other obscure location in the code
pubsub.on 'myEvent', (param1)->
x2 = param1
# do something with x2

# ...
pubsub.trigger 'myEvent', 5
# ...
pubsub.trigger 'myEvent', 5,10
-

The “pubsub.on(event name, callback)” API is very common amongst different JS pub/sub frameworks. here’s an example of how a generic implementation might look like:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pubsub = {
_listeners : {}
on : (channel, callback)->
@_listeners[ channel ] ?= []
@_listeners[ channel ].push callback

trigger : (channel, params...)->
@_listeners[ channel ]?.forEach (cb)-> cb.apply(null, params)
}

pubsub.on 'myEvent', (param1, param2)->
x1 = param1 + param2
# ... do something with x1..

# in some other obscure location in the code
pubsub.on 'myEvent', (param1)->
x2 = param1
# .... do something with x2

# ...
pubsub.trigger 'myEvent', 5
# ...
pubsub.trigger 'myEvent', 5,10
-

If your using the very popular Socket.IO library, for realtime client/server communication, you are using the exact same API, in which case it makes it even more complicated to keep track of, since the ambiguity is manifested on both the client and the server.

-

3rd party API’s

This is how a type related bug might look like in a dynamic language

-
1
2
3
a = "42" # returned from some API
a == 42
=> false
-

Enter “Contractor” & “Backbone-typed”

Contractor
https://github.com/romansky/Contractor

-

Backbone-typed
https://github.com/romansky/backbone-typed

-

I’m introducing a convention here, that helps keep the API consistent across the application, the approach here is to create wrapper functions, that are used as both the API documentation and run-time validation facility for required and optional arguments.

-

Example:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# declare the events and their APIs
Messages = {
LoginEvent : Contractor.Create( "LoginEvent", Contractor.Required("user ID"), Contractor.Optional("additional info") )
AppErrorEvent : Contractor.Create( "AppErrorEvent", Contractor.Required("user ID"), Contractor.Required("error description"), Contractor.Optional("exception") )
}

# register on event API
pubsub.on Messages.LoginEvent, (userID, info)-> #... do stuff here
pubsub.on Messages.AppErrorEvent, (userID, description, exception)-> #... do stuff here

# trigger the event
pubsub.apply(pubsub, Messages.LoginEvent(112233, "mobile"))
pubsub.apply(pubsub, Messages.LoginEvent(112233))

pubsub.apply(pubsub, Messages.AppErrorEvent(112233, "user did not have access to resource", e))
pubsub.apply(pubsub, Messages.AppErrorEvent(112233)) # since we did not provide the second required argument, this will log an error and return null
-

With NodeJS, I am using the same code on the front and back end, which makes it really easy to manage my Socket.IO communication.

-

If you have been paying attention, there’s still the type issue. Since I work allot with Backbone, it made a lot of sense to write “backbone-typed” which adds optional typing to Backbone-Models.
Example:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Wearing = {
"bracelet" : "bracelet"
"watch" : "watch"
}

class User extends TypedModel
defaults: {
name: null
email: null
lotteryNumber: null
isAwesome: null
}

types: {
name: Types.String
email: Types.String
lotteryNumber: Types.Integer
isAwesome: Types.Boolean
wearing : Types.Enum(Wearing)
}

user1 = new User({name: "foo", email: "foo@bar.com", lotteryNumber: 12345, isAwesome: true, wearing: Wearing.watch})
user1.toJSON() #=> {name: "foo", email: "foo@bar.com", lotteryNumber: 12345, isAwesome: true, wearing: "watch"} - nothing special going on here..

user2 = new User({name: "foo", email: "foo@bar.com", lotteryNumber: "54321", isAwesome: "false", wearing: "thong"})
user2.toJSON() #=> {name: "bar", email: "bar@foo.com", lotteryNumber: 54321, isAwesome: false, wearing: null} - shit happens!

user2.set({wearing: Wearing.bracelet})

if user2.get("lotteryNumber") == 54321 and user2.get("wearing") == Wearing.bracelet then user2.set({isAwesome: "true"})
user2.toJSON() #=> {name: "bar", email: "bar@foo.com", lotteryNumber: 54321, isAwesome: true, wearing: "bracelet"} - awesome for sure..
-

If you want to know a little more about how these work, please visit the respective Git repositories, and let me know what you think!

- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/tags/queue/index.html b/tags/queue/index.html deleted file mode 100644 index 6c261b6..0000000 --- a/tags/queue/index.html +++ /dev/null @@ -1,259 +0,0 @@ - - - - - - Tag: queue | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- Distributed Scheduled Queue With Redis -

- - -
- -
- -

Unlike the classic FIFO queue, jobs are scheduled for execution in the future, possibly on a specific day, week, month year.. similar to “cron” in a Unix OS. Unlike the normal Unix “cron” implementation, we want it to be distributed, so that several machines(workers) can query and then execute these jobs.

-

Implementing

    -
  • Planning - The standart Unix cron format is very flexible and has been time tested for describing schedules
  • -
  • Managing/Coordinating - The queues are stored in Redis Sorted Sets, Lua will be used for atomic execution
  • -
  • Execution - Getting the pieces together
  • -
-

Planning

“Unix Time” format, is the number of milliseconds since (UTC) 1 January 1970
Example (* the actual unix time number is in UTC, while the formatted date is localized)

-
1350745504313 == Sat Oct 20 2012 17:05:04 GMT+0200 (IST)
-

Using this format will make it possible to slice the queue with specific point in time in mind.

-

Managing/Coordinating

Redis’st will be holding the job IDs as members scored by their corresponding execution times(in unix time format).

-

-

Redis commands that will come in handy

-
    -
  • Scheduling job run: ZADD cron:queue 1350745504313 1000
  • -
  • Finding a job to run: ZRANGEBYSCORE cron:queue 1350745504314 LIMIT 0 1
  • -
-

Execution

Without going into any platform specific implementation details, this is what the process looks like

-
    -
  1. When a new schedule is added, jobs are created for upcoming execution times, which are calculated for the next two hours and added to our cron queue
  2. -
  3. A special job is added to be run every hour, it will run over all the schedules, calculate and add their execution time to the scheduled queue, two hours into the future
  4. -
  5. Workers will be polling the database every second to find jobs
  6. -
  7. Once an entry comes up, it is removed from the queue and added to another Sorted Set queue, so we dont “forget” any jobs..
  8. -
-

As for items 3 and 4 above, it is not possible to implement this logic in the client, because for example when we find a job that needs to be executed, another worker might grab it, or worse they both “grabbed” it and are now working on the same job. To work around this, we need to write a little Lua script, since Lua scripts are executed serially, just like any other Redis command

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
local currentTimeMS = KEYS[1]
local workingQueue = "scheduler:working"
local scheduleQueue = "schedule"
-- for easier reading, epoch time is in milliseconds
local second = 1000

local function foundJob(jobID)
-- implement removing the job from all other queues
-- implement updating job status where the actual job object is stored
end

local function findNextJob()
local result
-- check if there are any "stale" jobs
result = redis.call( 'ZRANGEBYSCORE', workingQueue, 0, currentTimeMS-60*second, 'LIMIT',0,1 )
if result[1] ~= nil then return foundJob(result[1]) end
-- check the normal queue
result = redis.call( 'ZRANGEBYSCORE', scheduleQueue, 0, currentTimeMS, 'LIMIT',0,1 )
if result[1] ~= nil then return foundJob(result[1]) end
-- if we didnt find anything return nil
return nil
end

return findNextJob()
-

And there you have it! a “Distributed scheduled queue” implemented on top of Redis.

-

If you are going to implement this with node, I created a little cron format parser you can find here:
JSCron

- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/tags/redis/index.html b/tags/redis/index.html deleted file mode 100644 index dca4f39..0000000 --- a/tags/redis/index.html +++ /dev/null @@ -1,259 +0,0 @@ - - - - - - Tag: redis | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- Distributed Scheduled Queue With Redis -

- - -
- -
- -

Unlike the classic FIFO queue, jobs are scheduled for execution in the future, possibly on a specific day, week, month year.. similar to “cron” in a Unix OS. Unlike the normal Unix “cron” implementation, we want it to be distributed, so that several machines(workers) can query and then execute these jobs.

-

Implementing

    -
  • Planning - The standart Unix cron format is very flexible and has been time tested for describing schedules
  • -
  • Managing/Coordinating - The queues are stored in Redis Sorted Sets, Lua will be used for atomic execution
  • -
  • Execution - Getting the pieces together
  • -
-

Planning

“Unix Time” format, is the number of milliseconds since (UTC) 1 January 1970
Example (* the actual unix time number is in UTC, while the formatted date is localized)

-
1350745504313 == Sat Oct 20 2012 17:05:04 GMT+0200 (IST)
-

Using this format will make it possible to slice the queue with specific point in time in mind.

-

Managing/Coordinating

Redis’st will be holding the job IDs as members scored by their corresponding execution times(in unix time format).

-

-

Redis commands that will come in handy

-
    -
  • Scheduling job run: ZADD cron:queue 1350745504313 1000
  • -
  • Finding a job to run: ZRANGEBYSCORE cron:queue 1350745504314 LIMIT 0 1
  • -
-

Execution

Without going into any platform specific implementation details, this is what the process looks like

-
    -
  1. When a new schedule is added, jobs are created for upcoming execution times, which are calculated for the next two hours and added to our cron queue
  2. -
  3. A special job is added to be run every hour, it will run over all the schedules, calculate and add their execution time to the scheduled queue, two hours into the future
  4. -
  5. Workers will be polling the database every second to find jobs
  6. -
  7. Once an entry comes up, it is removed from the queue and added to another Sorted Set queue, so we dont “forget” any jobs..
  8. -
-

As for items 3 and 4 above, it is not possible to implement this logic in the client, because for example when we find a job that needs to be executed, another worker might grab it, or worse they both “grabbed” it and are now working on the same job. To work around this, we need to write a little Lua script, since Lua scripts are executed serially, just like any other Redis command

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
local currentTimeMS = KEYS[1]
local workingQueue = "scheduler:working"
local scheduleQueue = "schedule"
-- for easier reading, epoch time is in milliseconds
local second = 1000

local function foundJob(jobID)
-- implement removing the job from all other queues
-- implement updating job status where the actual job object is stored
end

local function findNextJob()
local result
-- check if there are any "stale" jobs
result = redis.call( 'ZRANGEBYSCORE', workingQueue, 0, currentTimeMS-60*second, 'LIMIT',0,1 )
if result[1] ~= nil then return foundJob(result[1]) end
-- check the normal queue
result = redis.call( 'ZRANGEBYSCORE', scheduleQueue, 0, currentTimeMS, 'LIMIT',0,1 )
if result[1] ~= nil then return foundJob(result[1]) end
-- if we didnt find anything return nil
return nil
end

return findNextJob()
-

And there you have it! a “Distributed scheduled queue” implemented on top of Redis.

-

If you are going to implement this with node, I created a little cron format parser you can find here:
JSCron

- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/tags/scala/index.html b/tags/scala/index.html deleted file mode 100644 index c0bf97f..0000000 --- a/tags/scala/index.html +++ /dev/null @@ -1,433 +0,0 @@ - - - - - - Tag: scala | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- Using Spray to mock 3rd party APIs in your tests -

- - -
- -
- -

If you need to test/mock 3rd party API’s / a crawler / external web service, and you dont want to use some fancy DI framework (I hate the magic in DI frameworks, I use constructor DI instead..), anyway, here’s a helper class I put togather to that end

-

If you haven’t got a chance to try out Spray, you totally should, its a high performance web server built on top of Netty and Akka

-

Spray has a very nice HTTP API. Few things I liked:

-
    -
  • HTTP method mapping spray.http.HttpMethods - GET, POST etc..
  • -
  • HttpRequest and HttpResopnse are basically wrappers around data you would expect to have in a request and response data
  • -
  • spray.http.Uri wrapper class around what you would expect to find in the URI
  • -
  • it really is just an Akka Actor, with just the right amount of control exposed and the rest well hidden behind its nice APIs
  • -
-

So, for testing/mocking all I really need is:

-
    -
  • HTTP method
  • -
  • URI
  • -
  • body(content)
  • -
-

Here’s an implementation of the helper class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import akka.actor._
import akka.io.IO
import java.util.concurrent.atomic.AtomicInteger
import spray.can.Http
import spray.http.{Uri, HttpMethod, HttpResponse, HttpRequest}

object Helpers {

lazy implicit val testSystem = ActorSystem("helpers-test-system")
// used to track the last assigned port
val lastPort = new AtomicInteger(20000)
// a single expected triplet of method+uri+body
type Replay = (HttpMethod,Uri.Path,String)

// factory method to get a replater to play around with
def webReplayer(replay : List[Replay]) : (ActorRef,Int) = {
// everytime the factory is used a new port is assigned,
// so many instances can co-exist and tests can run in parallel without getting
// on eachother's toes
val newPort = lastPort.addAndGet(1)
// instantiate a new Spray server. Yes, its that simple!
val newServer = testSystem.actorOf(Props(new Helpers.ReplayerActor(replay)))
IO(Http) ! Http.Bind(newServer, interface = "127.0.0.1", port = newPort)
// return a pair of the server instance and the assigned port
// the newPort should be used by client code to construct the remote URI with
(newServer,newPort)
}

// object to use to tell the server to shutdown..
object ShutTestServer

// a concrete implementation for a Spray Actor to listen to incoming HttpRequest(..) messages
// provided a list of messages to be matched against arrived and content to be sent back
class ReplayerActor(var replay :List[Replay]) extends Actor {
var ioServer : ActorRef = null
override def receive: Receive = {
case _ : Http.Connected =>
sender ! Http.Register(self)
ioServer = sender()
// this is basically a "catch all" HttpRequest pattern
case HttpRequest(method, uri,_,_,_) =>
// a test to see that this is indeed expected to happen at this point
if (!replay.isEmpty && replay.head._1 == method && uri.path == replay.head._2){
sender ! HttpResponse(entity = replay.head._3)
replay = replay.tail
} else {
sender ! HttpResponse(status = 400, entity = "was expecting a different request at this point")
}
case ShutTestServer => ioServer ! PoisonPill
case _ => //ignore
}
}
}
-

And now a cocrete client code example to use this tool

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// some random content I would like to receive in this scenario (or test..)
val content =
s"""
|<html>
|<head>
|<title>sometitle</title>
|</head>
|<body>some body</body>
|</html>
""".stripMargin

// we use the factory method to get an instance of "replayer" to play around with
val (_testWebServer,port) = Helpers.webReplayer(
List(
(GET,Uri.Path("/somesite"),content)
)
)

// and where ever in your actual code, use the port we got back above and have it "hit" the server
val whatEver = YourHTTPGetter.fetch(s"http://127.0.0.1:$port/somesite")
assert(whatEver == content)
-

Profit!

- - -
- -
- -
- - - -
- -
- - -
- - -

- Making The Case For Kotlin -

- - -
- -
- -

The Kotlin project page has this to say about Kotlin: “is a statically typed programming language that compiles to JVM byte codes and JavaScript.” project page

-

Kotlin is a very interesting project, I wanted to check it out, so I decided to write this blog post exploring some design aspects behind Kotlin, and specifically how it compares to the leading JVM “hacker” language - Scala.

-

Who Is Behind Kotlin?

Even though there are several decent free IDE’s available today (Eclipse, NetBeans, Visual Studio Express..), it (could be) surprising that JetBrains is able to rack in between 199$ for a personal license and up to 699$ for a commercial license (for companies) including 1 year update subscription (I have indeed bought one personal license for my development needs). If you are developing on top of VisualStudio you are (most) probably familiar with the 250$+ plugin ReSharper.

-

I would argue that this makes them a good candidate, with insight into good and bad language features, but lets not get ahead of ourselves and set the expectations too high.

-

Why Another JVM Language?

JVM is a well known platform, its weaknesses and scalability strategies are available and documented well on the web, its being used as classic Java run-time VM and for some other popular languages like Scala, Groovy, Clojure and others. Many modules have been already written in Java, normally JVM run languages are compatible with the existing Java ecosystem.

-

Why Static Typing?

In the old (and still ongoing) battle of dynamic and static languages, the consensus is that dynamic languages are faster and easier way to get things done, on the static languages side they allow programmers harness the power of intelligent IDEs to scale up and maintain their code. Type-inferred languages try to find a middle ground approach so that you don’t need to write so much boilerplate code (cue JAVA) to support the static typing, while having the benefits of a full compile time type enforcement.

-

Finally, Kotlin Vs. Scala - Fight!

Scala today is the fastest growing JVM language and is probably competing for the same crowd as Kotlin, Kotlin put up a comparison page link so we will simply go over the omissions part and try to make sense of it.

-

OMITTED - Implicit conversions, parameters

What is it? Scala implicits is (IMHO) a single biggest weakness in the language, once they are used, along with the ability to boast symbols for syntax, it quickly stops being human readable and more like a DSL. Which requires many trips to the docs or even worse - the source code.

-

SBT (Scala Build Tool) is the Maven/Ant of the Scala world link, its very difficult to get things done with it without many trips to google/documentation

-
1
2
3
4
5
lazy val pricing = Project (
"pricing",
file ("cdap2-pricing"),
settings = buildSettings ++ Seq (libraryDependencies ++= pricingDeps)
) dependsOn (common, compact, server)
-

Lift Is a popular web framework, here’s a snippet I took from one of its documentation pages link (see the "#result *" #> x part)

-
1
2
3
4
5
6
7
8
9
def render = inputParam match {
case Full(x) =>
println("Input is: "+x)
"#result *" #> x

case _ =>
println("No input present! Rendering input form HTML")
PassThru
}
-

Thoughts - Indeed for many people this is the part that makes Kotlin a better “scalable” language, by not allowing programmers and libraries hinder the code unreadable without an IDE

-

OMITTED - Overridable Type Members

What is it? Type members in Scala allow you to manage local generic types by assign a type to a class/abstract class/trait member

-
1
2
3
abstract class HasTypedMember {
type typeMe
}
-

An extending class will provide a specific type for this type type member

-
1
2
3
class ExtendingClass extends HasTypedMember {
type typeMe = Int
}
-

Thoughts - Frankly, its not really clear why Kotlin decided to omit having typed members, I could be missing something.. Never the less Kotlin usually takes the minimalistic and simplistic route, my guess is that if this is indeed useful, the door is open to add it later..

-

OMITTED - Path-dependent types

What is it? Pointing to a class under another class in Java would mean a specific type (Parent.Child), well not so in Scala. Scala makes the nested class of two same class instances different, much like you would expect two members of two different instances of a single class. Confused? here is some code to explain this better

-

Java:

-
1
2
3
4
5
6
7
8
9
10
11
12
public class Outer {
public class Inner {}
public void useInner(Outer.Inner inner){...}
}
//.. somewhere in the code
o1 = new Outer()
o2 = new Outer()
i1 = new o1.inner()
i2 = new o2.inner()
// this would work fine
o1.useInner(i2) // works
o2.useInner(i1) // works
-

Scala:

1
2
3
4
5
6
7
8
9
10
11
12
class Outer {
class Inner {}
def useInner(inner : Inner){...}
}
//.. somewhere in the code..
val o1 = new Outer
val o2 = new Outer
val i1 = new o1.Inner
val i2 = new o2.Inner
o1.useInner(i1) // works fine
o2.useInner(i2) // works fine
o1.useInner(i2) // THROWS type mismatch exception!

-

Thoughts - It makes some sense that the path to a type that can only exist in an instance is enforced as such.. It would be interesting to know why Kotlin decided to implement this feature in a similar way to Java (conservative?)

-

OMITTED - Existential types

What is it? This is yet another trade-off for Scala to be able to inter-op with Java
Let’s see an example:

-
1
2
3
4
5
// Create a case class with generic type
case class Z[T]( blah:T )
// create an instance and cast it to a type created with existential type annotation
Seq( Z[Int]( 5 ), Z[String]("blah") ) : Seq[Z[X] forSome { type X >: String with Int }]
res1 = Seq[Z[_ >: String with Int]] = List(Z(5), Z(blah))
-

Thoughts - Scala supports type variance to describe generic type bounds, Scala designer (Odersky) chose to add existential types support due to three main reasons link: erasure, raw types (lack of generics) and Java’s “wildcard” types.

-

Kotlin took the more “purist” approach (also known as reified types) of dropping support for erasure, raw types, wildcard types and finally existential types.

-

OMITTED - Complicated logic for initialization of traits

What is it? Traits are similar to abstract classes, except that you may use several traits on the same class at the same time (like interfaces).

-

In Scala, traits are constructed before the extended classes

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
trait Averager {
val first : Int
val second : Int
val avg : Int = first / second
}
// if we were to naively construct a class like so:
class Naive extends Averager {
val first = 20
val second = 10
}
// create instance
> new Naive()
java.lang.ArithmeticException: / by zero
// what happened was that our val "avg" was computed at construction time before seeing the values we put in Naive
-

Alternatively the trait can be used with a pre-initialized anonymous class val res = new { val first = 20 ; val second = 10 } with Averager, also the val which depends on other vals (avg in our case) can be made “lazy” with the (obvious) lazy keyword lazy val avg : Int = first / second.

-

As a side note, using def for “avg” could work, but it would mean evaluating whatever is defined under “avg” every-time this method is called.

-

Thoughts - Traits in Scala can have state which could be made tricky. If you read this blog post link you will see Kotlin developers thoughts and a nice insight into what went into designing the inheritance in Kotlin. (don’t forget to read the comments!)

-

OMITTED - Custom symbolic operations

What is it? Scala does not have a built in handling of symbols (like =,+,/ etc), they are actually method calls, as you can have symbols used for method names.

-

Thoughts - This may sound useful at first, but can and (arguably) will create very confusing looking code, like we saw before in the Lift and SBT examples.

-

OMITTED - Built-in XML

What is it? Exactly as it sounds, Scala built in XML native support, so you can do stuff like this

-
1
val holdsXml = <outer><inner>some text</inner></outer>
-

Thoughts - Having a built in support for XML as a language feature is interesting, and sometimes useful. An interview link with Odersky reveals that he would consider removing the native XML support in Scala if had written Scala now.

-

Bottom Line

Kotlin is an idiomatic language that draws from the many good features in todays leading languages, specifically from Scala which is a leading language on the JVM platform today. We’ve looked into omissions on Kotlin’s part versus Scala

-

I’ve very much enjoyed writing this blog post and learning about the evolution that the design of Java/Scala/Kotlin languages, and the choices the respective authors made along the way.

-

To get a better feeling of Kotlin I might pick it up for a small project, at which point I will surely blog about it some more..

- - -
- -
- -
- - - -
- -
- - -
- - -

- Setting Up IntelliJ For Android And Scala On Ubuntu -

- - -
- -
- -

Prerequisites

Android SDK

    -
  1. get the latest SDK

    -
  2. -
  3. after extracting, run ./tools/android

    -
  4. -
  5. from the list select the Android version your afer, right now its Android 4.0.3 (API 15) (I also installed Android 2.3.3), click on Install packages to get the installation started

    -
  6. -
  7. setup your env echo "export ANDROID_HOME=~/Applications/android-sdk-linux/" >> ~/.bashrc

    -
  8. -
  9. refresh your currect session source ~/.bashrc

    -
  10. -
-

Adding SBT support to IntelliJ

    -
  1. open File > Settings > Plugin

    -
  2. -
  3. click on Browser repositories

    -
  4. -
  5. find SBT, right click and select Download and install

    -
  6. -
-

Setup SBT

    -
  1. create folder ~/bin/ (if you dont have ~/bin in your PATH, then you can do this: echo "PATH=$PATH:~/bin" >> ~/.bashrc (refresh your currect session source ~/.bashrc)

    -
  2. -
  3. download sbt-launcher.jar and place it under ~/bin

    -
  4. -
  5. create launcher: touch ~/bin/sbt and then echo 'java -Xmx512M -jar `dirname $0`/sbt-launch.jar "$@"' >> ~/bin/sbt

    -
  6. -
  7. make it executable: chmod u+x ~/bin/sbt

    -
  8. -
-

Setup android-plugin for Scala support

    -
  1. install giter8 curl https://raw.github.com/n8han/conscript/master/setup.sh | sh

    -
  2. -
  3. run it ~/bin/cs n8han/giter8

    -
  4. -
-

Setup sbt-idea

    -
  1. create folder (if it doesnt exist) mkdir -p ~/.sbt/plugins/

    -
  2. -
  3. create the file ~/.sbt/plugins/build.sbt and add the following lines to it:

    -
    resolvers += "sbt-idea-repo" at "http://mpeltonen.github.com/maven/"
    -addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.0.0")
    -
  4. -
-

Finally, now that you have all the tools you need, proceed to creating a new project

    -
  1. run the android plugin project setup tool: ~/bin/g8 jberkel/android-app, follow the onscreen questions (or press return for defaults)
    this will create the project folder and create the files needed to build an android project

    -
  2. -
  3. open the new folder cd <project name>

    -
  4. -
  5. setup IntelliJ project support sbt gen-idea

    -
  6. -
  7. open the project in IntelliJ

    -
  8. -
- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/tags/spray/index.html b/tags/spray/index.html deleted file mode 100644 index cbd705a..0000000 --- a/tags/spray/index.html +++ /dev/null @@ -1,250 +0,0 @@ - - - - - - Tag: spray | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- Using Spray to mock 3rd party APIs in your tests -

- - -
- -
- -

If you need to test/mock 3rd party API’s / a crawler / external web service, and you dont want to use some fancy DI framework (I hate the magic in DI frameworks, I use constructor DI instead..), anyway, here’s a helper class I put togather to that end

-

If you haven’t got a chance to try out Spray, you totally should, its a high performance web server built on top of Netty and Akka

-

Spray has a very nice HTTP API. Few things I liked:

-
    -
  • HTTP method mapping spray.http.HttpMethods - GET, POST etc..
  • -
  • HttpRequest and HttpResopnse are basically wrappers around data you would expect to have in a request and response data
  • -
  • spray.http.Uri wrapper class around what you would expect to find in the URI
  • -
  • it really is just an Akka Actor, with just the right amount of control exposed and the rest well hidden behind its nice APIs
  • -
-

So, for testing/mocking all I really need is:

-
    -
  • HTTP method
  • -
  • URI
  • -
  • body(content)
  • -
-

Here’s an implementation of the helper class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import akka.actor._
import akka.io.IO
import java.util.concurrent.atomic.AtomicInteger
import spray.can.Http
import spray.http.{Uri, HttpMethod, HttpResponse, HttpRequest}

object Helpers {

lazy implicit val testSystem = ActorSystem("helpers-test-system")
// used to track the last assigned port
val lastPort = new AtomicInteger(20000)
// a single expected triplet of method+uri+body
type Replay = (HttpMethod,Uri.Path,String)

// factory method to get a replater to play around with
def webReplayer(replay : List[Replay]) : (ActorRef,Int) = {
// everytime the factory is used a new port is assigned,
// so many instances can co-exist and tests can run in parallel without getting
// on eachother's toes
val newPort = lastPort.addAndGet(1)
// instantiate a new Spray server. Yes, its that simple!
val newServer = testSystem.actorOf(Props(new Helpers.ReplayerActor(replay)))
IO(Http) ! Http.Bind(newServer, interface = "127.0.0.1", port = newPort)
// return a pair of the server instance and the assigned port
// the newPort should be used by client code to construct the remote URI with
(newServer,newPort)
}

// object to use to tell the server to shutdown..
object ShutTestServer

// a concrete implementation for a Spray Actor to listen to incoming HttpRequest(..) messages
// provided a list of messages to be matched against arrived and content to be sent back
class ReplayerActor(var replay :List[Replay]) extends Actor {
var ioServer : ActorRef = null
override def receive: Receive = {
case _ : Http.Connected =>
sender ! Http.Register(self)
ioServer = sender()
// this is basically a "catch all" HttpRequest pattern
case HttpRequest(method, uri,_,_,_) =>
// a test to see that this is indeed expected to happen at this point
if (!replay.isEmpty && replay.head._1 == method && uri.path == replay.head._2){
sender ! HttpResponse(entity = replay.head._3)
replay = replay.tail
} else {
sender ! HttpResponse(status = 400, entity = "was expecting a different request at this point")
}
case ShutTestServer => ioServer ! PoisonPill
case _ => //ignore
}
}
}
-

And now a cocrete client code example to use this tool

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// some random content I would like to receive in this scenario (or test..)
val content =
s"""
|<html>
|<head>
|<title>sometitle</title>
|</head>
|<body>some body</body>
|</html>
""".stripMargin

// we use the factory method to get an instance of "replayer" to play around with
val (_testWebServer,port) = Helpers.webReplayer(
List(
(GET,Uri.Path("/somesite"),content)
)
)

// and where ever in your actual code, use the port we got back above and have it "hit" the server
val whatEver = YourHTTPGetter.fetch(s"http://127.0.0.1:$port/somesite")
assert(whatEver == content)
-

Profit!

- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/tags/testing/index.html b/tags/testing/index.html deleted file mode 100644 index 856eab0..0000000 --- a/tags/testing/index.html +++ /dev/null @@ -1,250 +0,0 @@ - - - - - - Tag: testing | Uniformlyrandom - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- -
- -
- - -
- - -

- Using Spray to mock 3rd party APIs in your tests -

- - -
- -
- -

If you need to test/mock 3rd party API’s / a crawler / external web service, and you dont want to use some fancy DI framework (I hate the magic in DI frameworks, I use constructor DI instead..), anyway, here’s a helper class I put togather to that end

-

If you haven’t got a chance to try out Spray, you totally should, its a high performance web server built on top of Netty and Akka

-

Spray has a very nice HTTP API. Few things I liked:

-
    -
  • HTTP method mapping spray.http.HttpMethods - GET, POST etc..
  • -
  • HttpRequest and HttpResopnse are basically wrappers around data you would expect to have in a request and response data
  • -
  • spray.http.Uri wrapper class around what you would expect to find in the URI
  • -
  • it really is just an Akka Actor, with just the right amount of control exposed and the rest well hidden behind its nice APIs
  • -
-

So, for testing/mocking all I really need is:

-
    -
  • HTTP method
  • -
  • URI
  • -
  • body(content)
  • -
-

Here’s an implementation of the helper class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import akka.actor._
import akka.io.IO
import java.util.concurrent.atomic.AtomicInteger
import spray.can.Http
import spray.http.{Uri, HttpMethod, HttpResponse, HttpRequest}

object Helpers {

lazy implicit val testSystem = ActorSystem("helpers-test-system")
// used to track the last assigned port
val lastPort = new AtomicInteger(20000)
// a single expected triplet of method+uri+body
type Replay = (HttpMethod,Uri.Path,String)

// factory method to get a replater to play around with
def webReplayer(replay : List[Replay]) : (ActorRef,Int) = {
// everytime the factory is used a new port is assigned,
// so many instances can co-exist and tests can run in parallel without getting
// on eachother's toes
val newPort = lastPort.addAndGet(1)
// instantiate a new Spray server. Yes, its that simple!
val newServer = testSystem.actorOf(Props(new Helpers.ReplayerActor(replay)))
IO(Http) ! Http.Bind(newServer, interface = "127.0.0.1", port = newPort)
// return a pair of the server instance and the assigned port
// the newPort should be used by client code to construct the remote URI with
(newServer,newPort)
}

// object to use to tell the server to shutdown..
object ShutTestServer

// a concrete implementation for a Spray Actor to listen to incoming HttpRequest(..) messages
// provided a list of messages to be matched against arrived and content to be sent back
class ReplayerActor(var replay :List[Replay]) extends Actor {
var ioServer : ActorRef = null
override def receive: Receive = {
case _ : Http.Connected =>
sender ! Http.Register(self)
ioServer = sender()
// this is basically a "catch all" HttpRequest pattern
case HttpRequest(method, uri,_,_,_) =>
// a test to see that this is indeed expected to happen at this point
if (!replay.isEmpty && replay.head._1 == method && uri.path == replay.head._2){
sender ! HttpResponse(entity = replay.head._3)
replay = replay.tail
} else {
sender ! HttpResponse(status = 400, entity = "was expecting a different request at this point")
}
case ShutTestServer => ioServer ! PoisonPill
case _ => //ignore
}
}
}
-

And now a cocrete client code example to use this tool

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// some random content I would like to receive in this scenario (or test..)
val content =
s"""
|<html>
|<head>
|<title>sometitle</title>
|</head>
|<body>some body</body>
|</html>
""".stripMargin

// we use the factory method to get an instance of "replayer" to play around with
val (_testWebServer,port) = Helpers.webReplayer(
List(
(GET,Uri.Path("/somesite"),content)
)
)

// and where ever in your actual code, use the port we got back above and have it "hit" the server
val whatEver = YourHTTPGetter.fetch(s"http://127.0.0.1:$port/somesite")
assert(whatEver == content)
-

Profit!

- - -
- -
- -
- - - - -
- - - -
-
- -
- -
-
-
- - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/themes/landscape/Gruntfile.js b/themes/landscape/Gruntfile.js new file mode 100644 index 0000000..59fd5df --- /dev/null +++ b/themes/landscape/Gruntfile.js @@ -0,0 +1,46 @@ +module.exports = function(grunt){ + grunt.initConfig({ + gitclone: { + fontawesome: { + options: { + repository: 'https://github.com/FortAwesome/Font-Awesome.git', + directory: 'tmp/fontawesome' + }, + }, + fancybox: { + options: { + repository: 'https://github.com/fancyapps/fancyBox.git', + directory: 'tmp/fancybox' + } + } + }, + copy: { + fontawesome: { + expand: true, + cwd: 'tmp/fontawesome/fonts/', + src: ['**'], + dest: 'source/css/fonts/' + }, + fancybox: { + expand: true, + cwd: 'tmp/fancybox/source/', + src: ['**'], + dest: 'source/fancybox/' + } + }, + _clean: { + tmp: ['tmp'], + fontawesome: ['source/css/fonts'], + fancybox: ['source/fancybox'] + } + }); + + require('load-grunt-tasks')(grunt); + + grunt.renameTask('clean', '_clean'); + + grunt.registerTask('fontawesome', ['gitclone:fontawesome', 'copy:fontawesome', '_clean:tmp']); + grunt.registerTask('fancybox', ['gitclone:fancybox', 'copy:fancybox', '_clean:tmp']); + grunt.registerTask('default', ['gitclone', 'copy', '_clean:tmp']); + grunt.registerTask('clean', ['_clean']); +}; \ No newline at end of file diff --git a/themes/landscape/LICENSE b/themes/landscape/LICENSE new file mode 100644 index 0000000..9ce4d32 --- /dev/null +++ b/themes/landscape/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2013 Tommy Chen + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/themes/landscape/README.md b/themes/landscape/README.md new file mode 100644 index 0000000..6567104 --- /dev/null +++ b/themes/landscape/README.md @@ -0,0 +1,111 @@ +# Landscape + +A brand new default theme for [Hexo]. + +- [Preview](http://hexo.io/hexo-theme-landscape/) + +## Installation + +### Install + +``` bash +$ git clone https://github.com/tommy351/hexo-theme-landscape.git themes/landscape +``` + +**Landscape requires Hexo 2.4 and above.** + +### Enable + +Modify `theme` setting in `_config.yml` to `landscape`. + +### Update + +``` bash +cd themes/landscape +git pull +``` + +## Configuration + +``` yml +# Header +menu: + Home: / + Archives: /archives +rss: /atom.xml + +# Content +excerpt_link: Read More +fancybox: true + +# Sidebar +sidebar: right +widgets: +- category +- tag +- tagcloud +- archives +- recent_posts + +# Miscellaneous +google_analytics: +favicon: /favicon.png +twitter: +google_plus: +``` + +- **menu** - Navigation menu +- **rss** - RSS link +- **excerpt_link** - "Read More" link at the bottom of excerpted articles. `false` to hide the link. +- **fancybox** - Enable [Fancybox] +- **sidebar** - Sidebar style. You can choose `left`, `right`, `bottom` or `false`. +- **widgets** - Widgets displaying in sidebar +- **google_analytics** - Google Analytics ID +- **favicon** - Favicon path +- **twitter** - Twiiter ID +- **google_plus** - Google+ ID + +## Features + +### Fancybox + +Landscape uses [Fancybox] to showcase your photos. You can use Markdown syntax or fancybox tag plugin to add your photos. + +``` +![img caption](img url) + +{% fancybox img_url [img_thumbnail] [img_caption] %} +``` + +### Sidebar + +You can put your sidebar in left side, right side or bottom of your site by editing `sidebar` setting. + +Landscape provides 5 built-in widgets: + +- category +- tag +- tagcloud +- archives +- recent_posts + +All of them are enabled by default. You can edit them in `widget` setting. + +## Development + +### Requirements + +- [Grunt] 0.4+ +- Hexo 2.4+ + +### Grunt tasks + +- **default** - Download [Fancybox] and [Font Awesome]. +- **fontawesome** - Only download [Font Awesome]. +- **fancybox** - Only download [Fancybox]. +- **clean** - Clean temporarily files and downloaded files. + +[Hexo]: http://zespia.tw/hexo/ +[Fancybox]: http://fancyapps.com/fancybox/ +[Font Awesome]: http://fontawesome.io/ +[Grunt]: http://gruntjs.com/ diff --git a/themes/landscape/_config.yml b/themes/landscape/_config.yml new file mode 100644 index 0000000..3a8edb1 --- /dev/null +++ b/themes/landscape/_config.yml @@ -0,0 +1,26 @@ +# Header +menu: + Home: / + Archives: /archives +rss: /atom.xml + +# Content +excerpt_link: Read More +fancybox: true + +# Sidebar +sidebar: right +widgets: +- category +- tag +- tagcloud +- archive +- recent_posts + +# Miscellaneous +google_analytics: 'UA-28741273-1' +favicon: /favicon.png +twitter: +google_plus: +fb_admins: +fb_app_id: diff --git a/themes/landscape/layout/_partial/after-footer.ejs b/themes/landscape/layout/_partial/after-footer.ejs new file mode 100644 index 0000000..fdb2970 --- /dev/null +++ b/themes/landscape/layout/_partial/after-footer.ejs @@ -0,0 +1,24 @@ +<% if (config.disqus_shortname){ %> + +<% } %> + + + +<% if (theme.fancybox){ %> + + +<% } %> + +<%- js('js/script') %> \ No newline at end of file diff --git a/themes/landscape/layout/_partial/archive-post.ejs b/themes/landscape/layout/_partial/archive-post.ejs new file mode 100644 index 0000000..36f2cc3 --- /dev/null +++ b/themes/landscape/layout/_partial/archive-post.ejs @@ -0,0 +1,8 @@ +
+
+
+ <%- partial('post/date', {class_name: 'archive-article-date', date_format: 'MMM D'}) %> + <%- partial('post/title', {class_name: 'archive-article-title'}) %> +
+
+
\ No newline at end of file diff --git a/themes/landscape/layout/_partial/archive.ejs b/themes/landscape/layout/_partial/archive.ejs new file mode 100644 index 0000000..8401769 --- /dev/null +++ b/themes/landscape/layout/_partial/archive.ejs @@ -0,0 +1,33 @@ +<% if (pagination == 2){ %> + <% page.posts.each(function(post){ %> + <%- partial('article', {post: post, index: true}) %> + <% }) %> + <% if (page.total > 1){ %> + + <% } %> +<% } else { %> + <% var last; %> + <% page.posts.each(function(post, i){ %> + <% var year = post.date.year(); %> + <% if (last != year){ %> + <% if (last != null){ %> + + <% } %> + <% last = year; %> +
+ +
+ <% } %> + <%- partial('archive-post', {post: post, even: i % 2 == 0}) %> + <% }) %> + <% if (page.posts.length){ %> +
+ <% } %> +<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/_partial/article.ejs b/themes/landscape/layout/_partial/article.ejs new file mode 100644 index 0000000..404b065 --- /dev/null +++ b/themes/landscape/layout/_partial/article.ejs @@ -0,0 +1,44 @@ +
+ +
+ <%- partial('post/gallery') %> + <% if (post.link || post.title){ %> +
+ <%- partial('post/title', {class_name: 'article-title'}) %> +
+ <% } %> +
+ <% if (post.excerpt && index){ %> + <%- post.excerpt %> + <% if (theme.excerpt_link){ %> +

+ <%= theme.excerpt_link %> +

+ <% } %> + <% } else { %> + <%- post.content %> + <% } %> +
+
+ Share + <% if (post.comments && config.disqus_shortname){ %> + Comments + <% } %> + <%- partial('post/tag') %> +
+
+ <% if (!index){ %> + <%- partial('post/nav') %> + <% } %> +
+ +<% if (!index && post.comments && config.disqus_shortname){ %> +
+
+ +
+
+<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/_partial/footer.ejs b/themes/landscape/layout/_partial/footer.ejs new file mode 100644 index 0000000..b7fa5e2 --- /dev/null +++ b/themes/landscape/layout/_partial/footer.ejs @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/themes/landscape/layout/_partial/google-analytics.ejs b/themes/landscape/layout/_partial/google-analytics.ejs new file mode 100644 index 0000000..8f23d46 --- /dev/null +++ b/themes/landscape/layout/_partial/google-analytics.ejs @@ -0,0 +1,13 @@ +<% if (theme.google_analytics){ %> + +<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/_partial/head.ejs b/themes/landscape/layout/_partial/head.ejs new file mode 100644 index 0000000..261952c --- /dev/null +++ b/themes/landscape/layout/_partial/head.ejs @@ -0,0 +1,35 @@ + + + + + <% + var title = page.title; + + if (is_archive()){ + title = 'Archives'; + + if (is_month()){ + title += ': ' + page.year + '/' + page.month; + } else if (is_year()){ + title += ': ' + page.year; + } + } else if (is_category()){ + title = 'Category: ' + page.category; + } else if (is_tag()){ + title = 'Tag: ' + page.tag; + } + %> + <% if (title){ %><%= title %> | <% } %><%= config.title %> + + <%- open_graph({twitter_id: theme.twitter, google_plus: theme.google_plus, fb_admins: theme.fb_admins, fb_app_id: theme.fb_app_id}) %> + <% if (theme.rss){ %> + + <% } %> + <% if (theme.favicon){ %> + + <% } %> + + <%- css('css/style') %> + + <%- partial('google-analytics') %> + \ No newline at end of file diff --git a/themes/landscape/layout/_partial/header.ejs b/themes/landscape/layout/_partial/header.ejs new file mode 100644 index 0000000..107f5b1 --- /dev/null +++ b/themes/landscape/layout/_partial/header.ejs @@ -0,0 +1,32 @@ + \ No newline at end of file diff --git a/themes/landscape/layout/_partial/mobile-nav.ejs b/themes/landscape/layout/_partial/mobile-nav.ejs new file mode 100644 index 0000000..3e1c5bd --- /dev/null +++ b/themes/landscape/layout/_partial/mobile-nav.ejs @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/themes/landscape/layout/_partial/post/category.ejs b/themes/landscape/layout/_partial/post/category.ejs new file mode 100644 index 0000000..db2ed48 --- /dev/null +++ b/themes/landscape/layout/_partial/post/category.ejs @@ -0,0 +1,10 @@ +<% if (post.categories && post.categories.length){ %> +
+ <%- list_categories(post.categories, { + show_count: false, + class: 'article-category', + style: 'none', + separator: '►' + }) %> +
+<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/_partial/post/date.ejs b/themes/landscape/layout/_partial/post/date.ejs new file mode 100644 index 0000000..99470c8 --- /dev/null +++ b/themes/landscape/layout/_partial/post/date.ejs @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/themes/landscape/layout/_partial/post/gallery.ejs b/themes/landscape/layout/_partial/post/gallery.ejs new file mode 100644 index 0000000..ac264cc --- /dev/null +++ b/themes/landscape/layout/_partial/post/gallery.ejs @@ -0,0 +1,11 @@ +<% if (post.photos && post.photos.length){ %> +
+
+ <% post.photos.forEach(function(photo, i){ %> + + + + <% }) %> +
+
+<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/_partial/post/nav.ejs b/themes/landscape/layout/_partial/post/nav.ejs new file mode 100644 index 0000000..da272e0 --- /dev/null +++ b/themes/landscape/layout/_partial/post/nav.ejs @@ -0,0 +1,22 @@ +<% if (post.prev || post.next){ %> + +<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/_partial/post/tag.ejs b/themes/landscape/layout/_partial/post/tag.ejs new file mode 100644 index 0000000..e0f327f --- /dev/null +++ b/themes/landscape/layout/_partial/post/tag.ejs @@ -0,0 +1,6 @@ +<% if (post.tags && post.tags.length){ %> + <%- list_tags(post.tags, { + show_count: false, + class: 'article-tag' + }) %> +<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/_partial/post/title.ejs b/themes/landscape/layout/_partial/post/title.ejs new file mode 100644 index 0000000..6a792c2 --- /dev/null +++ b/themes/landscape/layout/_partial/post/title.ejs @@ -0,0 +1,15 @@ +<% if (post.link){ %> +

+ +

+<% } else if (post.title){ %> + <% if (index){ %> +

+ <%= post.title %> +

+ <% } else { %> +

+ <%= post.title %> +

+ <% } %> +<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/_partial/sidebar.ejs b/themes/landscape/layout/_partial/sidebar.ejs new file mode 100644 index 0000000..c1e48e5 --- /dev/null +++ b/themes/landscape/layout/_partial/sidebar.ejs @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/themes/landscape/layout/_widget/archive.ejs b/themes/landscape/layout/_widget/archive.ejs new file mode 100644 index 0000000..9a428ba --- /dev/null +++ b/themes/landscape/layout/_widget/archive.ejs @@ -0,0 +1,8 @@ +<% if (site.posts.length){ %> +
+

Archives

+
+ <%- list_archives() %> +
+
+<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/_widget/category.ejs b/themes/landscape/layout/_widget/category.ejs new file mode 100644 index 0000000..7836a27 --- /dev/null +++ b/themes/landscape/layout/_widget/category.ejs @@ -0,0 +1,8 @@ +<% if (site.categories.length){ %> +
+

Categories

+
+ <%- list_categories() %> +
+
+<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/_widget/recent_posts.ejs b/themes/landscape/layout/_widget/recent_posts.ejs new file mode 100644 index 0000000..3986ffb --- /dev/null +++ b/themes/landscape/layout/_widget/recent_posts.ejs @@ -0,0 +1,14 @@ +<% if (site.posts.length){ %> +
+

Recents

+
+ +
+
+<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/_widget/tag.ejs b/themes/landscape/layout/_widget/tag.ejs new file mode 100644 index 0000000..c163c41 --- /dev/null +++ b/themes/landscape/layout/_widget/tag.ejs @@ -0,0 +1,8 @@ +<% if (site.tags.length){ %> +
+

Tags

+
+ <%- list_tags() %> +
+
+<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/_widget/tagcloud.ejs b/themes/landscape/layout/_widget/tagcloud.ejs new file mode 100644 index 0000000..4b82283 --- /dev/null +++ b/themes/landscape/layout/_widget/tagcloud.ejs @@ -0,0 +1,8 @@ +<% if (site.tags.length){ %> +
+

Tag Cloud

+
+ <%- tagcloud() %> +
+
+<% } %> \ No newline at end of file diff --git a/themes/landscape/layout/archive.ejs b/themes/landscape/layout/archive.ejs new file mode 100644 index 0000000..52f9b21 --- /dev/null +++ b/themes/landscape/layout/archive.ejs @@ -0,0 +1 @@ +<%- partial('_partial/archive', {pagination: config.archive, index: true}) %> \ No newline at end of file diff --git a/themes/landscape/layout/category.ejs b/themes/landscape/layout/category.ejs new file mode 100644 index 0000000..3ffe252 --- /dev/null +++ b/themes/landscape/layout/category.ejs @@ -0,0 +1 @@ +<%- partial('_partial/archive', {pagination: config.category, index: true}) %> \ No newline at end of file diff --git a/themes/landscape/layout/index.ejs b/themes/landscape/layout/index.ejs new file mode 100644 index 0000000..60a2c68 --- /dev/null +++ b/themes/landscape/layout/index.ejs @@ -0,0 +1 @@ +<%- partial('_partial/archive', {pagination: 2, index: true}) %> \ No newline at end of file diff --git a/themes/landscape/layout/layout.ejs b/themes/landscape/layout/layout.ejs new file mode 100644 index 0000000..1524a62 --- /dev/null +++ b/themes/landscape/layout/layout.ejs @@ -0,0 +1,18 @@ +<%- partial('_partial/head') %> + +
+
+ <%- partial('_partial/header') %> +
+
<%- body %>
+ <% if (theme.sidebar && theme.sidebar !== 'bottom'){ %> + <%- partial('_partial/sidebar') %> + <% } %> +
+ <%- partial('_partial/footer') %> +
+ <%- partial('_partial/mobile-nav') %> + <%- partial('_partial/after-footer') %> +
+ + \ No newline at end of file diff --git a/themes/landscape/layout/page.ejs b/themes/landscape/layout/page.ejs new file mode 100644 index 0000000..bea6318 --- /dev/null +++ b/themes/landscape/layout/page.ejs @@ -0,0 +1 @@ +<%- partial('_partial/article', {post: page, index: false}) %> \ No newline at end of file diff --git a/themes/landscape/layout/post.ejs b/themes/landscape/layout/post.ejs new file mode 100644 index 0000000..bea6318 --- /dev/null +++ b/themes/landscape/layout/post.ejs @@ -0,0 +1 @@ +<%- partial('_partial/article', {post: page, index: false}) %> \ No newline at end of file diff --git a/themes/landscape/layout/tag.ejs b/themes/landscape/layout/tag.ejs new file mode 100644 index 0000000..048cdb0 --- /dev/null +++ b/themes/landscape/layout/tag.ejs @@ -0,0 +1 @@ +<%- partial('_partial/archive', {pagination: config.tag, index: true}) %> \ No newline at end of file diff --git a/themes/landscape/package.json b/themes/landscape/package.json new file mode 100644 index 0000000..a11e9f6 --- /dev/null +++ b/themes/landscape/package.json @@ -0,0 +1,12 @@ +{ + "name": "hexo-theme-landscape", + "version": "0.0.1", + "private": true, + "devDependencies": { + "grunt": "~0.4.2", + "load-grunt-tasks": "~0.2.0", + "grunt-git": "~0.2.2", + "grunt-contrib-clean": "~0.5.0", + "grunt-contrib-copy": "~0.4.1" + } +} diff --git a/themes/landscape/scripts/fancybox.js b/themes/landscape/scripts/fancybox.js new file mode 100644 index 0000000..83f1fdc --- /dev/null +++ b/themes/landscape/scripts/fancybox.js @@ -0,0 +1,24 @@ +var rUrl = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[.\!\/\\w]*))?)/; + +/** +* Fancybox tag +* +* Syntax: +* {% fancybox /path/to/image [/path/to/thumbnail] [title] %} +*/ + +hexo.extend.tag.register('fancybox', function(args){ + var original = args.shift(), + thumbnail = ''; + + if (args.length && rUrl.test(args[0])){ + thumbnail = args.shift(); + } + + var title = args.join(' '); + + return '' + + '' + title + '' + '' + + (title ? '' + title + '' : ''); +}); \ No newline at end of file diff --git a/themes/landscape/source/css/_extend.styl b/themes/landscape/source/css/_extend.styl new file mode 100644 index 0000000..96a1817 --- /dev/null +++ b/themes/landscape/source/css/_extend.styl @@ -0,0 +1,63 @@ +$block-caption + text-decoration: none + text-transform: uppercase + letter-spacing: 2px + color: color-grey + margin-bottom: 1em + margin-left: 5px + line-height: 1em + text-shadow: 0 1px #fff + font-weight: bold + +$block + background: #fff + box-shadow: 1px 2px 3px #ddd + border: 1px solid color-border + border-radius: 3px + +$base-style + h1 + font-size: 2em + h2 + font-size: 1.5em + h3 + font-size: 1.3em + h4 + font-size: 1.2em + h5 + font-size: 1em + h6 + font-size: 1em + color: color-grey + hr + border: 1px dashed color-border + strong + font-weight: bold + em, cite + font-style: italic + sup, sub + font-size: 0.75em + line-height: 0 + position: relative + vertical-align: baseline + sup + top: -0.5em + sub + bottom: -0.2em + small + font-size: 0.85em + acronym, abbr + border-bottom: 1px dotted + ul, ol, dl + margin: 0 20px + line-height: line-height + ul, ol + ul, ol + margin-top: 0 + margin-bottom: 0 + ul + list-style: disc + ol + list-style: decimal + dt + font-weight: bold \ No newline at end of file diff --git a/themes/landscape/source/css/_partial/archive.styl b/themes/landscape/source/css/_partial/archive.styl new file mode 100644 index 0000000..165b678 --- /dev/null +++ b/themes/landscape/source/css/_partial/archive.styl @@ -0,0 +1,78 @@ +.archives-wrap + margin: block-margin 0 + +.archives + clearfix() + +.archive-year-wrap + margin-bottom: 1em + +.archive-year + @extend $block-caption + +.archives + column-gap: 10px + @media mq-tablet + column-count: 2 + @media mq-normal + column-count: 3 + +.archive-article + avoid-column-break() + +.archive-article-inner + @extend $block + padding: 10px + margin-bottom: 15px + +.archive-article-title + text-decoration: none + font-weight: bold + color: color-default + transition: color 0.2s + line-height: line-height + &:hover + color: color-link + +.archive-article-footer + margin-top: 1em + +.archive-article-date + color: color-grey + text-decoration: none + font-size: 0.85em + line-height: 1em + margin-bottom: 0.5em + display: block + +#page-nav + clearfix() + margin: block-margin auto + background: #fff + box-shadow: 1px 2px 3px #ddd + border: 1px solid color-border + border-radius: 3px + text-align: center + color: color-grey + overflow: hidden + a, span + padding: 10px 20px + a + color: color-grey + text-decoration: none + &:hover + background: color-grey + color: #fff + .prev + float: left + .next + float: right + .page-number + display: inline-block + @media mq-mobile + display: none + .current + color: color-default + font-weight: bold + .space + color: color-border \ No newline at end of file diff --git a/themes/landscape/source/css/_partial/article.styl b/themes/landscape/source/css/_partial/article.styl new file mode 100644 index 0000000..46094f9 --- /dev/null +++ b/themes/landscape/source/css/_partial/article.styl @@ -0,0 +1,357 @@ +.article + margin: block-margin 0 + +.article-inner + @extend $block + overflow: hidden + +.article-meta + clearfix() + +.article-date + @extend $block-caption + float: left + +.article-category + float: left + line-height: 1em + color: #ccc + text-shadow: 0 1px #fff + margin-left: 8px + &:before + content: "\2022" + +.article-category-link + @extend $block-caption + margin: 0 12px 1em + +.article-header + padding: article-padding article-padding 0 + +.article-title + text-decoration: none + font-size: 2em + font-weight: bold + color: color-default + line-height: line-height-title + transition: color 0.2s + a&:hover + color: color-link + +.article-entry + @extend $base-style + clearfix() + color: color-default + padding: 0 article-padding + p, table + line-height: line-height + margin: line-height 0 + h1, h2, h3, h4, h5, h6 + font-weight: bold + h1, h2, h3, h4, h5, h6 + line-height: line-height-title + margin: line-height-title 0 + a + color: color-link + text-decoration: none + &:hover + text-decoration: underline + ul, ol, dl + margin-top: line-height + margin-bottom: line-height + img, video + max-width: 100% + height: auto + display: block + margin: auto + iframe + border: none + table + width: 100% + border-collapse: collapse + border-spacing: 0 + th + font-weight: bold + border-bottom: 3px solid color-border + padding-bottom: 0.5em + td + border-bottom: 1px solid color-border + padding: 10px 0 + blockquote + font-family: font-serif + font-size: 1.4em + margin: line-height 20px + text-align: center + footer + font-size: font-size + margin: line-height 0 + font-family: font-sans + cite + &:before + content: "—" + padding: 0 0.5em + .pullquote + text-align: left + width: 45% + margin: 0 + &.left + margin-left: 0.5em + margin-right: 1em + &.right + margin-right: 0.5em + margin-left: 1em + .caption + color: color-grey + display: block + font-size: 0.9em + margin-top: 0.5em + position: relative + text-align: center + // http://webdesignerwall.com/tutorials/css-elastic-videos + .video-container + position: relative + padding-top: (9 / 16 * 100)% // 16:9 ratio + height: 0 + overflow: hidden + iframe, object, embed + position: absolute + top: 0 + left: 0 + width: 100% + height: 100% + margin-top: 0 + +.article-more-link a + display: inline-block + line-height: 1em + padding: 6px 15px + border-radius: 15px + background: color-background + color: color-grey + text-shadow: 0 1px #fff + text-decoration: none + &:hover + background: color-link + color: #fff + text-decoration: none + text-shadow: 0 1px darken(color-link, 20%) + +.article-footer + clearfix() + font-size: 0.85em + line-height: line-height + border-top: 1px solid color-border + padding-top: line-height + margin: 0 article-padding article-padding + a + color: color-grey + text-decoration: none + &:hover + color: color-default + +.article-tag-list-item + float: left + margin-right: 10px + +.article-tag-list-link + &:before + content: "#" + +.article-comment-link + float: right + &:before + content: "\f075" + font-family: font-icon + padding-right: 8px + +.article-share-link + cursor: pointer + float: right + margin-left: 20px + &:before + content: "\f064" + font-family: font-icon + padding-right: 6px + +#article-nav + clearfix() + position: relative + @media mq-normal + margin: block-margin 0 + &:before + absolute-center(8px) + content: "" + border-radius: 50% + background: color-border + box-shadow: 0 1px 2px #fff + +.article-nav-link-wrap + text-decoration: none + text-shadow: 0 1px #fff + color: color-grey + box-sizing: border-box + margin-top: block-margin + text-align: center + display: block + &:hover + color: color-default + @media mq-normal + width: 50% + margin-top: 0 + +#article-nav-newer + @media mq-normal + float: left + text-align: right + padding-right: 20px + +#article-nav-older + @media mq-normal + float: right + text-align: left + padding-left: 20px + +.article-nav-caption + text-transform: uppercase + letter-spacing: 2px + color: color-border + line-height: 1em + font-weight: bold + #article-nav-newer & + margin-right: -2px + +.article-nav-title + font-size: 0.85em + line-height: line-height + margin-top: 0.5em + +.article-share-box + position: absolute + display: none + background: #fff + box-shadow: 1px 2px 10px rgba(0, 0, 0, 0.2) + border-radius: 3px + margin-left: -145px + overflow: hidden + z-index: 1 + &.on + display: block + +.article-share-input + width: 100% + background: none + box-sizing: border-box + font: 14px font-sans + padding: 0 15px + color: color-default + outline: none + border: 1px solid color-border + border-radius: 3px 3px 0 0 + height: 36px + line-height: 36px + +.article-share-links + clearfix() + background: color-background + +$article-share-link + width: 50px + height: 36px + display: block + float: left + position: relative + color: #999 + text-shadow: 0 1px #fff + &:before + font-size: 20px + font-family: font-icon + absolute-center(@font-size) + text-align: center + &:hover + color: #fff + +.article-share-twitter + @extend $article-share-link + &:before + content: "\f099" + &:hover + background: color-twitter + text-shadow: 0 1px darken(color-twitter, 20%) + +.article-share-facebook + @extend $article-share-link + &:before + content: "\f09a" + &:hover + background: color-facebook + text-shadow: 0 1px darken(color-facebook, 20%) + +.article-share-pinterest + @extend $article-share-link + &:before + content: "\f0d2" + &:hover + background: color-pinterest + text-shadow: 0 1px darken(color-pinterest, 20%) + +.article-share-google + @extend $article-share-link + &:before + content: "\f0d5" + &:hover + background: color-google + text-shadow: 0 1px darken(color-google, 20%) + +.article-gallery + background: #000 + position: relative + +.article-gallery-photos + position: relative + overflow: hidden + +.article-gallery-img + display: none + max-width: 100% + &:first-child + display: block + &.loaded + position: absolute + display: block + img + display: block + max-width: 100% + margin: 0 auto +/* +$article-gallery-ctrl + position: absolute + top: 0 + height: 100% + width: 60px + color: #fff + text-shadow: 0 0 3px rgba(0, 0, 0, 0.3) + opacity: 0.3 + transition: opacity 0.2s + cursor: pointer + &:hover + opacity: 0.8 + &:before + font-size: 30px + font-family: font-icon + position: absolute + top: 50% + margin-top: @font-size * -0.5 + +.article-gallery-prev + @extend $article-gallery-ctrl + left: 0 + &:before + content: "\f053" + left: 15px + +.article-gallery-next + @extend $article-gallery-ctrl + right: 0 + &:before + content: "\f054" + right: 15px*/ \ No newline at end of file diff --git a/themes/landscape/source/css/_partial/comment.styl b/themes/landscape/source/css/_partial/comment.styl new file mode 100644 index 0000000..296b7dd --- /dev/null +++ b/themes/landscape/source/css/_partial/comment.styl @@ -0,0 +1,9 @@ +#comments + background: #fff + box-shadow: 1px 2px 3px #ddd + padding: article-padding + border: 1px solid color-border + border-radius: 3px + margin: block-margin 0 + a + color: color-link \ No newline at end of file diff --git a/themes/landscape/source/css/_partial/footer.styl b/themes/landscape/source/css/_partial/footer.styl new file mode 100644 index 0000000..fe2fd24 --- /dev/null +++ b/themes/landscape/source/css/_partial/footer.styl @@ -0,0 +1,14 @@ +#footer + background: color-footer-background + padding: 50px 0 + border-top: 1px solid color-border + color: color-grey + a + color: color-link + text-decoration: none + &:hover + text-decoration: underline + +#footer-info + line-height: line-height + font-size: 0.85em \ No newline at end of file diff --git a/themes/landscape/source/css/_partial/header.styl b/themes/landscape/source/css/_partial/header.styl new file mode 100644 index 0000000..24b3368 --- /dev/null +++ b/themes/landscape/source/css/_partial/header.styl @@ -0,0 +1,165 @@ +#header + height: banner-height + position: relative + border-bottom: 1px solid color-border + &:before, &:after + content: "" + position: absolute + left: 0 + right: 0 + height: 0px + &:before + top: 0 + background: linear-gradient(rgba(0, 0, 0, 0.2), transparent) + &:after + bottom: 0 + background: linear-gradient(transparent, rgba(0, 0, 0, 0.2)) + +#header-outer + height: 100% + position: relative + +#header-inner + position: relative + overflow: hidden + +#banner + position: absolute + top: 0 + left: 0 + width: 100% + height: 100% + background-color: #0b1b1c + background-size: cover + z-index: -1 + +#header-title + text-align: center + height: logo-size + position: absolute + top: 50% + left: 0 + margin-top: logo-size * -0.5 + +$logo-text + text-decoration: none + color: #fff + font-weight: 300 + text-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) + +#logo + @extend $logo-text + font-size: logo-size + line-height: logo-size + letter-spacing: 2px + +#subtitle + @extend $logo-text + font-size: subtitle-size + line-height: subtitle-size + letter-spacing: 1px + +#subtitle-wrap + margin-top: subtitle-size + +#main-nav + float: left + margin-left: -15px + +$nav-link + float: left + color: #fff + opacity: 0.6 + text-decoration: none + text-shadow: 0 1px rgba(0, 0, 0, 0.2) + transition: opacity 0.2s + display: block + padding: 20px 15px + &:hover + opacity: 1 + +.nav-icon + @extend $nav-link + font-family: font-icon + text-align: center + font-size: font-size + width: font-size + height: font-size + padding: 20px 15px + position: relative + cursor: pointer + +.main-nav-link + @extend $nav-link + font-weight: 300 + letter-spacing: 1px + @media mq-mobile + display: none + +#main-nav-toggle + display: none + &:before + content: "\f0c9" + @media mq-mobile + display: block + +#sub-nav + float: right + margin-right: -15px + +#nav-rss-link + &:before + content: "\f09e" + +#nav-search-btn + &:before + content: "\f002" + +#search-form-wrap + position: absolute + top: 15px + width: 150px + height: 30px + right: -150px + opacity: 0 + transition: 0.2s ease-out + &.on + opacity: 1 + right: 0 + @media mq-mobile + width: 100% + right: -100% + +.search-form + position: absolute + top: 0 + left: 0 + right: 0 + background: #fff + padding: 5px 15px + border-radius: 15px + box-shadow: 0 0 10px rgba(0, 0, 0, 0.3) + +.search-form-input + border: none + background: none + color: color-default + width: 100% + font: 13px font-sans + outline: none + &::-webkit-search-results-decoration + &::-webkit-search-cancel-button + -webkit-appearance: none + +.search-form-submit + position: absolute + top: 50% + right: 10px + margin-top: -7px + font: 13px font-icon + border: none + background: none + color: #bbb + cursor: pointer + &:hover, &:focus + color: #777 diff --git a/themes/landscape/source/css/_partial/highlight.styl b/themes/landscape/source/css/_partial/highlight.styl new file mode 100644 index 0000000..9151638 --- /dev/null +++ b/themes/landscape/source/css/_partial/highlight.styl @@ -0,0 +1,154 @@ +// https://github.com/chriskempson/tomorrow-theme +highlight-background = #2d2d2d +highlight-current-line = #393939 +highlight-selection = #515151 +highlight-foreground = #cccccc +highlight-comment = #999999 +highlight-red = #f2777a +highlight-orange = #f99157 +highlight-yellow = #ffcc66 +highlight-green = #99cc99 +highlight-aqua = #66cccc +highlight-blue = #6699cc +highlight-purple = #cc99cc + +$code-block + background: highlight-background + margin: 0 article-padding * -1 + padding: 15px article-padding + border-style: solid + border-color: color-border + border-width: 1px 0 + overflow: auto + color: highlight-foreground + line-height: font-size * line-height + +$line-numbers + color: #666 + font-size: 0.85em + +.article-entry + pre, code + font-family: font-mono + code + background: color-background + text-shadow: 0 1px #fff + padding: 0 0.3em + pre + @extend $code-block + code + background: none + text-shadow: none + padding: 0 + .highlight + @extend $code-block + pre + border: none + margin: 0 + padding: 0 + table + margin: 0 + width: auto + td + border: none + padding: 0 + figcaption + clearfix() + font-size: 0.85em + color: highlight-comment + line-height: 1em + margin-bottom: 1em + a + float: right + .gutter pre + @extend $line-numbers + text-align: right + padding-right: 20px + .gist + margin: 0 article-padding * -1 + border-style: solid + border-color: color-border + border-width: 1px 0 + background: highlight-background + padding: 15px article-padding 15px 0 + .gist-file + border: none + font-family: font-mono + margin: 0 + .gist-data + background: none + border: none + .line-numbers + @extend $line-numbers + background: none + border: none + padding: 0 20px 0 0 + .line-data + padding: 0 !important + .highlight + margin: 0 + padding: 0 + border: none + .gist-meta + background: highlight-background + color: highlight-comment + font: 0.85em font-sans + text-shadow: 0 0 + padding: 0 + margin-top: 1em + margin-left: article-padding + a + color: color-link + font-weight: normal + &:hover + text-decoration: underline + +pre + .comment + .title + color: highlight-comment + .variable + .attribute + .tag + .regexp + .ruby .constant + .xml .tag .title + .xml .pi + .xml .doctype + .html .doctype + .css .id + .css .class + .css .pseudo + color: highlight-red + .number + .preprocessor + .built_in + .literal + .params + .constant + color: highlight-orange + .class + .ruby .class .title + .css .rules .attribute + color: highlight-green + .string + .value + .inheritance + .header + .ruby .symbol + .xml .cdata + color: highlight-green + .css .hexcolor + color: highlight-aqua + .function + .python .decorator + .python .title + .ruby .function .title + .ruby .title .keyword + .perl .sub + .javascript .title + .coffeescript .title + color: highlight-blue + .keyword + .javascript .function + color: highlight-purple \ No newline at end of file diff --git a/themes/landscape/source/css/_partial/mobile.styl b/themes/landscape/source/css/_partial/mobile.styl new file mode 100644 index 0000000..eb68b3a --- /dev/null +++ b/themes/landscape/source/css/_partial/mobile.styl @@ -0,0 +1,19 @@ +@media mq-mobile + #mobile-nav + position: absolute + top: 0 + left: 0 + width: mobile-nav-width + height: 100% + background: color-mobile-nav-background + border-right: 1px solid #fff + +@media mq-mobile + .mobile-nav-link + display: block + color: color-grey + text-decoration: none + padding: 15px 20px + font-weight: bold + &:hover + color: #fff diff --git a/themes/landscape/source/css/_partial/sidebar-aside.styl b/themes/landscape/source/css/_partial/sidebar-aside.styl new file mode 100644 index 0000000..838b167 --- /dev/null +++ b/themes/landscape/source/css/_partial/sidebar-aside.styl @@ -0,0 +1,27 @@ +#sidebar + @media mq-normal + column(sidebar-column) + +.widget-wrap + margin: block-margin 0 + +.widget-title + @extend $block-caption + +.widget + color: color-sidebar-text + text-shadow: 0 1px #fff + background: color-widget-background + box-shadow: 0 -1px 4px color-widget-border inset + border: 1px solid color-widget-border + padding: 15px + border-radius: 3px + a + color: color-link + text-decoration: none + &:hover + text-decoration: underline + ul, ol, dl + ul, ol, dl + margin-left: 15px + list-style: disc \ No newline at end of file diff --git a/themes/landscape/source/css/_partial/sidebar-bottom.styl b/themes/landscape/source/css/_partial/sidebar-bottom.styl new file mode 100644 index 0000000..b385275 --- /dev/null +++ b/themes/landscape/source/css/_partial/sidebar-bottom.styl @@ -0,0 +1,15 @@ +.widget-wrap + margin-bottom: block-margin !important + @media mq-normal + column(sidebar-column) + +.widget-title + color: #ccc + text-transform: uppercase + letter-spacing: 2px + margin-bottom: 1em + line-height: 1em + font-weight: bold + +.widget + color: color-grey \ No newline at end of file diff --git a/themes/landscape/source/css/_partial/sidebar.styl b/themes/landscape/source/css/_partial/sidebar.styl new file mode 100644 index 0000000..0c12b4c --- /dev/null +++ b/themes/landscape/source/css/_partial/sidebar.styl @@ -0,0 +1,34 @@ +if sidebar is bottom + @import "sidebar-bottom" +else + @import "sidebar-aside" + +.widget + @extend $base-style + line-height: line-height + word-wrap: break-word + font-size: 0.9em + ul, ol + list-style: none + margin: 0 + ul, ol + margin: 0 20px + ul + list-style: disc + ol + list-style: decimal + +.category-list-count +.tag-list-count +.archive-list-count + padding-left: 5px + color: color-grey + font-size: 0.85em + &:before + content: "(" + &:after + content: ")" + +.tagcloud + a + margin-right: 5px \ No newline at end of file diff --git a/themes/landscape/source/css/_util/grid.styl b/themes/landscape/source/css/_util/grid.styl new file mode 100644 index 0000000..2a14dd2 --- /dev/null +++ b/themes/landscape/source/css/_util/grid.styl @@ -0,0 +1,38 @@ +///////////////// +// Semantic.gs // for Stylus: http://learnboost.github.com/stylus/ +///////////////// + +// Utility function — you should never need to modify this +// _gridsystem-width = (column-width + gutter-width) * columns +gridsystem-width(_columns = columns) + (column-width + gutter-width) * _columns + +// Set @total-width to 100% for a fluid layout +// total-width = gridsystem-width(columns) +total-width = 100% + +////////// +// GRID // +////////// + +body + clearfix() + width: 100% + +row(_columns = columns) + clearfix() + display: block + width: total-width * ((gutter-width + gridsystem-width(_columns)) / gridsystem-width(_columns)) + margin: 0 total-width * (((gutter-width * .5) / gridsystem-width(_columns)) * -1) + +column(x, _columns = columns) + display: inline + float: left + width: total-width * ((((gutter-width + column-width) * x) - gutter-width) / gridsystem-width(_columns)) + margin: 0 total-width * ((gutter-width * .5) / gridsystem-width(_columns)) + +push(offset = 1) + margin-left: total-width * (((gutter-width + column-width) * offset) / gridsystem-width(columns)) + +pull(offset = 1) + margin-right: total-width * (((gutter-width + column-width) * offset) / gridsystem-width(columns)) \ No newline at end of file diff --git a/themes/landscape/source/css/_util/mixin.styl b/themes/landscape/source/css/_util/mixin.styl new file mode 100644 index 0000000..b56f037 --- /dev/null +++ b/themes/landscape/source/css/_util/mixin.styl @@ -0,0 +1,31 @@ +// http://www.zeldman.com/2012/03/01/replacing-the-9999px-hack-new-image-replacement/ +hide-text() + text-indent: 100% + white-space: nowrap + overflow: hidden + +// http://codepen.io/shshaw/full/gEiDt +absolute-center(width, height = width) + // margin: auto + // position: absolute + // top: 50% + // top: 0 + // left: 0 + // bottom: 0 + // right: 0 + // width: width + // height: height + // overflow: auto + width: width + height: height + position: absolute + top: 50% + left: 50% + margin-top: width * -0.5 + margin-left: height * -0.5 + +avoid-column-break() + vendor("column-break-inside", avoid, only: webkit) + page-break-inside: avoid // for firefox + overflow: hidden // fix for firefox + break-inside: avoid-column diff --git a/themes/landscape/source/css/_variables.styl b/themes/landscape/source/css/_variables.styl new file mode 100644 index 0000000..a780997 --- /dev/null +++ b/themes/landscape/source/css/_variables.styl @@ -0,0 +1,56 @@ +// Colors +color-default = #555 +color-grey = #999 +color-border = #ddd +color-link = #258fb8 +color-background = #eee +color-sidebar-text = #777 +color-widget-background = #ddd +color-widget-border = #ccc +color-footer-background = #262a30 +color-mobile-nav-background = #191919 +color-twitter = #00aced +color-facebook = #3b5998 +color-pinterest = #cb2027 +color-google = #dd4b39 + +// Fonts +font-sans = "Helvetica Neue", Helvetica, Arial, sans-serif +font-serif = Georgia, "Times New Roman", serif +font-mono = "Source Code Pro", Consolas, Monaco, Menlo, Consolas, monospace +font-icon = FontAwesome +font-icon-path = "fonts/fontawesome-webfont" +font-icon-version = "4.0.3" +font-size = 14px +line-height = 1.6em +line-height-title = 1.1em + +// Header +logo-size = 40px +subtitle-size = 16px +// banner-height = 300px +banner-url = "images/banner.jpg" + +sidebar = hexo-config("sidebar") + +// Layout +block-margin = 50px +article-padding = 20px +mobile-nav-width = 280px +main-column = 9 +sidebar-column = 3 + +if sidebar and sidebar isnt bottom + _sidebar-column = sidebar-column +else + _sidebar-column = 0 + +// Grids +column-width = 80px +gutter-width = 20px +columns = main-column + _sidebar-column + +// Media queries +mq-mobile = "screen and (max-width: 479px)" +mq-tablet = "screen and (min-width: 480px) and (max-width: 767px)" +mq-normal = "screen and (min-width: 768px)" diff --git a/css/fonts/FontAwesome.otf b/themes/landscape/source/css/fonts/FontAwesome.otf similarity index 100% rename from css/fonts/FontAwesome.otf rename to themes/landscape/source/css/fonts/FontAwesome.otf diff --git a/css/fonts/fontawesome-webfont.eot b/themes/landscape/source/css/fonts/fontawesome-webfont.eot similarity index 100% rename from css/fonts/fontawesome-webfont.eot rename to themes/landscape/source/css/fonts/fontawesome-webfont.eot diff --git a/css/fonts/fontawesome-webfont.svg b/themes/landscape/source/css/fonts/fontawesome-webfont.svg similarity index 100% rename from css/fonts/fontawesome-webfont.svg rename to themes/landscape/source/css/fonts/fontawesome-webfont.svg diff --git a/css/fonts/fontawesome-webfont.ttf b/themes/landscape/source/css/fonts/fontawesome-webfont.ttf similarity index 100% rename from css/fonts/fontawesome-webfont.ttf rename to themes/landscape/source/css/fonts/fontawesome-webfont.ttf diff --git a/css/fonts/fontawesome-webfont.woff b/themes/landscape/source/css/fonts/fontawesome-webfont.woff similarity index 100% rename from css/fonts/fontawesome-webfont.woff rename to themes/landscape/source/css/fonts/fontawesome-webfont.woff diff --git a/css/images/banner.jpg b/themes/landscape/source/css/images/banner.jpg similarity index 100% rename from css/images/banner.jpg rename to themes/landscape/source/css/images/banner.jpg diff --git a/themes/landscape/source/css/style.styl b/themes/landscape/source/css/style.styl new file mode 100644 index 0000000..ffb0e2a --- /dev/null +++ b/themes/landscape/source/css/style.styl @@ -0,0 +1,88 @@ +@import "nib" +@import "_variables" +@import "_util/mixin" +@import "_util/grid" + +global-reset() + +input, button + margin: 0 + padding: 0 + &::-moz-focus-inner + border: 0 + padding: 0 + +@font-face + font-family: FontAwesome + font-style: normal + font-weight: normal + src: url(font-icon-path + ".eot?v=#" + font-icon-version) + src: url(font-icon-path + ".eot?#iefix&v=#" + font-icon-version) format("embedded-opentype"), + url(font-icon-path + ".woff?v=#" + font-icon-version) format("woff"), + url(font-icon-path + ".ttf?v=#" + font-icon-version) format("truetype"), + url(font-icon-path + ".svg#fontawesomeregular?v=#" + font-icon-version) format("svg") + +html, body, #container + height: 100% + +body + background: color-background + font: font-size font-sans + -webkit-text-size-adjust: 100% + +.outer + clearfix() + max-width: (column-width + gutter-width) * columns + gutter-width + margin: 0 auto + padding: 0 gutter-width + +.inner + column(columns) + +.left, .alignleft + float: left + +.right, .alignright + float: right + +.clear + clear: both + +#container + position: relative + +.mobile-nav-on + overflow: hidden + +#wrap + height: 100% + width: 100% + position: absolute + top: 0 + left: 0 + transition: 0.2s ease-out + z-index: 1 + background: color-background + .mobile-nav-on & + left: mobile-nav-width + +if sidebar and sidebar isnt bottom + #main + @media mq-normal + column(main-column) + +if sidebar is left + #main + float: right + +@import "_extend" +@import "_partial/header" +@import "_partial/article" +@import "_partial/comment" +@import "_partial/archive" +@import "_partial/footer" +@import "_partial/highlight" +@import "_partial/mobile" + +if sidebar + @import "_partial/sidebar" \ No newline at end of file diff --git a/fancybox/blank.gif b/themes/landscape/source/fancybox/blank.gif similarity index 100% rename from fancybox/blank.gif rename to themes/landscape/source/fancybox/blank.gif diff --git a/fancybox/fancybox_loading.gif b/themes/landscape/source/fancybox/fancybox_loading.gif similarity index 100% rename from fancybox/fancybox_loading.gif rename to themes/landscape/source/fancybox/fancybox_loading.gif diff --git a/fancybox/fancybox_loading@2x.gif b/themes/landscape/source/fancybox/fancybox_loading@2x.gif similarity index 100% rename from fancybox/fancybox_loading@2x.gif rename to themes/landscape/source/fancybox/fancybox_loading@2x.gif diff --git a/fancybox/fancybox_overlay.png b/themes/landscape/source/fancybox/fancybox_overlay.png similarity index 100% rename from fancybox/fancybox_overlay.png rename to themes/landscape/source/fancybox/fancybox_overlay.png diff --git a/fancybox/fancybox_sprite.png b/themes/landscape/source/fancybox/fancybox_sprite.png similarity index 100% rename from fancybox/fancybox_sprite.png rename to themes/landscape/source/fancybox/fancybox_sprite.png diff --git a/fancybox/fancybox_sprite@2x.png b/themes/landscape/source/fancybox/fancybox_sprite@2x.png similarity index 100% rename from fancybox/fancybox_sprite@2x.png rename to themes/landscape/source/fancybox/fancybox_sprite@2x.png diff --git a/fancybox/helpers/fancybox_buttons.png b/themes/landscape/source/fancybox/helpers/fancybox_buttons.png similarity index 100% rename from fancybox/helpers/fancybox_buttons.png rename to themes/landscape/source/fancybox/helpers/fancybox_buttons.png diff --git a/fancybox/helpers/jquery.fancybox-buttons.css b/themes/landscape/source/fancybox/helpers/jquery.fancybox-buttons.css similarity index 100% rename from fancybox/helpers/jquery.fancybox-buttons.css rename to themes/landscape/source/fancybox/helpers/jquery.fancybox-buttons.css diff --git a/fancybox/helpers/jquery.fancybox-buttons.js b/themes/landscape/source/fancybox/helpers/jquery.fancybox-buttons.js similarity index 100% rename from fancybox/helpers/jquery.fancybox-buttons.js rename to themes/landscape/source/fancybox/helpers/jquery.fancybox-buttons.js diff --git a/fancybox/helpers/jquery.fancybox-media.js b/themes/landscape/source/fancybox/helpers/jquery.fancybox-media.js similarity index 100% rename from fancybox/helpers/jquery.fancybox-media.js rename to themes/landscape/source/fancybox/helpers/jquery.fancybox-media.js diff --git a/fancybox/helpers/jquery.fancybox-thumbs.css b/themes/landscape/source/fancybox/helpers/jquery.fancybox-thumbs.css similarity index 100% rename from fancybox/helpers/jquery.fancybox-thumbs.css rename to themes/landscape/source/fancybox/helpers/jquery.fancybox-thumbs.css diff --git a/fancybox/helpers/jquery.fancybox-thumbs.js b/themes/landscape/source/fancybox/helpers/jquery.fancybox-thumbs.js similarity index 100% rename from fancybox/helpers/jquery.fancybox-thumbs.js rename to themes/landscape/source/fancybox/helpers/jquery.fancybox-thumbs.js diff --git a/fancybox/jquery.fancybox.css b/themes/landscape/source/fancybox/jquery.fancybox.css similarity index 100% rename from fancybox/jquery.fancybox.css rename to themes/landscape/source/fancybox/jquery.fancybox.css diff --git a/fancybox/jquery.fancybox.js b/themes/landscape/source/fancybox/jquery.fancybox.js similarity index 100% rename from fancybox/jquery.fancybox.js rename to themes/landscape/source/fancybox/jquery.fancybox.js diff --git a/fancybox/jquery.fancybox.pack.js b/themes/landscape/source/fancybox/jquery.fancybox.pack.js similarity index 100% rename from fancybox/jquery.fancybox.pack.js rename to themes/landscape/source/fancybox/jquery.fancybox.pack.js diff --git a/js/script.js b/themes/landscape/source/js/script.js similarity index 100% rename from js/script.js rename to themes/landscape/source/js/script.js