header image
 

[Ruby] Calcul des jours fériés

Je n’aime pas commencer quelque chose et ne pas le terminer alors hop, la suite du post précédent :

voici tout ce qu’il vous faut pour déterminer si un jour donné est férié ou pas :)

et également, à la demande de Denis, des méthodes utiles pour manipuler tout ça ^^

Il faudra bien-sur penser à maintenir tout ça en cas de disparition spontanée de jour férié dans les années à venir, comme c’est la tendance depuis quelques temps ;p

je suis également preneur de tout conseil sur la qualité du code et l’état de l’art, une façon plus élégante ou plus efficace de gérer tout ça ;p

Le code suivant est à ajouter au même niveau que la méthode pour calculer Pâques :

#Permet de calculer le jour de l’ascencion de l’année donnée
def self.getAscencion(date)
ascencion = self.getPaques(date) + 38
return ascencion
end

#Permet de calculer le jour du lundi de pentecote de l’année donnée
def self.getPentecote(date)
pentecote = self.getPaques(date) + 49
return pentecote
end

#Permet de récupérer le nombre de jours ouvrés pour un mois donné
def self.getNbJoursOuvresMois(date)
nb_jours_ouvres_mois = 0
date_debut = DateTime::new(date.year, date.month, 1)
date_fin = self.getDernierJour(date)
nb_jours_ouvres_mois = self.getNbJoursOuvresPeriode(date_debut, date_fin)
return nb_jours_ouvres_mois
end

# Permet de déterminer si le jour passé en param est férié ou non
def self.jourFerie?(date)
is_jour_ferie = false
tab_jours_feries = {
:nouvel_an => DateTime::new(date.year,1,1),
:armistice_39_45 => DateTime::new(date.year,5,8),
:toussaint => DateTime::new(date.year,11,1),
:armistice_14_18 => DateTime::new(date.year,11,11),
:assomption => DateTime::new(date.year,8,15),
:fete_du_travail => DateTime::new(date.year,5,1),
:fete_nationnale => DateTime::new(date.year,7,14),
:noel => DateTime::new(date.year,12,25),
:paques => UtilsDate.getPaques(date),
:ascencion => UtilsDate.getAscencion(date),
:pentecote => UtilsDate.getPentecote(date)
}
tab_jours_feries.each do |j|
if j[1] == date
is_jour_ferie = true
break
end
end
return is_jour_ferie
end

# Permet de récupérer le nombre de jours ouvrés pour un mois donné
def self.getNbJoursOuvresMois(date)
nb_jours_ouvres_mois = 0
date_debut = DateTime::new(date.year, date.month, 1)
date_fin = self.getDernierJour(date)
nb_jours_ouvres_mois = self.getNbJoursOuvresPeriode(date_debut, date_fin)
return nb_jours_ouvres_mois
end

# Permet de récupérer le nombre de jours ouvrés entre un jour donné et la fin du mois
def self.getNbJoursOuvresAvantFinMois(date)
nb_jours_ouvres_avant_fin_mois = 0
date_fin_mois = self.getDernierJour(date)
date.upto(date_fin_mois){ |ma_date|
nb_jours_ouvres_avant_fin_mois += 1 if self.isJourOuvre(ma_date)
}
return nb_jours_ouvres_avant_fin_mois
end

# Permet de récupérer le nombre de jours ouvré entre deux dates, inclues
def self.getNbJoursOuvresPeriode(date_debut, date_fin)
nb_jours_ouvres_periode = 0
#pour être certain de boucler dans le bon sens :
date_temp = nil
if date_debut > date_fin
date_temp= date_debut
date_debut = date_fin
date_fin = date_temp
end
date_debut.upto(date_fin){ |ma_date|
nb_jours_ouvres_periode += 1 if self.isJourOuvre(ma_date)
}
return nb_jours_ouvres_periode
end

# Permet de déterminer si le jour passé en param est ouvré
# ie : ni un jour férié, ni un samedi, ni un dimanche
def self.isJourOuvre(date)
is_jour_ouvre = false
if (!self.isJourFerie(date) && date.wday!=0 && date.wday!=6)
is_jour_ouvre = true
end
return is_jour_ouvre
end

# Permet d’obtenir le dernier jour du mois de la date passée en param
def self.getDernierJour(date)
if date.month == 12
annee = date.year+1
mois = 1
else
annee = date.year
mois = date.month+1
end
date_debut_mois_suivant = DateTime::new(annee, mois, 1)
date_dernier_jour = date_debut_mois_suivant - 1
return date_dernier_jour
end

[Ruby] Calcul du Lundi de Pâques

Ayant besoin de savoir discerner un jour ouvré d’un autre jour tout bête, je me suis mis en vain en quête d’un bout de code Ruby qui saurait me calculer Pâques, alors je m’y suis mis.

En effet il y a deux types de jours fériés en France : fixes (25 décembre, 11 novembre, etc…) et variables (Pâques, Ascension, Pentecôte).

Les jours variables dépendes tous du calcul du jours de Pâques (Ascension = Pâques + 38 jours, Pentecote = Pâques + 49 jours), et ce calcul étant tout sauf simple je vous fais cadeau des explications et du code Ruby.

Car il faut avouer que si c’est utile, ce n’est pas très palpitant à coder ^^

Vous pouvez y aller, ce bout de code est testé avec succès jusqu’en 2035 !

# permet de calculer le jour du lundi de paques de l’année donnée
#
# calcul du lundi de paques : exemples données pour 2007
#
# Les divisions doivent toujours être entières (on supprime les décimales).
#
# G qui représente le nombre d’or diminué de 1: Diviser l’année par 19, en prendre le reste
# ( 2007/19=105 or 105×19=1995 et il nous faut 2007, donc l’écart vaut G=12 )
#
# C et C_4 permettent le suivi des années bissextiles: diviser l’année par 100 puis encore par 4
# ( 2007/100=C=20
# et 20/4=C_4=5 )
#
# E : Diviser (8 x C + 13) par 25 sans les décimales
# ( 8×20+13=173 /25=E=6 )
#
# H qui dépend de l’épacte : diviser (19xG + C - C_4 - E + 15) par 30, en prendre le reste
# ( On prend le reste d’une division selon le même principe que pour G: (252)/30=8 or 8×30=240 et il nous faut (252), donc l’écart vaut H=12 )
#
# K : diviser H par 28
# ( 12 / 28 = K = -0 )
#
# P : diviser 29 par (H+1)
# ( 29 / 13 = P = 2 )
#
# Q : diviser (21-G) par 11
# ( 21-12=9 /11=Q=0 )
#
# I représente le nombre de jours entre la pleine lune pascale et le 21 mars : ( KxPxQ - 1 ) x K + H
# ( -0×2x0-1=-1 x-0=0 + 12= I=12 )
#
# B : diviser l’année par 4 et enlever les décimales, y ajouter l’année
# ( 2007/4=501 +2007 = 2508 )
#
# J1 : Additionner B + I + 2 + C_4 et retrancher C
# ( J1 = 2507 )
#
# J2 calcule le jour de la lune pascale (0=dimanche 1=lundi…6=samedi) : diviser J1 par 7 et en prendre le reste.
# ( On calcule toujours le reste d’une division selon le même principe qu’avec G et H, le résultat est J2=1 )
#
# R le résultat final, enfin : 28 + I - J2
# ( R= 39 )
#
# R représente la date du mois de mars, s’il dépasse 31 on déborde sur avril
# (… 30 correspond au 30 mars, 31 au 31 mars, 32 au 1er avril, 33 au 2 avril, …).
# Retrancher 31 le cas échéant pour obtenir la date d’avril. (Pâques 2007 tombe donc le 8 avril.)
def self.getPaques
year = date.year
g = year%19
c = (year/100).to_i
c_4 = (c/4).to_i
e = ((8 * c + 13)/25).to_i
h = (19 * g + c - c_4 - e + 15)%30
k = (h/28).to_i
p = (29/(h + 1)).to_i
q = ((21 - g)/11).to_i
i = ( k * p * q - 1 ) * k + h
b = (year/4).to_i + year
j1 = b + i + 2 + c_4 - c
j2 = j1%7
r = 28 + i - j2 + 1
if r <= 31
mois_paques = 3
jour_paques = r
else
mois_paques = 4
jour_paques = r - 31
end
date_paques = DateTime::new(year, mois_paques, jour_paques)
# on ajoute 1 car on cherche le lundi de paques alors que le calcul permet de trouver le dimanche de paques
return date_paques
end

voici en prime parceque ça aussi ce n’est pas très passionnant de quoi tester ce code jusqu’en 2035, avec les dates correctes délivrées par le conseil dont c’est le rôle comme point de comparaison :

def test_getPaques
assert_equal(DateTime::new(2007,4,9), UtilsDate.getPaques(DateTime::new(2007, 1, 1)))
assert_equal(DateTime::new(2008,3,24), UtilsDate.getPaques(DateTime::new(2008, 1, 1)))
assert_equal(DateTime::new(2009,4,13), UtilsDate.getPaques(DateTime::new(2009, 1, 1)))
assert_equal(DateTime::new(2010,4,5), UtilsDate.getPaques(DateTime::new(2010, 1, 1)))
assert_equal(DateTime::new(2011,4,25), UtilsDate.getPaques(DateTime::new(2011, 1, 1)))
assert_equal(DateTime::new(2012,4,9), UtilsDate.getPaques(DateTime::new(2012, 1, 1)))
assert_equal(DateTime::new(2013,4,1), UtilsDate.getPaques(DateTime::new(2013, 1, 1)))
assert_equal(DateTime::new(2014,4,21), UtilsDate.getPaques(DateTime::new(2014, 1, 1)))
assert_equal(DateTime::new(2015,4,6), UtilsDate.getPaques(DateTime::new(2015, 1, 1)))
assert_equal(DateTime::new(2016,3,28), UtilsDate.getPaques(DateTime::new(2016, 1, 1)))
assert_equal(DateTime::new(2017,4,17), UtilsDate.getPaques(DateTime::new(2017, 1, 1)))
assert_equal(DateTime::new(2018,4,2), UtilsDate.getPaques(DateTime::new(2018, 1, 1)))
assert_equal(DateTime::new(2019,4,22), UtilsDate.getPaques(DateTime::new(2019, 1, 1)))
assert_equal(DateTime::new(2020,4,13), UtilsDate.getPaques(DateTime::new(2020, 1, 1)))
assert_equal(DateTime::new(2021,4,5), UtilsDate.getPaques(DateTime::new(2021, 1, 1)))
assert_equal(DateTime::new(2022,4,18), UtilsDate.getPaques(DateTime::new(2022, 1, 1)))
assert_equal(DateTime::new(2023,4,10), UtilsDate.getPaques(DateTime::new(2023, 1, 1)))
assert_equal(DateTime::new(2024,4,1), UtilsDate.getPaques(DateTime::new(2024, 1, 1)))
assert_equal(DateTime::new(2025,4,21), UtilsDate.getPaques(DateTime::new(2025, 1, 1)))
assert_equal(DateTime::new(2026,4,6), UtilsDate.getPaques(DateTime::new(2026, 1, 1)))
assert_equal(DateTime::new(2027,3,29), UtilsDate.getPaques(DateTime::new(2027, 1, 1)))
assert_equal(DateTime::new(2028,4,17), UtilsDate.getPaques(DateTime::new(2028, 1, 1)))
assert_equal(DateTime::new(2029,4,2), UtilsDate.getPaques(DateTime::new(2029, 1, 1)))
assert_equal(DateTime::new(2030,4,22), UtilsDate.getPaques(DateTime::new(2030, 1, 1)))
assert_equal(DateTime::new(2031,4,14), UtilsDate.getPaques(DateTime::new(2031, 1, 1)))
assert_equal(DateTime::new(2032,3,29), UtilsDate.getPaques(DateTime::new(2032, 1, 1)))
assert_equal(DateTime::new(2033,4,18), UtilsDate.getPaques(DateTime::new(2033, 1, 1)))
assert_equal(DateTime::new(2034,4,10), UtilsDate.getPaques(DateTime::new(2034, 1, 1)))
assert_equal(DateTime::new(2035,3,26), UtilsDate.getPaques(DateTime::new(2035, 1, 1)))
end

[Java] Compiler automatiquement ses projets Eclipse hors Eclipse

Je vous explique la problématique : Eclipse se débrouille pour compiler tout seul son workspace, en déterminant l’ordre de compilation des projets selon leurs dépendances entre eux.

Lorsqu’on travaille sur un workspace de plus de 40 projets Java tous dépendants les uns des autres pour la compile, et que l’on souhaite les compiler hors Eclipse, via Ant par exemple (dans mon cas pour mettre en place une intégration continue avec CruiseControl), la mise et point et la maintenance du script de build Ant qui va piloter tout ça devient complexe.

Pour éviter de passer mon temps à chercher l’ordre des dépendances, voici la solution que j’ai mise en place (je ne peux vous livrer que les principes, le code étant sous NDA) :

A partir d’un workspace Eclipse correct (ie : contenant les bons projets et qui compile), faire une exportation en .psf (file -> export -> team -> team project set). Ce fichier produit par Eclipse au format xml liste tous les projets du workspace.

Ecrire un petit outil en Java (ou autre, comme vous le sentez) qui va, à partir du parsing de ce psf, constituer la liste des projets à compiler, et qui va aller lire le contenu du .project et du .classpath de chacun d’eux.

Dans chaque .classpath, récupérer et associer à chaque projet sa liste de dépendances (« kind=”lib” »)

Une fois tout ceci récupéré, écrire un traitement de trie ordonné des projets. L’algo peut être du type :

-tant que tous mes projets ne sont pas triés,

- pour chaque projet de la liste non triée, je vérifie si toutes ses dépendances figurent déjà dans la liste triée : - si oui, j’ajoute le projet à la fin de la liste triée - si non je ne l’ajoute pas et je passe au projet suivant

On obtient ainsi automatique l’ordre de compilation des différents projetsIl suffit ensuite de générer un Ant « chapeau » qui va enchainer les antcall sur les builds des différents projets dans le bon ordre.

(Le .psf que j’utilise est en conf sous ClearCase, les développeurs doivent le mettre à jour lorsqu’ils créent ou suppriment un projet du workspace de référence)

Tant qu’on y est, et comme on a déjà toutes les infos pour le faire, on peut également générer de toute pièce le build.xml de chaque projet.

Petite précaution à prendre : vérifier un compteur à chaque passage pour s’assurer qu’on n’est pas parti dans une boucle infinie. En effet, en cas de .psf non synchro avec la réalité des .classpath, certains projets peuvent ne jamais être triés (une dépendance jamais trouvée, ce genre de chose). Par exemple, j’ai constaté qu’au pire (ça dépend de l’ordre des projets dans le .psf) mon outil trie tous les projets en 15 tours de boucle, j’ai mis une limite à 100 (le temps d’exécution ne s’en ressent pas, c’est vraiment rapide) à partir de laquelle je break en demandant de vérifier les sources (psf, classpath, etc…).

Grâce à cet outil, j’ai pu mettre en place une intégration continue sur un entrelas de plus de 40 projets Eclipse, répartis sur plusieurs branches ClearCase, avec génération automatique des builds, etc…

Mes compiles CruiseControl sont toujours up-to-date, les devs n’ont pas à maintenir de build.xml dans leur projet, et moi je n’ai pas à maintenir la liste et l’ordre de compile des projets J

[Intranet] Safari pour les PME

Pour le cas du développement d’une application web (Rails ou non) à destination d’un Intranet, il arrive que le navigateur web cible ne soit pas déjà déterminé (cas des PME souvent). Dans ce cas, il peut être intéressant d’étudier l’adoption de Safari, y compris en environnement Windows.

Il est suffisemment stable et rapide pour soutenir la comparaison avec les autres moteurs, et s’il est plus aride en terme de fonctionnalités et de possibilités, il embarque l’essentiel pour une appli web pro.

Ce qui peut faire la différence selon moi, en plus de son IHM “Mac-OS Style” qui a le succès qu’on connait, c’est son moteur de lissage des polices. J’y vois au moins deux gros avantages :

Tout d’abord, lorsqu’on s’imagine passer la journée à bosser les yeux sur son navigateur web, l’anti-aliasing du rendu permet de moins fatiguer les yeux.

Ensuite et surtout, d’un point de vue ergonomique, avec la police “Lucida Grande” notemment, les très petites polices (8pt, voir 7pt) sont toujours très lisible, bien plus que les résultats que j’ai pu obtenir lors de mes tests sur les autres navigateurs.

Cela permet de mettre au point des écrans de type “tableau de bord” agrégeant une grande quantité d’informations (souvent préférés à un page-flow plus “étalé”) sans pour autant obliger le client à viser une résolution de 1600*1200 (pensez à celui qui ne travaille que sur son portable).

De plus les principales librairies Ajax (Scriptaculous, Prototype, etc…) fonctionnent parfaitement sous Safari, parfois même mieux que sur d’autres navigateurs.

[Rails] NetBeans 6

Pour mes développements Ruby et Rails, après avoir testé un peu tout ce qui existe en matière d’éditeurs et d’IDEs
(de Textmate à ThirdRail en passant par Intellij Idea ou Aptana),
ma préférence va assez facilement à NetBeans 6.
http://www.netbeans.org/

Tout d’abord et de façon tout à fait personnelle, j’ai toujours préféré les IDEs (pour peut qu’on évite l’effet usine à gaz) aux éditeurs à tout faire, et ce pour plusieurs raisons (facilité d’installation, tous les outils accessibles au même endroit, avec la même logique, cohérence, etc…).

Ensuite NetBeans, en plus d’être multi-platforme (ce qui est bien pratique pour ceux qui comme moi bossent sur un Mac à la maison mais sous Windows au boulot), NetBeans dans sa version 6 offre une intégration de Ruby et de Rails très poussée et très complète (bien plus que RadRails ou Aptana par exemple)

Des éditeurs aux générateurs en passant par la coloration syntaxique, l’intégration de Mongrel, de l’IRB, et encore et surtout la complétion automatique (pour le moment inégalée), tout fonctionne parfaitement et permet de développer de façon très efficace, l’esprit léger.

Le seul concurrant que je vois pour le moment est ThirdRail, mais pour un outil coutant d’ors et déjà 300$, il est encore bien trop buggé pour soutenir la comparaison avec l’IDE gratuit de Sun, déjà parfaitement stable dès la beta 1.
De plus, cerise sur le gateau, si une version “Rails Only” plus légère est disponible, les “multi-casquettes” qui bossent également sur Java/J2EE, C# ou autre pourront trouver très agréable la version “Full” qui leur offrira un support de travail unique toujours aussi complet (voir bien plus complet dans le cas de Java) sans avoir à changer d’outil et d’habitude d’une techno à l’autre.

Un petit mot sur IntelliJ IDea, qui progresse très vite lui aussi dans son intégration de Ruby et de Rails. Selon moi il n’est pas encore au niveau de NetBeans mais est à surveillé de très près, tant il est par ailleurs performant et bien conçu dans son approche “je code intelligemment”.

[Rails] toggle local en JS

J’ai souvent eu à mettre en place dans mes applis Rails des mécanismes de “remplacement” dynamique du contenu d’un <div>
(par exemple pour des fonctionnalités d’édition “in place”), et un link_to_remote avec la mise à jour Ajax du div concerné est souvent la meilleure solution.
Cependant, dans certains cas il est plus facile et surtout bien plus performant d’un point de vue utilisateur de générer à l’avance le contenu caché dans un span
à la suite du span affiché (j’utilise <span> car il n’impose pas de retour à la ligne, contrairement à <div&gt ;)
On positionne ensuite un event javascript qui va permettre de faire le remplacement en local sans passer par un controller et une action.
Par exemple :

<span id=”span1″><a href=”javascript:toggle_div(’span1′,’span2′)” mce_href=”javascript:toggle_div(’span1′,’span2′)”>afficher la date</a></span>
<span id=”span2″ style=”display:none;”>nous sommes le <%=Time.now.strftime(”%d %m %Y”)%></span>

La méthode toggle_div javascript, à placer dans votre application.js par exemple :

function toggle_div(div1,div2) {
  var element1 = document.getElementById(div1);
  var element2 = document.getElementById(div2);
  if (element1.getStyle(’display’)!=’none’) {
  Effect.toggle(div1, ‘appear’, {queue:’front’, duration:0.2});
  Effect.toggle(div2, ‘appear’, {queue:’end’, duration:0.2});
  } else {
  Effect.toggle(div2, ‘appear’, {queue:’front’, duration:0.2});
  Effect.toggle(div1, ‘appear’, {queue:’end’, duration:0.2});
  }
}

l’important à remarquer ici est le “queue: xxx” qui permet de chainer les effets de disparition et d’apparition.
Sans ce param, le span caché apparait avant que le span à masquer n’ait disparu, provocant un rendu des plus disgratieux :/

Avec cette méthode, pas besoin de positionner de spinner de loading:
l’effet est instantané, et ça le client aime en général beaucoup ça :)

[Rails] RedBox

Pour les fenêtres modales de mes applications Rails, j’utilise RedBox :
http://blog.craigambrose.com/past/2006/8/16/redbox-a-rails-compatible-lightbox/

Il s’agit d’un plug-in type LightBox (donc prenant le focus en avant-plan tantis que l’arrière-plan est recouvert d’un masque opaque), très facile à installer et à utiliser,
et non orienté image ou diaporama.

Pour l’installer:
script/plugin install svn://rubyforge.org/var/svn/ambroseplugins/redbox

Quand à la syntaxe, on peut positionner un lien remote vers sa fenêtre modale comme ceci:
<%= link_to_remote_redbox ‘ma fenêtre modale’, :url => {:controller => ‘messagerie’, :action => ‘check_mails’, :user_id => “JFX”}%>

Une fois ouverte notre fenêtre modale contenant un formulaire, il existe en général deux options : valider ou annuler.
La validation peut se faire en utilisant un submit_tag Rails, dans lequel on parametre la fermeture de la RedBox :
<%= submit_tag(value = ” valider “, options ={”class”,”BleuFonce”,”onclick”,”RedBox.close()”}) %>

L’annulation, dont le but est de provoquer la fermeture de la RedBox pour récupérer le focus sur l’arrière plan sans valider le formulaire, peut être codée de la façon suivante :
<%= link_to_close_redbox ‘<input type=button value=” annuler ” class=”BleuFonce”>’%>

Pour se faire une idée plus précise des possibilités de RedBox, voici les différentes méthodes mises à disposition dans le helper:
link_to_redbox(name, id, html_options = {})
link_to_component_redbox(name, url_options = {}, html_options = {})
link_to_remote_redbox(name, link_to_remote_options = {}, html_options = {})
link_to_close_redbox(name, html_options = {})
button_to_close_redbox(name, html_options = {})
launch_remote_redbox(link_to_remote_options = {}, html_options = {})

A partir de là, rien n’empêche de compléter cette liste avec ses propres besoins (par exemple un select_to_remote_redbox ouvrant la fenêtre modale sur un onChange), le code est sous licence MIT et très lisible.

Au final, RedBox s’avère tout à fait suffisant pour mes besoins, tout en étant facile à mettre en oeuvre et parfaitement stable :)

[Rails] Générer ses fichiers de migration depuis un shema SQL existant

Les fonctionnalités de migration de Rails sont très pratiques, mais il peut être assez fastidieux de rédiger ses différentes migrations, surtout si l’on doit partir d’une base de données déjà existante.

heureusement, il existe une tache Rake très pratique pour se faciliter la vie dans ce cas :

rake db_schema_dump

permet de créer le /db/schema.rb de toute pièce depuis la base associée au projet. Rien ne vous empêche ensuite de créer un fichier de migration à partir d’un copier/coller du code généré dans le self.up :)

[Rails] passer à Rails 2 sans soucis

un petit lien bien pratique si vous souhaitez migrer vers rails 2 une application déjà écrite avec une version précédente :

http://pastie.caboo.se/private/krcevozww61drdeza13e3a

je vous conseille également de parcourir le guide suivant pour être certain de mettre toutes les chances de votre côté :

http://mislav.caboo.se/rails/rails-2-0-taking-the-plunge/

pour avoir testé sur une application ni trop simple ni trop complexe, ça fonctionne parfaitement et la migration se fait sans douleur en quelques minutes :)

choses simple d’aujourd’hui

- un vol de grues reformant le V après une pause

- mon fils regardant les trainées blanches d’un avion dans le ciel

- la couverture du pic-nic séchant sur le balcon