… rien de plus simple avec BozoCarck qui utilise une recherche sur Google. Et encore mieux, il y a Md5This, si vous ne voulez/pouvez utiliser BozoCrack.
… rien de plus simple avec BozoCarck qui utilise une recherche sur Google. Et encore mieux, il y a Md5This, si vous ne voulez/pouvez utiliser BozoCrack.
Voici un framework CSS qui vaut le coup d’oeil, surtout pour ceux comme moi qui n’ont pas encore bien stabilisé les bonnes pratiques avec HTML5 : Boilerplate.
Bien que je ne sois pas fan des frameworks CSS, celui ci à l’avantage de se présenter sous différente forme et surtout assez didactique. J’avoue je n’ai pas passé au crible tous les frameworks CSS, mais au premier coup d’oeil celui ci me paraît valoir le coup.
Encore un petit outil sympathique fait en Ruby : nanoc.
Nanoc est un outil qui permet de réaliser un site web statique. D’ailleurs leur site est réalisé avec nanoc et vous pouvez télécharger les sources
Mais pour ma part je préfère l’utiliser dans le cas d’intégration HTML et que je dois livrer à des clients une version statique. Comme les frameworks MVC évolués, il gère un layout, des partial.
pour l’installation de nanoc, un petit coup de gem :
$ gem install nanoc
pour commencer le site, avec toute l’architecture :
$ nanoc create_site monsite create config.yaml create Rakefile create Rules create content/index.html create content/stylesheet.css create layouts/default.html Created a blank nanoc site at 'monsite'. Enjoy! $ cd monsite $ ls -l -rw-r--r-- 1 1000 1000 2090 mai 27 10:18 config.yaml drwxr-xr-x 2 1000 1000 4096 mai 27 10:18 content drwxr-xr-x 2 1000 1000 4096 mai 27 10:18 layouts drwxr-xr-x 2 1000 1000 4096 mai 27 10:18 lib drwxr-xr-x 2 1000 1000 4096 mai 27 10:18 output -rw-r--r-- 1 1000 1000 22 mai 27 10:18 Rakefile -rw-r--r-- 1 1000 1000 692 mai 27 10:18 Rules
Ensuite soit vous voulez une version statique de votre site :
$ nanoc compile Loading site data... Compiling site... create [0.00s] output/style.css create [0.04s] output/index.html Site compiled in 0.21s.
ok mais moi, je veux développer, intégrer, modifier sans avoir à recompiler .. bah il y a le autocompile.
L’option autocompile lance un serveur web (comme webrick avec rails) et à chaque requête vérifie les timestamps des fichiers, recompile si nécéssaire et envoie la version statique via le serveur web.
$ nanoc autocompile Running on http://0.0.0.0:3000/
Il suffit de regarder dans le navigateur à l’adresse http://0.0.0.0:3000/ et comme ça ldéveloppeur Rails n’est pas dépaysé !
Si vous ne souhaitez pas utiliser l’autocompile et encore moins le compile, mais juste voir le site via un serveur web rapidement il y la commande view :
$ nanoc view Running on http://0.0.0.0:3000/
Bon ça c’est le cas simple du tutoriel. Pour réaliser l’intégration, j’ai du apporter mon lot de configuration tiré des sources du site de nanoc lui-même :
Il faut faut savoir que tout ce qui est dans content et layout est compilé par nanoc, et donc peut modifier les feuilles de style, déplacer les images.
Personnellement, je veux juste qu’il me génère mes pages HTML. J’ai donc mis mes feuilles de style et toutes les images de style et autre fichiers JS dans un dossier static
$ cd monsite $ mkdir static
Editer le fichier config.yaml et ajouter le dossier static comme étant de type ‘static’
.... layouts_root: / - type: static items_root: /static/
le type static n’existe pas dans nanoc. Il faut le déclarer soit-même et créer un data_source. Dans le dossier lib/data_sources, il faut créer le fichier static.rb :
require 'digest' module Nanoc3::DataSources class Static < Nanoc3::DataSource identifier :static def items # Get prefix prefix = config[:prefix] || 'static' # Get all files under prefix dir filenames = Dir[prefix + '/**/*'].select { |f| File.file?(f) } # Convert filenames to items filenames.map do |filename| attributes = { :extension => File.extname(filename)[1..-1], :filename => filename, } identifier = filename[(prefix.length+1)..-1] + '/' mtime = File.mtime(filename) checksum = checksum_for(filename) Nanoc3::Item.new( filename, attributes, identifier, :binary => true, :mtime => mtime, :checksum => checksum ) end end private # Returns a checksum of the given filenames # TODO un-duplicate this somewhere def checksum_for(*filenames) filenames.flatten.map do |filename| digest = Digest::SHA1.new File.open(filename, 'r') do |io| until io.eof data = io.readpartial(2**10) digest.update(data) end end digest.hexdigest end.join('-') end end end
Ce qui aura pour effet que de copier les fichiers dans le dossier output sans traitement particulier.
Note : Ce fichier est contenu dans les sources du site nanoc.
Il faut également modifier le fichier Rules, qui sert traiter les routes et à indiquer quels dossiers et fichiers doivent être compilé (vous pouvez compiler également des feuilles de style) :
#!/usr/bin/env ruby route '/static/*' do # /static/foo.html/ → /foo.html item.identifier[7..-2] end compile '/static/*' do end compile '/stylesheet/' do # don’t filter or layout end compile '*' do filter :erb layout 'default' end route '/stylesheet/' do '/style.css' end route '*' do item.identifier + 'index.html' end layout '*', :erb
Maintenant vous pouvez travailler sur le layout :layouts/default.html, et sur le contenu d’une page : content/index.html.
Pour réaliser un partial style Rails :
<%= render 'monpartial' %>
Voilà au moins le minimum pour pouvoir intégrer sereinement, et en plus vous êtes plusieurs sur l’intégration (ce qui est rare) SVN ou Git sont vos amis.
J’ai besoin une version de quelques sites réalisés avec Rails en version statique pour une présentation offline.
J’aurai pu utiliser wget, mais franchement j’avais envie et besoin d’un outil plus personnalisable. Ou bien étendre les controller pour récupérer la sortie mais trop de problème et pas assez de souplesse. C’est là qu’intervient Anemone.
La mise en place est simple (via gem bien sûr), et la prise en main l’est tout autant, comme le montre l’exmple de leur site :
require 'anemone' Anemone.crawl("http://www.example.com/") do |anemone| anemone.on_every_page do |page| puts page.url end end
C’est assez rudimentaire, mais en couplant avec des outils puissant comme Hpricot, et une surcharge de la classe URI, on en sort un outil vraiment personnalisé et puissant.
J’ai pu réécrire les chemins statiques de mon code en chemin relatif pour une visualisation offline en surchargeant la classe URI
require 'uri' require 'rubygems' require 'anemone' require 'hpricot' OUTPUT_DIR = File.join(File.dirname(__FILE__), 'export', 'sites') module UriExt def last_item_of_path_is_used_for_filename self.query || (File.basename(self.path) =~ /^(.*)\..*$/) end def to_static_filename filename = '' if self.query if (File.basename(self.path) =~ /^(.*)\..*$/) filename = $1 else filename = File.basename(self.path) end filename += '-' + self.query_to_filename_ext + '.html' else if (File.basename(self.path) =~ /^(.*)\..*$/) filename = File.basename(self.path) else filename = 'index.html' end end filename end def query_to_filename_ext self.query ? self.query.gsub(/[=;&]/, '-') : nil end def build_relative_path(with_host=true) if self.path !~ /^\/$/ items = with_host ? [self.host] : [] original_path = (self.path =~ /^\/(.*)$/ ? $1 : self.path) items_original_path = original_path.split('/') items_original_path.shift if items_original_path.first == '/' items_original_path.pop if self.last_item_of_path_is_used_for_filename items += items_original_path File.join(items) end end def complete_path_filename(with_host=true) items = [] relative_path = self.build_relative_path(with_host) items << relative_path if relative_path items << self.to_static_filename File.join(items) end def create_dir relative_path = self.build_relative_path if relative_path FileUtils.mkdir_p(File.join(OUTPUT_DIR, relative_path)) end end def create_file(content) self.create_dir File.open(File.join(OUTPUT_DIR, self.complete_path_filename), 'w') do |file| file.write(content) end end def build_relative_path_for_html if self.path !~ /^\/$/ (self.path =~ /^\/(.*)$/ ? $1 : self.path) else 'index.html' end end def root_back_relative_path(an_absloute_path) if self.path !~ /^\/$/ return File.join((['..'] * ((self.path.split('/')).length - 1)).join('/'), (an_absloute_path =~ /^\/(.*)$/ ? $1 : an_absloute_path)) else (an_absloute_path =~ /^\/(.*)$/ ? $1 : an_absloute_path) end end end URI::HTTP.send(:include, UriExt) URI::Generic.send(:include, UriExt) Anemone.crawl("http://www.example.com") do |anemone| # cache pages anemone.storage = Anemone::Storage.PStore('arnaudkozlinski.pstore') # avoid JPG crawl (just a particular case for my website) # it's here only for th example anemone.focus_crawl { |page| page.links.select{ |uri| uri.path !~ /\.(jpg|JPG)$/ } } anemone.on_every_page do |page| puts page.url # if craw a html page parse and modify it with Hpricot if page.headers['content-type'].join('') =~ /^text\/html/ doc = Hpricot(page.body) # image source rewriting doc.search('img').each do |img| if img.attributes['src'] uri_img = URI.parse(img.attributes['src']) img['src'] = page.url.root_back_relative_path(uri_img.path) end end .... # stylesheet source rewriting doc.search('link').each do |script| if script.attributes['href'] uri_script = URI.parse(script.attributes['href']) script['href'] = page.url.root_back_relative_path(uri_script.path) end end .... end end
Petit bémol, reste le parsing des feuilles de style CSS pour la réécriture des url() vers les images de style. Mais comme les url des feuilles de style sont déjà en relatif (c’est ma règle en tout cas), je n’ai pas eu besoin de parser, juste copier les images au bon endroit. D’autant que je n’ai pas trouvé le Hpricot du CSS.
Ça peut paraître long à première vue, mais quand on maîtrise Ruby, c’est largement moins prise de tête que d’utiliser un web spider automatique, car il y a toujours des cas particuliers et par expérience aucun ne permet d’obtenir un résultat parfait.
c’est bien cache_key pour les ActiveRecord, mais c’est encore mieux pour les collections d’ActiveRecord.
ActiveRecord::Base.class_eval do def cache_key_with_extra_keys(*args) extra = args.empty? ? '' : '-' + args.collect{|a| a.to_s }.join('-') cache_key_without_extra_keys + extra end alias_method_chain :cache_key, :extra_keys end Array.class_eval do def cache_key(*args) keys = [] self.each do |item| keys << (item.respond_to?(:updated_at) && item.respond_to?(:id) ? "#{item.id}-#{item.updated_at.to_s(:number)}" : item.object_id.to_s) end extra = args.empty? ? '' : '-' + args.collect{|a| a.to_s }.join('-') return keys.join('-') + extra end end
comme ça, vous pouvez faire une cache key pour un ensemble :
last_articles = Article.find(:all,:limit => 5) last_articles.cache_key
Il y a des jours où vraiment Ruby me simplifie la vie. Ce qui suit n’est pas révolutionnaire mais je suis tellement content de gagner des heures de codes et de tests grâce à la souplesse de Ruby que je ne peux m’empêcher de vous en faire part.
Voici le problème :
Je possède un plugin contenant plusieurs modèles « métiers » que j’utilise dans plusieurs sites/applications.
Et dernièrement, je souhaite ajouter un attribut commun à tous ces modèles. Jusque là rien de bien compliqué, mais juste pour une seule application, c’est à dire sans avoir d’impact sur les autres applications.
Ex :
foo.rb :
class Foo < ActiveRecord::Base has_many :bars end
bar.rb :
class Bar < ActiveRecord::Base belongs_to :foo end
Ensuite vous avez une classe qui gère des tas de Foo et de Bar :
class MyScript def initialize(foo_name, bar_name) @foo_name = foo_name @bar_name = bar_name end def do_a_lot_things ... # a lot of things before if foo = Foo.find_by_name(@foo_name) ... end if bar = Bar.find_by_name(@bar_name) ... end # a lot of things after ... end end
ok. maintenant j’ajoute un champ commun : domain une sorte d’enum contenant par exemple : [‘Sport’, ‘Economy’, ‘Culture’]
>> Foo.new.domain = 'Sport' >> a_bar =Bar.find(:first) >> a_bar.domain 'Culture'
bref simple … mais je veux cloisonner mon script en fonction du domaine. il faudrait que je fasse :
class MyScript ... def do_a_lot_things ... # a lot of things before if foo = Foo.find_by_name_and_domain(@foo_name, @domain) ... end if bar = Bar.find_by_name_and_domaine(@bar_name, @domain) ... end # a lot of things after ... end end
Ok j’ai bien fait mes tests, la refactorisation ne devrait pas être compliquée … ah mais non ! mon code doit être compatible pour les applications qui utilise Foo, Bar et MyScript sans utilser le domaine.
On y arrive .. Heureusement ruby et rails mettent à disposition plusieurs outils qui permettent détendre sans tout casser.
Première piste : alias_method_chain
@@domaine = 'Sport' .... def find_with_domaine(*args) find_options = {} if @domaine find_options[:conditions] = [ 'domaine = ? ', @@domaine ] end with_scope(:find => find_options) do find_without_domaine(*args) end end alias_method_chain :find, :domaine
mouais ok mais bon comment fournir le bon domaine pour tous les Foo et Bar utilisés dans MyScript ?
Deuxième piste : une variable de classe mais attention il faut être sûr qu’elle soit valable uniquement pour le thread, voulant utiliser le hash Thread.current, j’ai quand même pris conseil chez coderr pour éviter d’abuser du Thread.current
class Class def thread_local_accessor name, options = {} m = Module.new m.module_eval do class_variable_set :"@@#{name}", Hash.new {|h,k| h[k] = options[:default] } end m.module_eval %{ FINALIZER = lambda {|id| @@#{name}.delete id } def #{name} @@#{name}[Thread.current.object_id] end def #{name}=(val) ObjectSpace.define_finalizer Thread.current, FINALIZER unless @@#{name}.has_key? Thread.current.object_id @@#{name}[Thread.current.object_id] = val end } class_eval do include m extend m end end end
et j’étends la classe active_record :
ActiveRecord::Base.class_eval do thread_local_accessor :domain_store, :default => nil class << self def do_actions_with_domain(domain, &block) ActiveRecord::Base.domain_store = domain yield ActiveRecord::Base.domain_store = nil end end end
Ensuite j’appelle mon script encadré par do_actions_with_domain
ActiveRecord::Base.do_actions_with_domain('Economy') do MyScript.new('foo_name', 'bar_name').do_a_lot_things end
et voilà pas besoin de modifier ma class MyScript et ça marche au poil, merci Ruby 🙂
Pour ceux qui n’ont pas migré le tinymce de leur site vers la version 3, l’import d’image ne fonctionne plus pour Firefox 3.6.9. Il suffit de modifier tiny_mce.js, dans la fonction fixGeckoBaseHREFBug :
if (m == 1) { // h = h.replace(/\ssrc=/gi, " mce_tsrc="); // h = h.replace(/\shref=/gi, " mce_thref="); return h; }
Dans un précédent article, je vous parlais de ma découverte sur les problèmes avec iconv influencé par les variables d’environnements.
En fait avec Ruby (1.8.6) ça se corse, car Ruby est aussi buggé.
Alors on recommence les explications.
L’influence de la variable d’environnement sur iconv :
$ echo éà | LANG=fr_FR.UTF-8 iconv -f UTF-8 -t ascii//translit ea $ echo éà | LANG=C iconv -f UTF-8 -t ascii//translit ??
Dans un premier temps, on vérifie que tout est ok avec irb.
Avec irb, avec ‘C’
$ LANG=C irb irb(main):001:0> require 'iconv' => true irb(main):002:0> Iconv.iconv('ASCII//TRANSLIT', 'UTF-8', 'éèà') => ["???"] irb(main):003:0>
maintenant irb et fr_FR
$ LANG=fr_FR.UTF-8 irb irb(main):001:0> require 'iconv' => true irb(main):002:0> Iconv.iconv('ASCII//TRANSLIT', 'UTF-8', 'éèà') => ["eea"] irb(main):003:0>
Ok cool tout va bien 🙂
Maintenant le runner de Ruby On Rails
$ LANG=fr_FR.UTF-8 script/runner "puts Iconv.iconv('ASCII//TRANSLIT', 'UTF-8', 'ééà').inspect; puts ENV['LANG'].inspect" ["???"] "fr_FR.UTF-8"
Et là on s’arrache les cheveux … qu’on change ou pas dans config/environement.rb, le résultat est toujours le même
Et pour vérifier que le problème ne vient pas de Rails, mais vraiment de Ruby :
$ ruby -e "require 'iconv'; puts Iconv.iconv('ASCII//TRANSLIT', 'UTF-8', 'éèà').inspect; puts ENV['LANG'].inspect" ["???"] "fr_FR.UTF-8"
Donc on cherche un peu sur le net et la réponse est sur ruby-forum : en gros un problème d’initialisation de la variable d’environnement LC_TYPE.
Youpi … En plus Nobuyoshi Nakada nous explique comment faire son patch ..
Pour plus de simplicité et de pérennité, j’ai fait mon package. D’abord télécharger les sources de locale-bug.
Ensuite en tant que root :
$ tar -xvzf locale-bug.tgz $ cd locale-bug $ ruby extconf.rb $ make $ make install
Et dans votre application Rails (config/environment.rb) :
... require 'locale_bug' LocaleBug.ctype = 'fr_FR.UTF-8' require 'iconv' ...
Juste pour faire un peu de pub à un développeur RoR, et pour son bébé qui est joliment fait pour un principe aussi vieux que le Web : Solimap un site d’annonces diverses. Du mashable, du RubyOnRails mais ça on ne le voit pas, de la transparence, simple et propre.