Es mostren els missatges amb l'etiqueta de comentaris tutorial. Mostrar tots els missatges
Es mostren els missatges amb l'etiqueta de comentaris tutorial. Mostrar tots els missatges

dimarts, 13 de setembre del 2016

The one about estimates


Other: "How long will this take?"
You: "I don't know"
Other: "It's just a button like the other"
You: "I know it looks like the other button but there's a whole different list of things to consider"
Other: "But I need costs to prioritise"
You: "I understand, and I wish I could help you. You know I won't lower quality and you don't want scope being reduced... in that case time is an open variable."
Other: "Not again with the triangle thing. You've done that a thousand times, that's jour profession".

(pause for drama)

You: "Ok. Let's play this game. I've already told you about scope-quality-time, right?"
Other: "Yes"
You: "Ok, John/Jane/MrSmith... do you have kids?"
Other: "Uuuh. Yes. How is that related?"
You: "Ok, let's imagine I asked how long will it take you to bath, feed and put to sleep tonight. I need to know so we can skype with Australia".
Other: "Uuuh... well, If we have to skype I can have my significant other to do it"
You: "Don't be a chicken. Give me the time when the kid will be bathed, belly-full and wonderfuly asleep"
Other: "9pm"
You: "exactly 9pm?"
Other: "9-ish"
You: "and bathroom is cleaned, kitchen is all cleaned up too, right? No traces of food on the wall or ceiling, right?"
Other: "Ok, maybe 9.30"
You: "9 or 9.30. You mentioned you start your routines at 7.30 so you just admitted to a 30% potential deviation."
Other: "err..."
You: "But you do this everyday. Ok, forget about tonight. Let me ask you something different. What time will your kid be at kindergarten tomorrow?"
Other: "8.30 sharp"
You: "not 8.35?"
Other: "No, 8.30 or they have shut the door"
You: "And the kid was perfectly dressed and ate everything you prepared for breakfast, right?"
Other: "Well, not always."
You: "No traces of food on the wall or ceiling, right? Bed is made and there's no breakfast utensils laying around."
Other: "We have to make it to the 8.30 deadline"
You: "But you do this every day. It's you field of expertise. Daily repetition, same tools, same team, same risks, ..."
Other: "yeahs, but kids... entropy."
You: "Daily repetition, same tools, same team, same risks, ... I don't deal with any of those simplifications. Plus I also get entropy, arguably smaller than the entropy generated by kids or toddlers, but entropy nonetheless."
Other: "Ok, but how long will this take?"



Special thanks to Roger Segura and Yorgo Saslis, their parenthood inspired this story. 
DISCLAIMER: None of them are The Other in the story, they just happened to tell personal experiences that helped building the script.



Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License.

dilluns, 18 d’agost del 2014

Baggage Collector Kata

I recently worked with a team trying to get them to grok some habits around testing. One of the most challenging tasks was to make them learn to unlearn.

They know a lot about their business and so they know all exceptions to all the rules. This leads to overcomplexifying any solution. That leads to paralysis by analysis in some cases too. Since the problem we were using to practice testing was getting in the way again and again (due to corner cases) I decided we would ignore the problem and do a kata instead.

Since I also wanted them to do a kata similar enough to what they were trying to solve I just made one up on the spot and made it so that some problems in the kata were close enough to the actual problem. 

DISCLAIMER: I know this intro is extremely vague. Just cut to the chase.

Baggage Collector Kata

Imagine you work at a baggage handling service of a regional airport in 1987. You spend your day taking bags that fall through a slide into a pile of baggage, from the pile to (a) the Hangars, (b) the baggage claim Hall or (c) Lost and Found.

Write the code so that a baggage is sent to the appropriate location:
 - a baggage has a label with a FROM and TO
 - a baggage whose FROM matches the current airport goes to the HANGARS
 - a baggage whose TO matches the current airport goes to the HALL


(Hidden rule)
 - all others go to LOST and FOUND


NOTE: The hidden rule was left out on purpose. I expected the team to notice there's cases not covered by the original set of rules and to (1) go ask the business expert (me) about what to do and (2) get a test for those cases.

Moving on to the future

While they were coding, I decided time was a variable to consider so I made it so each year would actually happen in 10 minutes. Meaning I added extra rules every 10 minutes.


Time goes by and the airport is being successful. Management is preparing some changes in order to make the airport an international hub.

Your team is merged with the Hangar's team. You now must:
 - send baggages to appropriate HANGAR depending on the TO indicated on the label. That is, you no longer send baggages to HANGAR but you send them to HANGAR-8 or HANGAR-4, etc...

Also, you may start receiving bags that are on transit!
 - a bag's label may now have several hops on the itinerary. Send bags to HALL if bag is on the last hop or send them to the appropriate hangar if current airport is on the itinerary. 
 -if the current airport is not on the itinerary, just move the bag to LOST and FOUND.


The Golden Years

The plan seems to be working and your airport is now an international airport! Kudos!
Here's some more considerations:
 - if a bag FROM your airport will go abroad, you must send it to X-Ray and then to the appropriate Hangar
 - a bag arriving (our airport is final destination) from a country abroad (original airport) then the bag must go to CUSTOMS and then to the HALL.


NOTE: At this point you can let your imagination flow and add all kinds of rules. For example:
 - merge your team with lost-and-found teams so that if you receive a baggage you can re-route (even if you are not on the itinerary), just do it.
 - make the hangar-to-flight mapping change during time. If a flight has a very early checkin, make it's baggage to the hangar even thought the plane is still not there or even if there's another plane there.
 - remove all bags belonging to JACINTO RAMIREZ (a known drug dealer on Interpol's Most Wanted List)



Head fake

I used the kata as a way to get the team to get used to a set of simple rules changing very quickly. You can only go refactor crazy if your basic functionality is granted.
The original problem was simple and easy enough to reason about so as to dig deep into the possible corner cases it presented. We got into the exception handling quick enough. The message I was trying to pass on was: a simple problem is easy to reason but will surely have unspecified corner cases get your business expert in hand, find those cases quickly. Before you start coding your cathedral, step back and think of a good set of possible inputs and the expected outputs. If you find cases you can't answer to, get back to the business expert.
Also, your business will change soon enough and probably on a direction different than what you expected. Don't try to anticipate the future and keep you design simple and uncoupled so that it accepts change easily.

dimecres, 29 de gener del 2014

Scala code in Android app

I've been meeting Dani at several meetups in Barcelona lately.

We're both interested on scala but he also investigates the options to use it in android apps.

Last Saturday I remebered reading something about scala being used in android apps and tried to restore my notes to share them with Dani. Here's what I could recover:

ALERT: The following is a druid recipee based on maven.

git clone git@github.com:jayway/maven-android-plugin.git 
git clone \git@github.com:jayway/maven-android-plugin-samples.git 
cd maven-android-plugin 
mvn clean install 
cd ../maven-android-plugin-samples/ 
cd scala/ 
sed -i '' 's/2.8.0/2.10.3/' pom.xml
mvn clean install 
adb install -r  target/scala-1.0.0-SNAPSHOT.apk

Note: the previous uses a Mac OSX flavour 'sed' that requires an extra pair of simple quotes.

dissabte, 25 de gener del 2014

DI and testing without mocking in Play!

At the scala developers barcelona meetup we are running monthly challenges. This month's challenge is to implement a REST API. Easy peasy.

At some point, akustik had a question about his approach in Play! and so asked for opinions on the mailing list. Turns out I had been over the same problems few weeks earlier at my (then) current project so offered to help. Since the answer is quite reusable I'm cross-posting here so that you can comment and discuss if my suggestion is actually correct. I worked may way into the solution to make sure I remembered all steps and the PR'd my code into the challenges github: https://github.com/scala-developers-bcn/challenges/pull/29

DISCLAIMER: I'm quite new to Play! and it's very possible there's a better way than what I suggest.




Dependency Injection and testing without Mocking in Play!

Play!'s Main Problem (TM) is the abuse of singleton objects for everything. While it's a great idea to create a single instance of anything and reuse it again and again it's also a problem to make everything object (scala equivalent for classes that contain only static methods). This object-based approach makes it difficult to inject instances at will.

Back to the trigger, here's akustik's question:
I wanted to change [the controller] implementation for testing purposes without modifying the code nor changing to a variable and adding a setter. 
To which I could only reply:
You hit one of the big problems of Play! testing: there's no easy
recommendation for unit testing. The path of objects and Integration
Testing is the default suggestion. Sad.
And finally found few minutes to properly solve the problem.



Dependency Injection

To escape the object singleton mess which forbids you from doing plain old DI you first have to convert you controllers from objects to classes.

(I'm not 100% of what I'll explain now)
But once you did that you have to prepend your routes mappings with an '@' sign. That causes you HTTP requests to request an instance to Play! The responsible to resolve the required instance is a thing** called GlobalSettings.
(back to 100% sure)

Then you have to create your subclass of play.GlobalSettings and then go to application.conf and specify what your Global class is. But you class controllers still use singleton repositories. Let's make the repos injectable. Add a constructor parameter to your controller:


  class MyCtlr(repo:MyRepo) extends Controller

And now you made your controller unavailable because Play can't resolve the incoming requests. Here's where you go back to your Global class and override the method getControllerInstance so that any incoming request lets you decide what Controller to use. And there you have it!

Your Global soon becomes this:


  def flightRepo = new InMemFlightsRepository
  def flightCtlr = new FlightsCtlr(flightRepo)

  override def getControllerInstance[A](controllerClass: Class[A]): A = {
   // extend here when adding more controllers... probably a pattern matching.
    flightCtlr.asInstanceOf[A]
  }


So far so good. We got ourselves so DI wonders.



(trolling mode on)
It's at this point where your Global should be renamed ApplicationContext and you should consider your:

  def flightCtlr = new FlightsCtlr(flightRepo)

a synonim of: (blogspot won't let me use lt and gt)

  bean id=flightCtlr class=controller.FlightsCtlr scope=request
     constructor-arg="flightRepo"

You could have also done:

  val flightCtlr = ...

which would be:

  bean id=... class=... scope=singleton


Scala 1 - XML fuck you!... I mean 0

(trolling mode off)




Unit Testing (without mocking)

And so we end up on our wonderful test. 
If you've read the Play docs you probably have a test that looks like this:

    "return 200 on flights/" in {
      running(FakeApplication()) {
        val flights = route(FakeRequest(GET, "/flights")).get
         ...

Well, without you knowing it uses your Global class. The final trick to easily inject your Mock classes is to tell your Fake Application to use a different Global:
      running(FakeApplication(  withGlobal= Some(MockingGlobal() ) )) {

And that's it!
Well, no!
Oh God this is horrible!
** Remember when I said "a thing** called GlobalSettings" , well by thing I mean some bytecode that may be class or trait and I was being vague on purpose because there's both a class and a trait called
GlobalSettings and you'll need to use both to achieve DI and testing. To distinguish which GlobalSettings you are using pay close attention at the package.

  • To override Global for you app extend play.GlobalSettings
  • To inject a MockGlobal into FakeApplication when testing mixing play.api.GlobalSettigns
Putting it all together

So, to make your testing work you have to provide a MockingGlobal of yours which must mix-in play.api.GlobalSettings. I chose reusing my original Global class and just override a faked repository.

Again, while the running environment requires you to provide a subclass of play.GlobalSettings, FakeApplications expects an Option[play.api.GlobalSettings] (which is the trait). (Mother of Mercy this is wrong!)

I just go and extend and mixin to keep all my DI in a single place:

  class MyGlobal extends play.GlobalSettings
                   with play.api.GlobalSettings { ...

and finally:

  val globalForTest = new Global {
    override def flightRepo = new InMemFlightsRepository {
      override def loadAll(): List[Flight] = 

          List(Flight("ON_TIME","BOS", "CHG", "19B"))
    }
  }



Conclusion

Now, after this terrible mess lets puts this in context (no pun intended) and compare this with the 'easy' setup of a Spring-MVC app with testing and everything.
It's not much simpler but it's 100% scala so it must be cooler.

dimecres, 8 de gener del 2014

More on Neo4J 2.x: Querying over labels


I'm just starting with Neo4J and got straight into v2.0 so my first models already use labels. I just got into a case where I expected labels be of help but not sure if what I want to do is possible. Let me explain:

THE DOMAIN

Let's imagine we modelled a food chain: (in pseudo-cypher)

   (grass:Vegetable)-[:EATS]-(marie:Cow)
   (marie)-[:EATS]-(ferocious:Velociraptor)
   (marie)-[:EATS]-(joseph:Human)


In this example, 'josep' and 'ferocious' shared a great meal of 'marie'. Meanwhile poor Marie the Cow had had a last meal of grass.

DETECTING CANNIBALISM

Now, Imagine I wanted to locate cannibalism relations in my graph. It's ok for any species to eat other species but it's unacceptable to eat those of your same species. So I want to locate nodes labelled X eating other nodes also labelled X. Put another way, I'd like to write predicates over the label. So far the only option I found was replicating the label information into a property.

Turns out the solution was right in my face... that is, in the docs.

   MATCH (n)--(p)
     WHERE labels(n) = labels(p)
     RETURN n, p


Now, this works because I only added a label to each of my nodes but if I were to use labels aggressively this query would not solve my cannibalism detection.

divendres, 27 de desembre del 2013

Neo4j 2.0 - Indexing

NOTE: this post is the unexpected continuation to my yesterday's post on Neo4J. You might want to start there.

DISCLAIMER: I'm no expert on the technology and this is more of personal notes while I keep playing around and learning.



INDEXING

So I went on with Alberto's workshop slides to learn Neo4J to refresh my memory on the features until I reached a certain slide which used indexing:

     START tom=node:node_auto_index(name="Tom Hanks")  
     MATCH (tom)-[:ACTED_IN]->()<- director="" span="">
     RETURN director.name;

I then tried to execute it and found a nasty error message:
   Index `node_auto_index` does not exist 
It is clear what the problem is: the index is missing. But considering it's the auto_index I was trying to use I assume there's no more indexing magic in Neo4J. I then started a pursuit to create an index so that I could reproduce what I had in Neo4J 1.9.x. I mean, it was clear to me now that if I wanted indexing over actors-name I would have to create it myself. So I started digging google to learn some more about indexing in 2.0.0.


Creating and using Indexes

First hit I checked on Indexing is a great webinar by Michael Hunger on new features in Neo4J. For what I could gather (in the matter of indexing) is that the main difference is that they are now truly indexes meaning once created they auto-magically maintained when data is updated/added. I deduce from that statement that this wasn't the case in previous versions. BTW, the index is maintained transactionally, the index is bound to the data transactionally.

So, to create an index you simply need to:

     CREATE INDEX ON :Actor(name)

This approach really simplifies the queries so that my original Cypher query becomes:

     MATCH (actor:Actor)-[:ACTED_IN]->()<- director="" span="">
     WHERE actor.name="Tom Hanks"
     RETURN director.name;

which is simpler and also more aligned to what a SQL-John might expect. What happens under the covers is that Neo4J detects I'm filtering by a field (name) over a labelled node (actor:Actor) and then finds out there's an index by ':Actor(name)'. So, it goes and automagically tries to use it.




But it is flawed

Turns out when I tried try to create the index using:

     CREATE INDEX ON :Actor(name)

it worked at indexing nothing because my dataset doesn't use labels. So I then tried to index anything by name:

     CREATE INDEX ON :(name)
     CREATE INDEX ON :*(name)
     CREATE INDEX ON (name)

it was a total waste of time since indexing requires labels in Neo4J 2.0.x. (insert sadface here). 




INTRODUCING LABELS!

Then, back on my quest to query using an index I noticed my only chance was to create a label and have all nodes that [:ACTED_IN] another node to be labelled as Actor. Turns out to be quite straight forward:

      MATCH (actor)-[:ACTED_IN]->(movie)
      SET actor :Actor
      RETURN actor;

This finally created my label, which unblocked my power to created indexes which allowed to query using them.



FUTURE WORK

Some doubts I need to investigate further:
  • The video mentions there's "no unique indexing yet" but the video is few months old now and is based on Neo4J 2.0.0-M0
  • there's also a mention to 'simple lookups for now' and I wonder what that might mean.
  • While reading the docs on indexing I noticed it is possible to force the usage of a given index when querying (which is wonderful and also expected by some SQL-John's).
  • I read s/where it's possible to alter the indexing technology. That's definitely worth a look at.

dijous, 26 de desembre del 2013

Neo4j 2.0 - Setup and first impressions

During the Christmas Holidays I took some time to play around with Neo4J. This is not the first time I tinker with it but definitely the first time I do it unsupervised. I must say the first time I played around with Neo4J it was under @albertoperdomo 's guidance and it felt like a liberation after several years of RDBMS.

DISCLAIMER: I'm a total newbie at Neo4j so don't take my advice for anything I'll be writing, this post is more of a compendium of notes for myself to check in the future.

INSTALLING

Installing any version (I needed 2.0.0) of Neo4J is insultingly simple thanks to @thedevel script: ndm. I even tweeted about it (again, for my own reference). Let me point out that even manually, isntalling neo4j is really simple.

IMPORTING MOVIE DATABASE

During Alberto's workshop intro to Neo4J we had a lot of small quizes so that each would have to keep on investigating and putting small concepts into practice. My idea to start playing around with Neo4J was to load that clean movie database, refresh some concepts from the notes I took during the workshop and then try to move on from that point.

First issue I faced with new features in Neo4J 2.0.0 were small syntax changes causing the load of a 1.9.3 database to fail. It's ended up being something quite silly though. What used to be:

     START n=node(*) MATCH (n)-[r?]-() DELETE r,n;

now turned into:

     START n=node(*) 
     MATCH (n)--() 
     OPTIONAL MATCH ()-[r]-() 
     DELETE r,n

There seems to be 2 differences:

  • the trailing semicolon seems to be unnecessary now. It was causing a parsing error when reading the following line which caused the error message to be miss-leading since it pointed me in the wrong direction. I finally noticed the error message made no sense and tried to remove the semi-colon. It worked.
  • Second thing is the replacement of '?' char to mark 'r' relationship optional in the query. Optional matcher's syntax seems to be new (or restricted to): OPTIONAL MATCH. I then replaced the edge from the query MATCHer and created an OPTIONAL MATCHer for it. It worked but I really doubt the two queries (old 1.9.x vs new 2.0.x) do the exact same thing. What I intended was to delete everything and that's what happens, but that's not enough proof to be satisfied with the rewrite.
(I'm not sure I can freely distribute the movies.cyp database) :-(

FIRST IMPRESSIONS


  1. The web console has improved incredibly. It was a great tool already but it is now beyond awesome. You can judge yourself:
    1. not only the tabular data presentation provides a clearer view of the schemaless data,


    2. you can now peek at the results in graph view


I'm only scratching the surface of Graph DB concept at the moment. I hope I can get my hands dirty in the upcoming days...

divendres, 20 de setembre del 2013

Parallel collection manipulation in scala

Scala collections API comes packed with a very cool feature which is parallelizing any processing. See this example:

I first create a list (I could use a range or s/thing else too):

scala> List(1,2,3,4,5,6,7,8,9)
res0: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)

... and then build the skeleton of my processing. What I want to to multiply each value by 1000 and then divide each value by 500:

scala> res0.map{ 
    i => i*1000
  }.map{
    i => i/500
  } 
res1: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18)

Nothing fancy so far.

Entering par

In scala every collection can be automagically wrapped into a counterpart that implements processing with a thread pool. I actually have no clue what the implementation is. Damn! I'll have to look it up. Anyway, insert 'par.' on your code and...

scala> res0.par.map{ 
    i => i*1000
  }.par.map{
    i => i/500
  }
res2: scala.collection.parallel.immutable.ParSeq[Int] = ParVector(2, 4, 6, 8, 10, 12, 14, 16, 18)


... the list becomes a ParVector and keeps all items sorted in the original position.
Let's try and see it in action: (added random sleep to 'help' context switching)

scala> import java.util.concurrent.TimeUnitimport java.util.concurrent.TimeUnit

scala> import java.util.Random
import java.util.Random

scala> new Random

res6: java.util.Random = java.util.Random@b9d964d

scala> res0.par.map { 
    i => TimeUnit.MILLISECONDS.sleep(res6.nextInt(1000));
    println(i);
    i*1000
  }.par.map{
    i => TimeUnit.MILLISECONDS.sleep(res6.nextInt(1000));
    println(i); 
    i/500
  } 
3
7
4
5
8
2
1
9
6
7000
3000
5000
4000
1000
6000
8000
9000
2000
res13: scala.collection.parallel.immutable.ParSeq[Int] = ParVector(2, 4, 6, 8, 10, 12, 14, 16, 18)

scala> 

Ta dah! Execution is run in parallel.

See more information re Parallel Collections on the overviews of the Scala Docs.

PS: For the curious...

If I get rid of the first 'par', the first processing is sequential, and the delays add up.


scala> res0.map { i => TimeUnit.MILLISECONDS.sleep( res6.nextInt(1000)  );println(i) ;i*1000}.par . map { i => TimeUnit.MILLISECONDS.sleep(  res6.nextInt(1000)  ); println(i); i/500 } 
1
2
3
4
5
6
7
8
9
5000
2000
1000
6000
3000
4000
9000
7000
8000
res17: scala.collection.parallel.immutable.ParSeq[Int] = ParVector(2, 4, 6, 8, 10, 12, 14, 16, 18)

divendres, 18 de febrer del 2011

Convertir en massa

Per a convertir un munt de fitxers OGG en MP3 (amb fitxers que tenen espais en blanc al nom, caràcters extranys) i canviant l'extensió:


for x in *.ogg; do ffmpeg -i "$x" "`basename "$x" .ogg`.mp3"; done


Extret de: http://www.brighthub.com/computing/linux/articles/40293.aspx#ixzz1ELDmbclb

dijous, 7 de gener del 2010

Practicant amb OCR

Avís!!! Article molt técnic d'interés nul per a no freaks dels ordinadors.

Feia temps tenia pendent el fer algunes d'OCR (reconeixement automàtic de caràcters). Finalment avui m'hi he posat una horeta i ja tinc un parell d'opcions que funcionen força bé al meu Ubuntu 9.04.

Aquí les teniu:
#/bin/bash

INPUT=lala.tif
MID1=out.ppm
MID2=bw.ppm
OUTPUT=result
MID3=$OUTPUT.tif

rm $MID1 $MID2 $MID3 $OUTPUT.txt

## OPTION 1: tesseract
anytopnm < $INPUT > $MID1 ; pnmdepth 1 $MID1 > $MID2 ; pnmtotiff < $MID2 > $MID3 ; tesseract $MID3 $OUTPUT -l eng ; cat $OUTPUT.txt

## OPTION 2: gocr
tifftopnm $INPUT | gocr -v 0 -m 0 -e - -f UTF8 -

dimecres, 4 de novembre del 2009

Sacades

(post ràpid bastant mal escrit, sobre un tema que m'ha generat discusions en el passat)

Les sacades són micromoviments de l'ull.
Sembla que l'ull humà (i també de molts altres animals) no s'està completament quiet sino que va fent uns petits moviments que recorren tot el que veiem. Així, "mirant aquí i allà" l'ull (i el cervell) es fan una idea de l'escena on som.

En general, l'ull (animal, en general) detecta millor el moviment com a mecanisme de defensa, prevenint així els atacs. Penseu en un felí acostant-se a poc a poc cap a una presa.

Les sacades també ens permeten veure les coses que no es mouen. Per a un ull humà sense sacades, un objecte immòvil acaba desapareixent. Tot plegat, és un mecanisme de defensa ja que facilita detectar els objectes que es mouen.

(avís de possible llegenda urbana)
Tinc entés que l'ull de les abelles no disposa de sacades. Així doncs, quan ens ataca una abella estar-se quiet és una manera de tornar-se invisble per a elles.

Més info: wikipedia (es), wikipedia (en)

divendres, 25 de setembre del 2009

Cadenes de mails i SPAM. Cas pràctic

Llegeixo a TICnovations un article que em facilita enormement la feina de com explicar perquè no vull rebre cadenes de mails amb power-points, totems de la suerte o convocatòries per a tancar els llums.

Cadenas de correo y spam. Caso práctico.

dissabte, 19 de setembre del 2009

Dropbox

(aquest article és una versió per a TOTS ELS PÚBLICS del meu article anterior Control de versions)

Si teniu un conjunt de documents o dades al vostre ordinador que NO voleu perdre, sempre heu d'estar pendents de fes còpies de seguretat, no?
I si esteu treballant en un document, potser teniu 14 versions diferents del mateix document al vostre disc dur.

Bé, DropBox és un servei d'internet que resol aquests dos problemes i d'altres com ara:
- si teniu 2 llocs de treball (un portàtil i un ordinador de sobretaula) i necessiteu disposar dels mateixos documents en tots ordinadors. Segur que esteu farts de passar coses amb pendrive's o per e-mail.

Dropbox és un servei que sincronitzarà una carpeta del vostre ordinador de fins a 2 Gb de capacitat (més capacitat en versions de pagament) amb un servidor remot. Això vol dir que si conecteu 3 ordinadors amb aquest servidor remot tots 3 ordinadors tindran els mateixos documents.
Pel mateix preu, dropbox recordarà TOTES les versions d'un document que hi tingueu desat, i fins i tot recordarà TOTS els fitxers que heu esborrat i que havieu desat a dropbox en algun moment del passat.

Més coses

Dropbox té 4 tipus de carpetes:
- privades: només el propietari hi té accés
- públiques: hi pot accedir qualsevol a través d'un navegador. Per exemple: http://dl.getdropbox.com/u/1519979/exemple.txt
- fotos: carpetes que només contenen fotografies. Són públiques i es presenten com a fotografies quan les mirem per la web. Com les meves fotos d'Eslovènia.
- compartides: carpetes que podem sincronitzar amb d'altres usuaris de dropbox.

Eines per a fer webs (Firefox only)

Firebug (getfirebug.com)

HttpFox (https://addons.mozilla.org/en-US/firefox/addon/6647)

YSlow (http://developer.yahoo.com/yslow/)

PageSpeed (http://code.google.com/speed/page-speed/)

dilluns, 7 d’abril del 2008

Control de versions

(avís, article per a informàtics/freaks, no és per a gent normal)

Avui he decidit parlar del control de versions perquè coding horror presenta un petit tutorial per a instal.lar i usar un dels SCM (Source Control Manager) més extesos i senzills que hi ha (subversion). Una de les coses que m'agrada d'aquest tutorial és que presenta com fer-ho en un entorn Windows. És a dir, no assumeix cap mena de coneixements per part de l'usuari.

Deixant de banda la meva opinió, també l'he volgut destacar perquè recentment estic usant SubVersion cop a sistema de backups. Tinc un parell d'usuaris d'ofimàtica a qui he instal.lat un TortoiseSVN i els he indicat que TOTA la feina ha d'anar en una carpeta determinada. Finalment, els he dit que abans de tancar el PC han d'aconseguir que totes les fletxetes estiguin de color verd**. Alguns avantatges que presenta aquesta estructura són poder tenir còpies de la informació distribuïdes geogràficament (per evitar incendis), accessibles des de qualsevol PC, a part dels avantatges de tenir la informació versionada, etc...

En definitiva, si mai heu usat un sistema de control de versions (o SCM) el tutorial de CODING HORROR) és un molt bon punt de partida.

** Podeu veure alguna captura de pantalla al tutorial de CODING HORROR a què faig referència al principi si no sabeu què són aquestes fletxetes de color verd.

dimarts, 15 de gener del 2008

Llistes de correu

Al meu post anterior, parlava de la dificultat de mantenir una agenda d'adresses de correu electronic. Sovint ens trobem en el cas de pertanyer a un colectiu, o de compartir un interes amb d'altra gent, o be de voler mantenir el contacte amb un grup de persones. En aquesta situacio el problema que descrivia es multiplica i esdeve impossible de manegar.

Una opcio per a evitar maldecaps son les llistes de correu electronic.

Un dels principals avantatges de les llistes de correu es que cada persona es la responsable de subscriure l'adressa de correu a la qual vol rebre els missatges. Un altre avantatge es que cada persona subscrita a la llista nomes ha de recordar una adressa de correu.

Possiblement, a la feina disposeu de llistes de correu i les useu, pero potser no sabieu que tambe en podeu crear una vosaltres mateixos. Dues alternatives molt populars i gratuites son el Yahoo Groups i tambe el Google Groups. El primer l'he usat durant molts anys i ara mateix seria la meva recomanacio perque disposa d'algunes funcionalitats que el Google Groups encara no te, per exemple: la possibilitat de crear enquestes entre els usuaris del grup o be de crear base de dades.

Creant una llista de correu

Una de les coses que us cal saber en crear una llista es que (en el 99% del casos) podeu configurarla per a que nomes els qui hi estan subscrits hi puguin enviar missatges. Aquesta opcio, apart de la paranoia, te l'enorme avantatge de filtrar mooolt SPAM. Hi ha d'altrs opcions habituals, pero ara tampoc no venen al cas.

dissabte, 21 d’abril del 2007

RSS (I)

Incís tècnic
(Atenció, aquest article/tutorial només és útil si useu un navegador Firefox o bé l'Internet Explorer 7. Si useu Internet Explorer, qualsevol versió, us recomano Firefox. No he usat mai ni Opera ni Safari)
NOTA: el logo de RSS està generat a partir del SVG de la Wikipèdia.
Fi incís tècnic


Des de fa temps, existeixen planes web que sindiquen el seu contingut. Això facilita a l'usuari el saber quan hi ha un contingut nou. De tota aquesta tecnologia se'n diu RSS.

Principalment, naveguem per internet per (1) a buscar informació puntual, (2) compartir els nostres interessos o bé (3) llegir sobre actualitat. Per al segon i al tercer cas, acostuma a passar que anem moltes vegades a les mateixs planes web per a mirar quins informació ha aparescut nova, quins són els últims titulars del dia, etc. Per a aquests casos, els servidors acostumen a publicar la informació en HTML (plana web normal i corrent) o bé en RSS (un format diferent). A continuació us ensenyo la diferència entre els dos formats:
el blog en format HTML (el que veieu habitualment)
el blog en format RSS (tal i com el mostra Firefox 2)

La primera diferència està en què l'HTML mostra el contingut (la informació) amb un conjunt d'estils que fan que tot sigui més maco per a
la vista. Mentre que l'RSS només mostra la informació tal i com és i en alguns casos, només es mostra un petit resum.
La segona diferència la podem veure en la manera d'afegir la plana als nostres bookmarks o favoritos (no sé com se'n diu en català). Quan accedim a una plana que publica el contingut en RSS apareix una petita icona taronja a la barra de navegació (en versions antigus d'Internet Explorer no, poseu-vos Firefox!) com la que us mostro a la dreta:
Podem afegir un favorito en HTML usant el menú de bookmarks.
O bé podem usar la icona de RSS per a tenir un favorito dinàmic.

Si cliquem en aquest quadrat taronja, se'ns afegirà una entrada als bookmarks que durà per nom "el nom del meu blog" però que serà desplegable:Un bookmark dinàmic gràcies a RSS.

La gràcia del bookmark que us mostro en l'anterior captura de pantalla és que cada vegada que jo afegixi infomació al blog, el desplegable mostrarà els nous articles que s'hagin afegit al meu blog!. En el meu cas, com que el blog canvia poc, no és massa important, però si sou fans de llegir els titulars de premsa de digital podeu subscriure-us als RSS de La Vanguardia o de El Pais (ElMundo.es NO té RSS, per assumptes de publicitat...).

De tota aquesta història dels RSS també se'n diu sindicació.
De moment, aquesta és la introducció als RSS
. En un proper article us explicaré què són els lectors de RSS.




dijous, 29 de març del 2007

Torrent (III)

A Torrent(I) i a Torrent(II) vaig explicar què són i com s'usen els torrents. Amb això ja ho teniu tot, però per tal que els torrents afavoreixin a tothom, cal que tots els usem bé.

Bones maneres
Per aconseguir que els torrents siguin útils, cal que sempre hi hagi algú compartint el fitxerInteressant. És a dir, no n'hi ha prou amb descarregar-lo al meu ordinador i oblidar-me del problema, cal que EXPLÍCITAMENT comparteixi el meu fitxer.

Seguir compartint
Estarem compartint un fitxerInteressant només si tenim el gestor de torrents encès. Tal i com explicava a Torrent(I), per tal de descarregar un torrent fa falta un pograma especial igual que per descarregar un mail també fa falta un programa especial. Doncs per tal de compartir (encara que ja el tinguem descarregat del tot) cal que mantinguem aquest programa encès.

I la pregunta és, i perquè cal seguir compartint? Per no deixar mai a ningú a mitja descàrrega. De la mateixa manera que jo he tret profit del fet que molta gent comprtaia un fitxerInteressant, cal que els qui venen al darrere també en treguin profit de les nostres aportacions (upload).

El Dilema
Aleshores, he de compartir un fitxer 'por siempre jamás'?
Aquest és el dilema... depèn de cadascú decidir quan deixa de compartir un fitxerInteressant per passar a compartir-ne un altre. El que és rellevant és que:
"com més fitxerInteressants comparteixis al mateix temps, més lentes seran les descàrregues/aportacions."
Un bon criteri per a decidir quan puc deixar de compartir un fitxerInteressant és mirà el teu %compartició (o shareRatio)


Torrent (II)

A Torrent (I) vaig començar a parlar dels torrents i d'un programa per a usar-los, però com s'usen els torrents???

Incís
En aquest article parlaré de 2 fitxers: (1) el que vull aconseguir i (2) un altre. Usaré els noms (1) fitxerInteressant i (2) fitxerDInstruccions.

Com usar un torrent
Seguint amb les analogies, si per a veure la plana web del Toni necessites la seva adreça (que és http://toninomyss.blogspot.com/), per a poder baixar el fitxerInteressant a través de torrents cal que formis part del conjunt de persones que comparteixen interès pel fitxer. Per a que tothom estigui d'acord, quan es publica un fitxerInteressant, es crea un fitxerDInstruccions, que és moooolt petit i SEMPRE diferent.
Aleshores, cal que donem el fitxerDInstruccions al nostre pograma gestor de torrents i esperar.
Els fitxersDInstruccions acostumen a tenir l'extensió .torrent. És a dir, si els Word tenen extensió .doc, els excels .xls i les fotos .jpg o .gif... els torrent usen .torrent .