2010-02-25 6 views
2

Je ne parviens pas à enregistrer des caractères non imprimables (par exemple «\ x83») dans la base de données à l'aide de Rails (v2.2).Enregistrement de caractères non imprimables dans la base de données dans Rails

Exemple simplifié (à partir de la console):

>> f = FileSpace.create({ :name => "/tmp/\x83" }) 
=> #<FileSpace id: 7, name: "/tmp/\203"> 
>> f.name 
=> "/tmp/\203" 
>> FileSpace.last 
=> #<FileSpace id: 7, name: "/tmp/"> 

Vous pouvez donc voir, Rails est silencieusement la mise au rebut "\ x83" (ou "\ 203") caractère de la chaîne.

Le "\ 83" caractère est pas stocké dans la base de données:

mysql> SELECT hex(name) FROM file_spaces WHERE id=7; 
+------------------------------------+ 
| hex(name)       | 
+------------------------------------+ 
| 2F746D702F       | 
+------------------------------------+ 
1 rows in set (0.03 sec) 

mysql> select x'2F746D702F'; 
+---------------+ 
| x'2F746D702F' | 
+---------------+ 
| /tmp/   | 
+---------------+ 
1 row in set (0.00 sec) 

Alors, comment puis-je Rails pour enregistrer le caractère non imprimable?

Répondre

1

La solution que je fini avec a été inspirée à la fois la réponse de @ dsander et @ commentaire de Glex et utilisations callbacks. Je devais d'abord créer un champ name_encoded (par défaut: false) pour la table file_spaces dans la base de données, car les espaces fichier déjà enregistrés ne sont pas codés.

Ensuite, j'ai créé un modèle à utiliser pour callbacks (malheureusement, pas le code plus propre):

class EncodingWrapper 
    require 'base64' 

    def initialize(attribute) 
    @attribute = attribute.to_s 
    @get_method = @attribute 
    @set_method = @attribute + "=" 
    @get_encoded_method = @attribute + "_encoded" 
    @set_encoded_method = @attribute + "_encoded=" 
    end 

    def before_save(record) 
    set_encoded(record) 
    encode(record) 
    end 

    def after_save(record) 
    decode(record) 
    end 

    # Rails dislikes the after_find callback because it has a potentially 
    # severe performance penalty. So it ignores it unless it is created a 
    # particular way in the model. So I have to change the way this method 
    # works. So long, DRY code. :-/ 
    def self.after_find(record, attribute) 
    # Ugly...but hopefully relatively fast. 
    a = attribute.to_s 
    if record.send(a + '_encoded') 
     record.send(a + '=', Base64.decode64(record.send(a))) 
    end 
    end 

    private 

    def is_encoded?(record) 
    record.send(@get_encoded_method) 
    end 

    def set_encoded(record) 
    record.send(@set_encoded_method, true) 
    end 

    def encode(record) 
    record.send(@set_method, Base64.encode64(record.send(@get_method))) 
    end 

    def decode(record) 
    record.send(@set_method, Base64.decode64(record.send(@get_method))) 
    end 

end 

dernier, crochet callbacks dans le modèle Filespace:

class FileSpace < ActiveRecord::Base 
    ... 
    before_save EncodingWrapper.new(:name) 
    after_save EncodingWrapper.new(:name) 
    # Have to do the after_find callback special, to tell Rails I'm willing to 
    # pay the performance penalty for this feature. 
    def after_find 
    EncodingWrapper.after_find(self, :name) 
    end 
    ... 
end 
1

Je ne peux pas vous dire exactement où \x83 est perdu, mais je suppose que ce serait la base de données (au moins PostgreSQL rejette cette chaîne en raison d'un codage octet invalide).

Pour travailler arround vous pouvez base64 encoder votre chaîne et stocker celle-là:

require 'base64' 
str = "/tmp/\x83" 
encoded = Base64.encode64(str) => "L3RtcC+D\n" 
Base64.decode64(encoded) => "/tmp/\x83" 
+0

En outre, tout en base64 encodage/décodage fonctionnerait, je préférerais quelque chose qui fonctionne de manière plus transparente avec Rails/ActiveRecord. En d'autres termes, je ne veux pas avoir à se souvenir de base64 décoder le champ chaque fois que je le récupère de la base de données. Mais, tout cela dit, merci pour le conseil sur base64 - c'est certainement mieux que rien! :-) –

+1

Vous pouvez remplacer setter/getter de l'attribut sur le modèle pour faire des choses de base64 pour vous – glebm

Questions connexes