Log4J Patch: SSL support for Gmail Notifications

PragDave@github

Objective-J

cappuccino1Ici commence notre voyage dans le monde merveilleux de Cappuccino.

Dans ce premier article, nous allons nous contenter de jouer avec Objective-J en dehors de toute application Web. En effet, même si Cappuccino est un framework Web, il n’en reste pas moins qu’il repose sur le langage Objective-J que nous pouvons utiliser sans forcement créer une application Web.

Objective-J, comme le laisse penser son nom, est une réécriture d’Objective-C pour Cappuccino qui est lui même un portage de Cocoa. Si nous faisons un parallèle, nous pouvons grossièrement dire que Cocoa repose sur Objective-C qui est une extension du C, alors que Cappuccino repose sur Objective-J qui est lui-même une extension de JavaScript. Nous n’allons pas entrer dans les détails de l’architecture de Cappuccino, mais sachant qu’il s’agit au final de JavaScript, vous allez voir que nous pouvons en utiliser toute la puissance, tout comme nous utilisons le C avec Objective-C.

Installation et test de Cappuccino

L’installation de Cappuccino est on ne peut plus simple. Commencez par télécharger les frameworks et outils. Inutile de prendre le Starter Package. Vous pouvez y jeter un oeil, mais en l’occurrence cet archive contient la documentation et le template d’application généré par défaut. Toutes choses que nous avons par ailleurs dans l’archive des frameworks et outils.

Une fois l’archive CappuccinoTools-X.X.zip téléchargée, décompressez là et utilisez votre shell préféré pour exécuter le script install-tools. Attention ce script doit être exécuté sous le compte root.

Par défaut, les outils et frameworks de Cappuccino sont installés dans /usr/local. Vous pouvez changer ce répertoire d’installation en utilisant l’option –prefix du script d’installation.

Une fois l’installation terminée, il vous sera demandé

  • de vérifier que votre variable d’environnement PATH contient bien le chemin /usr/local/bin,
  • de créer la variable d’environnement OBJJ_HOME en lui attribuant la valeur /usr/local/share/objj,
  • de créer la variable d’environnement STEAM_BUILD avec comme valeur $HOME/objj_build.

Cappuccino étant installé, nous pouvons tester. Pour cela, nous allons créer une application Web1. Dans les faits nous allons utiliser l’outil steam qui permet de générer un template d’application.

$ steam create test
Setting up Rhino utilties
...
$

Une fois ceci fait, vous devez vous retrouver avec un répertoire test contenant les fichiers et répertoires suivants :
AppController.j
Frameworks/
Info.plist
index-debug.html
index.html
main.j

Ouvrez le fichier index.html avec votre navigateur préféré. Si après une courte attente vous voyez apparaître le message Hello World c’est que tout va bien !

Objective-J

Si vous avez déjà utilisé Objective-C, ce qui va suivre ne devrait pas vous poser de problème… Si ce n’est pas le cas, rien de grave. J’hésite cependant à vous dire que vos éventuelles connaissances en C, JavaScript ou développement objet devraient vous aider. Particulièrement en ce qui concerne l’objet, je ne ferais aucune explication, considérant que les notions qui s’y rapportent vous sont familières.

Les types

Objective-J ce veut être un langage typé (tout comme Objective-C). Ceci implique normalement que tous les identifiants possèdent un type. Donc, si nous voulons déclarer une variable, nous utiliserons la notation suivante :

id monObject;

id représente ici le type. En fait id est un peu particulier dans le sens où il définit un type quelconque. Nous pouvons donc être plus précis en utilisant une des notations suivantes :

CPString maChaine;
int monEntier;

La différence entre les deux approches fait que dans le second cas, nous avons un typage statique. En effet, déclaré avec id, un identifiant peut faire référence à n’importe quel type d’objet. Ce n’est pas le cas si nous utilisons CPString, int ou tout autre type.

Dans la réalité, tout ce que je viens de dire est faut ! En effet, le typage statique n’est que cosmétique dans Objective-J. Souvenez-vous que nous somme au dessus de JavaScript. Or ce dernier est faiblement typé et Objective-J hérite de cette caractéristique.

Les messages

Pour demander à un objet d’exécuter une méthode, il faut lui envoyer un message2. Ceci se fait en utilisant une expression entre crochets :

[monObject monMessage];

En Objective-J, le message correspond toujours à un nom de méthode de l’objet récepteur. Si vous commencez à trouver un côté Lisp-ien à Objective-J, rassurez vous, vous allez voir que cette notation entraine d’énormes avantages. Pour le voir, regardons comment passer des paramètres. Dans un langage plus classique, vous avez l’habitude de passer des paramètres en utilisant la notation suivante :

monObjet.maMethode( parametre )

En Objective-J nous ferons le même appel de la façon suivante :

[monObjet monMessage:parametre];

Et alors me direz-vous ? Et alors allons plus loin. Imaginez que nous ayons une méthode prenant plusieurs paramètres. Prenons comme exemple le cas où nous avons une méthode permettant de passer le nom et le prénom d’un utilisateur et faisons en sorte que le nom de notre méthode soit parlant. Nous avons l’habitude de faire cela :

user.addFirstNameAndLastName( firstName, lastName )

En Objective-J nous ferons cela :

[user addFirstName:firstName andLastName:lastName];

N’est-ce pas beaucoup plus explicite (et naturel — finalement) ?

Bien entendu, il est possible d’enchaîner les messages. Pour cela il faut imbriquer les expressions entre crochets :

[[monObject monPremierMessage] monSecondMessage];

Cette notation est équivalente à l’écriture :

id monAutreObjet = [monObjet monPremierMessage];
[monAutreObjet monSecondMessage];

Les méthodes

Maintenant que nous savons utiliser les messages, voyons comment créer des méthodes. Si vous avez compris l’intérêt de la notation crochet pour le passage de paramètres, vous pouvez facilement deviner comment nous allons définir des méthodes :

- (void)doSomething
{
  // Cette méthode ne prend aucun paramètre en ne retourne rien (void)
}
 
- (int)age:(id)dateDeNaissance
{
  // Cette méthode prend en paramètre un objet et renvoie un entier
}
 
- (id)areaWithWidth:(int)width andHeight:(int)height
{
  // Cette méthode prend deux paramètres entiers et renvoie un objet
}

Tout cela est assez logique. La question qui se pose est le pourquoi du signe moins (-) en début de déclaration. En fait, il s’agit simplement de préciser que nous avons affaire à une méthode d’instance. Si nous voulons déclarer une méthode de classe, nous remplaçons ce moins par un signe plus (+).

+ (id)initWithWidth:(int)width andHeight:(int)height
{
  // ...
}

Les classes

Dans la majorité des langages objets compilés, nous avons l’habitude de distinguer la définition de l’interface d’une classe et son implémentation. En Objective-C par exemple, nous utilisons les mots clés @interface et @implement pour faire ces deux choses. Objective-J n’étant pas langage compilé3, la définition de l’interface ne sert à rien si l’implémentation ne lui est pas accolée.

La définition d’une classe Objective-J commence donc pas @implement, suivi du nom de la classe et de la classe dont elle hérite. S’en suivent les variables d’instance déclarées entre accolades. Viennent ensuite les définitions des méthodes. Puis nous terminons par @end. Voici un exemple :

@import <Foundation/CPObject.j>
 
@implementation User : CPObject
{
  CPString nom;
  CPString prenom;
}
 
- (id)init
{
  self = [super init];
 
  return self;
}
 
- (void)setFirstName:(CPString)n
{
  prenom = n;
}
 
- (void)setLastName:(CPString)n
{
  nom = n;
}
 
- (void)hello
{
  print( "Bonjour "+prenom+" "+nom );
}
@end

Comme vous pouvez le voir, nous avons ajouté avant la définition de la classe User, via le mot clé @import, l’import du fichier Foundation/CPObject.j déclarant la classe CPObject. Ceci est obligatoire, car la classe User hérite de la classe CPObject définie dans Foundation/CPObject.j.

Pour tester notre classe, placez dans un fichier (User.j) le code précédent et créez un nouveau fichier (test.j) contenant le code suivant :

@import "User.j"
 
u = [[User alloc] init];
[u setFirstName:@"Grégoire"];
[u setLastName:@"Lejeune"];
[u hello];

Nous allons maintenant utiliser l’outil objj qui permet d’exécuter, via la ligne de commande, un script Objective-J :

$ objj test.j
Setting up Rhino utilties
Bonjour Grégoire Lejeune
$

Dans le fichier test.j vous remarquerez que nous n’avons pas typé u. En fait Cappuccino ne nous permet pas de le faire et écrire

User u = [[User alloc] init];

ou

id u = [[User alloc] init];

entraînerait une erreur. La seule solution, si vraiment vous souhaitez indiquer que vous avez affaire à une variable, c’est d’utiliser la notation :

var u = [[User alloc] init];

Vous ne pouvez typer que les variables d’instances lors de la déclaration d’une classe.

Constructeur

Revenons sur la création d’un objet User. Dans l’exemple nous avons utilisé la ligne suivant :

u = [[User alloc] init];

En fait, la création d’un objet à partir d’une classe se fait en deux temps. D’abord l’allocation (via alloc) puis l’initialisation (via init). L’allocation n’a pas vraiment de sens ici. En effet, il s’agit avant tout de copier Objective-C qui implique qu’un objet est en réalité un pointeur. En effet, si nous avions été en Objective-C nous aurions dû faire :

User *u = [[User alloc] init];

Avec Objective-J, l’allocation ne sert qu’à copier Objective-C. C’est l’initialisation qui est importante. Sachez que nous pouvons définir plusieurs méthodes d’initialisation. En voici une par exemple qui pourrait être intéressante :

- (id)initWithFirstName:(CPString)fn andLastName:(CPString)ln
{
  self = [super init];
 
  prenom = fn;
  nom = ln;
 
  return self
}

Nous somme maintenant capable de créer un objet User comme ceci :

u = [[User alloc] initWithFirstName:@"Muriel" andLastName:@"Oger"];

Ayant cela, nous pouvons simplifier la méthode init définie initialement :

- (id)init
{
  return [self initWithFirstName:@"" andLastName:@""];
}

Ce que nous venons de faire est plus sain dans le sens ou nous sommes certains que, quelle que soit la méthode d’initialisation utilisée, nom et prenom seront affectés.

Pour terminer ce point, mettons en place une méthode de classe :

+ (id)newUserWithFirstName:(CPString)fn andLastName:(CPString)ln
{
  return [[self alloc] initWithFirstName:fn andLastName:ln];
}

Avec cette méthode, nous pouvez éviter l’utilisation du alloc en créant un nouvel utilisateur de la façon suivante :

u = [User newUserWithFirstName:@"Maïa" andLastName:@"Oger--Lejeune"];

Fundation et AppKit

Lorsque nous avons fait l’import d’CPObject.j nous avons utilisé le chemin Fundation. En fait, Cappuccino reprend tellement bien Cocoa, qu’il en copie l’architecture. En effet, il existe deux framework :

  • Fundation qui regroupe l’ensemble des objets qui ne contenant aucun composant d’interface, soit les classes CPArray, CPDictionary, CPNull, CPData, CPDate, CPNumber, CPString et CPObject
  • AppKit qui regroupe tout ce qui nous permettra de mettre en place des interfaces graphiques.

Vous trouverez la description complète de ces classes dans la documentation de Cappuccino.

La prochaine fois nous commencerons à jouer avec AppKit et nous créerons une application Web.

1 Et alors ? Je croyais que nous ne ferions pas d’application Web !!!
2 Normal !
3 - C’est pas vrai !
   - C’est pas faux !
   - Mais c’est pas vrai !
   - Mais c’est pas faux !

MacOSX sur mon canapé

Ils sont arrivés ce matin !!!

A propos d’Hadopi

J'ai pas mal de questions en vrac à propos d'Hadopi: Les FAI auront le rôle de rapporteur de concordances IP - Abonné, mais qui nous surveille ? Est ce qu'Hadopi signifie fin de la copie privée ? Comment on distingue un téléchargement légal d'un illégal ? On se base sur le protocole et on dit que tout p2p == piratage ? Ou on... Lire A propos d'Hadopi

MAC OS X : Synchroniser son carnet d’adresses avec GMail

Watchman en Alpha par JTEK

Un de mes projets chez JTEK est la création de Watchman. Cette application permet d'aider à vérifier la sécurité de son serveur.

Une annonce officielle a ainsi ouvert Watchman en Alpha. N'hésitez pas à le tester.


Article original écrit par Cyril Mougel et publié sur Shiny happy people coding | lien direct vers cet article | Si vous lisez cet article ailleurs que sur Shiny happy people coding, c'est qu'il a été reproduit illégalement et sans autorisation.

Les modèles dans Capcode

databaseJ’avais dit que la prochaine fois que je parlerais de Capcode, cela serait pour montrer son utilisation avec Cappuccino… Pardonnez-moi, mais il faudra encore attendre.

En effet, je me suis dit1 que vous parler de l’ajout des modèles était aussi une partie intéressante. En effet, bonne nouvelle, j’ai ajouté la possibilité de créer ses modèles avec couch_foo et DataMapper.

Vous avez pu vous en rendre compte avec l’exemple de blog donné dans la seconde partie de mon article sur la création d’un framework avec Rack, l’utilisation de couch_foo est relativement facile. En effet, il suffit de créer les classes (héritant de CouchFoo::Base) mappant le modèle de donné, d’initialiser la connexion à la base de données via CouchFoo::Base.set_database et le tour est joué. Dans l’exemple de blog, j’avais mis en place une simple classe Story (avec trois propriétés : title, body et date) :

require 'rubygems'
require 'couch_foo'
 
...
 
CouchFoo::Base.set_database(:host => "http://localhost:5984", :database => "my_blog")
 
class Story < CouchFoo::Base
  property :title, String
  property :body, String
  property :date, String
end

Par la suite, l’écriture, la lecture la mise à jour ou la suppression de données se font respectivement via les méthodes CouchFoo::Base.create, CouchFoo::Base.find, CouchFoo::Base.update et CouchFoo::Base.delete. Bien entendu, couch_foo supporte les conditions, les associations…

Faisons la même chose avec DataMapper.

La création de la classe Story est sensiblement identique. La différence est que dans le cas de DataMapper, cette classe n’hérite d’aucune classe, mais qu’elle doit inclure le module DataMapper::Resource :

class Story
  include DataMapper::Resource
 
  property :id, Integer, :serial => true
  property :title, String
  property :body, String
  property :date, String
end

Autre petite différence que vous aurez notée : la présence de la propriété :id. En effet, avec DataMapper, les tables doivent obligatoirement avoir une clé. Pour cela il faut soit déclarer la propriété de type serial, soit indiquer explicitement que c’est une clé. Dans le premier cas, vous avez deux possibilités :

property :id, Serial

ou

property :id, Integer, :serial => true

Dans le second cas, voici la méthode d’écriture :

property :name, String, :key => true

La connexion à la base se fait quant à elle en utilisant la méthode DataMapper.setup. Lors de cet appel, nous avons le choix entre utiliser une URL de connexion ou un hachage. Ainsi, les deux méthodes suivantes sont identiques :

DataMapper.setup( :default, "mysql://user:password@localhost:3306/my_database")
 
DataMapper.setup( :default, {
  :adapter => 'mysql',
  :database => 'my_database',
  :username => 'user',
  :password => 'password',
  :host => 'localhost',
  :port => 3306
})

Enfin, l’écriture, la lecture la mise à jour ou la suppression de données se font respectivement via les méthodes DataMapper::Model.new, DataMapper::Model.get!, DataMapper::Resource.update_attributes et DataMapper::Resource.destroy. Là encore, vous pouvez jouer avec les conditions, les associations et autres joyeusetés.

Comme vous avez pu le voir, nous n’avons pas parlé de migration. En fait avec couch_foo, il n’y a nul besoin de faire une quelconque demande pour créer le modèle de données. Cette opération est faite automatiquement lors de l’initialisation de la connexion. Avec DataMapper, il faut faire une demande explicite. Pour cela nous avons le choix entre plusieurs méthodes : DataMapper.auto_migrate! ou DataMapper.auto_upgrade!. Vous l’aurez certainement compris, mais auto_migrate! est destructeur alors qu’auto_upgrade! ne l’est pas.

Sachant tout cela, nous pouvons maintenant ajouter les modèles dans Capcode. Pour cela nous allons créer deux fichiers, l’un pour couch_foo (couchdb.rb) et l’autre pour DataMapper (dm.rb).

# couchdb.rb
require 'rubygems'
require 'couch_foo'
require 'yaml'
require 'logger'
 
module Capcode
  Base = CouchFoo::Base
  module Resource
  end
 
  class << self
    def db_connect( dbfile, logfile )
      dbconfig = YAML::load(File.open(dbfile)).keys_to_sym
      Base.set_database(dbconfig)
      Base.logger = Logger.new(logfile)
    end
  end
end
# dm.rb
require 'rubygems'
require 'dm-core'
require 'yaml'
require 'logger'
 
module Capcode
  class Base
  end
  Resource = DataMapper::Resource
 
  class << self
    def db_connect( dbfile, logfile ) #:nodoc:
      dbconfig = YAML::load(File.open(dbfile)).keys_to_sym
      DataMapper.setup(:default, dbconfig)
      DataMapper::Logger.new(logfile, :debug)
      DataMapper.auto_upgrade!
    end
  end
end

Sachant que dans un cas, notre modèle doit hériter d’une classe et dans l’autre il doit inclure un module, nous avons ajouté dans les deux fichiers un module et une classe. Ainsi, la création des modèles peut avoir sensiblement la même syntaxe.

Exemple de modèle créé avec couchdb.rb :

require 'capcode/base/couchdb'
 
class Story < Capcode::Base
  include Capcode::Resource
 
  property :title, String
  property :body, String
  property :date, String
end

Exemple de modèle créé avec dm.rb :

require 'capcode/base/dm'
 
class Story < Capcode::Base
  include Capcode::Resource
 
  property :id, Integer, :serial => true
  property :title, String
  property :body, String
  property :date, String
end

Enfin, afin d’éviter de devoir demander explicitement la création de la base, nous allons modifier la méthode run en y ajoutant le morceau de code suivant :

# Start database
if self.methods.include? "db_connect"
  db_connect( conf[:db_config], conf[:log] )
end

Et ActiveRecord ?

ActiveRecord est un peu plus long à mettre en place. En effet, non seulement il faut créer les modèles, mais en plus, il faut mettre en place les migrations. Avec couch_foo et DataMapper les informations relatives au modèle de données sont directement dans les classes d’accesseur. Avec ActiveRecord, ces informations sont à part. Donc soit vous créez le modèle à la main, soit vous devez créer des migrations. Si nous reprenons les exemples donnés ci-dessus, voici l’enchaînement demandé :

1. Création du script de migration :

# migrate/001_create_stories.rb
class CreateStories < ActiveRecord::Migration
  def self.up  
    create_table :stories do |t|  
      t.string :title
      t.string :body
      t.string :date
    end  
  end  
 
  def self.down  
    drop_table :stories  
  end  
end

2. Création d’un Rakefile pour la création du schéma de base :

# Rakefile
require 'active_record'  
require 'yaml'  
 
task :default => :migrate  
 
desc "Migration de la base via les scripts situés dans migrate. Vous pouvez utiliser VERSION=x"  
task :migrate => :environment do  
  ActiveRecord::Migrator.migrate('migrate', ENV["VERSION"] ? ENV["VERSION"].to_i : nil )  
end  
 
task :environment do  
  ActiveRecord::Base.establish_connection(
    YAML::load(File.open('database.yml'))
  )  
  ActiveRecord::Base.logger = 
    Logger.new(File.open('database.log', 'a'))  
end

3. Lancement de la migration :

$ rake
(in /Users/greg/temp/ar)
==  CreateStories: migrating ==================================================
-- create_table(:stories)
   -> 0.0029s
==  CreateStories: migrated (0.0037s) =========================================

4. Utilisation :

# test.rb
require 'rubygems'  
require 'active_record'  
require 'yaml'  
 
dbconfig = YAML::load(File.open('database.yml'))  
ActiveRecord::Base.establish_connection(dbconfig)  
 
class Story < ActiveRecord::Base   
end
 
Story.new( :title => "Hello", :body => "Bonjour le monde", :date => Time.now.to_s ).save
 
Story.find( :all ).each do |s|
  puts s.title
  puts s.date
  puts s.body
end

Pour exécuter cet exemple, nous avons également besoin d’un fichier de configuration YAML :

# database.yml
adapter: sqlite3
database: base.db

J’ai quelque peu exagéré. Il est tout à fait possible de tout mettre dans un même fichier (migration, Rakefile, utilisation). C’est par exemple ce que fait Camping

Quoi qu’il en soit, la mise en place des modèles avec ActiveRecord demande un effort légèrement plus important. De plus, couch_foo et bien plus proche de DataMapper qu’ActiveRecord dont il se revendique de copier le style. Enfin, je trouve DataMapper bien plus agréable à utiliser. ActiveRecord n’en reste pas moins un excellent ORM, mais qui ne trouvera pas sa place dans Capcode2.

Tout cela nous amène à une nouvelle version de Capcode.

1 Il ne s’est rien dit du tout ! C’est jusqu’il en a besoin.
2 En tout cas pas avec moi…

RailsCampParis 2, j’y étais et c’était sympa

Ca y est le RailsCamp Paris 2, c'est terminé hier avec un mashpit sur Merb. Je ne l'ai pas annoncé sur ce blog, mais j'étais bien sûr présent. Les locaux de Sun où nous avons pu faire ce RailsCamp était vraiment très beau et spacieux. Nous avons pu y avoir de très bonne discussion. Merci à Yannick d'avoir tenu le bar et à Jean-François Tran d'avoir organisé cet événement.:).

Au niveau des présentations, étant depuis décembre un fervent défenseur de Merb/DataMapper, j'ai fait de petite présentation sur ces deux outils merveilleux. En voici les slides :

Le lendemain, le mashpit sur Merb a ainsi vu l'apparition de 3 petit projets en Merb :


Article original écrit par Cyril Mougel et publié sur Shiny happy people coding | lien direct vers cet article | Si vous lisez cet article ailleurs que sur Shiny happy people coding, c'est qu'il a été reproduit illégalement et sans autorisation.

Capcode sur Rubyforge

Le projet Capcode, dont j’ai longuement parlé ici et vient de trouver sa place sur rubyforge.

Le gem de la version 0.1.0 est disponible, ainsi que la documentation.