I am using Rose::DB::Object, SQLite, and Chinese text. My classes look like this:
package My::DB;
use base qw(Rose::DB);
__PACKAGE__->use_private_registry;
__PACKAGE__->register_db(
driver => 'sqlite',
database => 'data/sqmple.db',
);
package Motorcycle;
use My::DB;
use base qw(Rose::DB::Object);
...
sub init_db { My::DB->new() };
The code used to store a record:
Motorcycle->new(
type => $self->param('type'),
brand => $self->param('brand'),
color => $self->param('color'),
)->save;
The code used to display the data (from within a Mojolicious app):
<td><%= Mojo::ByteStream->new($cycle->type)->decode("utf-8") %></td>
<td><%= Mojo::ByteStream->new($cycle->brand)->decode("utf-8") %></td>
<td><%= Mojo::ByteStream->new($cycle->color)->decode("utf-8") %></td>
How can I eliminate the decoding step? I'd like the display code to look like this instead:
<td><%= $cycle->type %></td>
<td><%= $cycle->brand %></td>
<td><%= $cycle->color %></td>
I think you need to get the sqlite_unicode => 1 configuration value down to SQLite, there was a similar question about UTF-8 and SQLite, setting sqlite_unicode did the trick there.
I don't think Rose::DB::SQLite supports this configuration parameter though. Based on this possibly similar issue with MySQL you might be able to patch Rose::DB::SQLite to add support for sqlite_unicode by adding this to the driver:
sub sqlite_unicode {
{
shift->dbh_attribute_boolean('sqlite_unicode', #_)
}
I'll leave a comment on John's answer so he can sanity check this.
If that works then you might want to send a patch to John Siracusa (who is not only already on this question but also the Rose::DB maintainer).
If you feed UTF8-encoded text into SQLite, it should give it right back to you in the same form. For example, given an SQLite database named test.db containing this schema:
CREATE TABLE things
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(64) NOT NULL
);
Run this code in a script in the same directory as the test.db database:
package My::DB;
use base qw(Rose::DB);
__PACKAGE__->use_private_registry;
__PACKAGE__->register_db
(
driver => 'sqlite',
database => 'test.db',
);
package My::Thing;
use base qw(Rose::DB::Object);
__PACKAGE__->meta->setup
(
table => 'things',
columns =>
[
id => { type => 'serial', primary_key => 1, not_null => 1 },
name => { type => 'text', length => 64, not_null => 1 },
],
);
sub init_db { My::DB->new }
package main;
# Set the name to a UTF8-encoded smiley: Unicode 0x263A
my $thing = My::Thing->new(name => "\x{e2}\x{98}\x{ba}")->save;
$thing = My::Thing->new(id => $thing->id)->load;
# This will print the UTF8-encoded smiley; make sure your
# terminal can handle UTF8 output.
print $thing->name, "\n";
If this is not working for you, then perhaps your calls to get the form parameters (e.g., $self->param('type')) are returning character strings instead of UTF8-encoded strings. That is, in the case of the smiley face string, perhaps $self->param('foo') is returning "\x{263a}" and not "\x{e2}\x{98}\x{ba}". In that case, the solution would be to encode the strings as UTF8 before setting the object attributes:
Motorcycle->new(
type => utf8::encode($self->param('type')),
brand => utf8::encode($self->param('brand')),
color => utf8::encode($self->param('color')),
)->save;
Related
When using Class::DBI in Perl, the insert() method from Class::DBI acts as a constructor returning an object. How can I use Class::DBI in combination with object attributes that are not part of any database tables?
For example: I would like to have my Music::Artist class to have a version attribute that is part of the resulting objects (so that I can use this object attribute in my overall application logic), but does not get written to the database?
Ultimately I would like to be able to combine the usage of Class::DBI with OO-systems like Moo(se).
Vanilla Class:DBI example code from metacpan:
package Music::DBI;
use base 'Class::DBI';
Music::DBI->connection('dbi:mysql:dbname', 'username', 'password');
package Music::Artist;
use base 'Music::DBI';
Music::Artist->table('artist');
Music::Artist->columns(All => qw/artistid name/);
#-- Meanwhile, in a nearby piece of code! --#
my $artist = Music::Artist->insert({ artistid => 1, name => 'U2' });
Pseude-code of what I would like to do:
package Music::Artist;
use base 'Music::DBI';
use Moo;
Music::DBI->connection('dbi:mysql:dbname', 'username', 'password');
Music::Artist->table('artist');
Music::Artist->columns(All => qw/artistid name/);
has name => ( is => 'rw' );
has version => ( is => 'rw' );
#-- Meanwhile, in a nearby piece of code! --#
my $artist = Music::Artist->new( name => 'U2', version => '0.1.0' );
$artist = Music::Artist->insert({ artistid => 1, name => $artist->name });
# ... do something with $artist->version ...
(Although this code could run, Class::DBI's insert() of cause overrides the object returned by Moo's new() in the first place.)
How to combine Class::DBI with own or third-party (Moo) constructors?
I read Class::DBIs documentation but did not find any information on how to override insert() as an approach to supply a combined constructor method. I also tried to find repositories on GitHub that make use of Class::DBI and own constructors or OO-systems in the same packages, but did not succeed either.
I'm creating a simple CRUD interface to a database, and I'm trying RapidApp.
I have an existing database, which I connect to with existing Moose-based code. There is a complication in that there is UTF-8 text in the database (eg 'Encyclopédie médico-chirurgicale. Técnicas quirúrgicas. Aparato digestivo')
My Moose-based code works just fine: data goes in & data comes out... and everyone is happy.
In my existing Moose code, the connector is:
$schema = My::Service::Schema->connect(
'dbi:Pg:dbname=my_db;host=my.host.name;port=1234',
'me',
'secret',
{ pg_enable_utf8 => 1 }
);
When I set about connecting RapidApp, I first tried a simple rdbic.pl command, but that doesn't pick up the UTF-8 strings. In an attempt to enforce UTF-8-ness, I've created the following:
use Plack::Runner;
use Plack::App::RapidApp::rDbic;
my $cnf = {
connect_info => {
dsn => 'dbi:Pg:dbname=my_db;host=my.host.name;port=1234',
user => 'me',
password => 'secret',
{ pg_enable_utf8 => 1 },
},
schema_class => 'My::Service::Schema'
};
my $App = Plack::App::RapidApp::rDbic->new( $cnf );
my $psgi = $App->to_app;
my $runner = Plack::Runner->new;
$runner->parse_options('--port', '5678');
$runner->run($psgi);
(which is pretty much rdbic.pl, compressed to one specific thing)
However - I'm getting mal-formed strings (eg: 'Encyclopédie médico-chirurgicale. Técnicas quirúrgicas. Aparato digestivo')
Having fought to get the correct text INTO the database, I know the database is correct... so how do I connect RapidApp to get UTF-8 back out?
Your schema will need to be configured to support UTF-8. Here's a helpful set of things to try:
How to properly use UTF-8-encoded data from Schema inside Catalyst app?
To display a task dashboard on the web I take information from a file with Perl, store that in a stacked hash and send it to HTML::Template::Compiled.
There I can only access the key of the hash but no further data.
Here is what I wrote in Perl
my $template = HTML::Template::Compiled->new(
filename => '../templates/task_dashboard.tmpl',
case_sensitive => 1,
search_path_on_include => 1,
loop_context_vars => 1,
use_query => 0,
default_escape => 'HTML',
);
$template->param(
TIME => $time,
VERSION => $version,
REPORTDAT => $reportdat,
INFOS => \%information,
);
print $template->output();
The hash %information looks like this:
%informmation{key} = ($RefToArrayOfHashes1, $RefToArrayOfHashes2, $Skalar1, $Skalar2);
Within HTML::Template::Compiled I want to loop through the hash %information and subsequently through its content (like Data::Dumper).
I wrote this in the template file:
<%each INFOS %>
<h2><%=__key__ %></h2>
<table>
...loop through RefToArrayOfHashes1
</table>
<table>
...loop through RefToArrayOfHashes2
</table>
<p>Skalar1, Skalar2</p>
<%/each %>
To access the keys of %information is not a problem, but I don't know how to access the lower levels of the data structure.
I read some things about to use Dot but still don't get how to do it.
Can anybody help me by giving an example?
I have following tree-like structure where each level is represented by separate class:
Book ------ Page ------ Line
1 n 1 n
Each class has a property that holds all its members (e/g/ Book::pages, as array of blessed refs), some properties specific to the level (e.g. Book::author) as well as some methods to add/remove its members.
Now I want to save/load all the data to/from a single file. It's not going to be a relational DB (most probably YAML will be used), so basically what I need to get at some point is something like:
my $book = {
author => "Fred Flinstone",
name => "My Favorite Stones",
pages => [
{
number => 1,
footer => "Dedicated to Wilma",
lines => [ ],
},
{
number => 2,
lines => [
{ text => "Preface", style => "h1" },
{ text => "This book is about my favorite stones:" },
{ text => "Marble" },
# ...
]
},
# ...
]
};
Should there be one smart pair of methods in Book that knows about all member classes? Or should each class implement part of it so that e.g. Line::save returned something like { text => "Marble" }?
What is the correct way to implement this? I would like solution that is as least as possible bounded to the actual data. What if I decide tomorrow to add Page::bookmarks and want to save Bookmarks as well?
Oh, and I'm using Moose, although that should not make much difference.
If you're using Moose, I would recommend looking at MooseX::Storage to handle this. You would use this module inside your package then add with Storage('format' => 'YAML', 'io' => 'File');.
This quick example may get you started:
package Book;
use Moose;
use MooseX::Storage;
with Storage('format' => 'YAML', 'io' => 'File');
...
1;
my $book = Book->new();
...
# to store object
$book->store('book.yml');
# to get object back
my $book2 = Book->load('book.yml');
Data defined inside Catalyst app or in templates has correct encoding and is diplayed well, but from database everything non-Latin1 is converted to ?. I suppose problem should be in model class, which is such:
use strict;
use base 'Catalyst::Model::DBIC::Schema';
__PACKAGE__->config(
schema_class => 'vhinnad::Schema::DB',
connect_info => {
dsn => 'dbi:mysql:test',
user => 'user',
password => 'password',
{
AutoCommit => 1,
RaiseError => 1,
mysql_enable_utf8 => 1,
},
'on_connect_do' => [
'SET NAMES utf8',
],
}
);
1;
I see no flaws here, but something must be wrong. I used my schema also with test scripts and data was well encoded and output was correct, but inside Catalyst app i did not get encoding right. Where may be the problem?
EDIT
For future reference i put solution here: i mixed in connect info old and new style.
Old style is like (dsn, username, passw, hashref_options, hashref_other options)
New style is (dsn => dsn, username => username, etc), so right is to use:
connect_info => {
dsn => 'dbi:mysql:test',
user => 'user',
password => 'password',
AutoCommit => 1,
RaiseError => 1,
mysql_enable_utf8 => 1,
on_connect_do => [
'SET NAMES utf8',
],
}
In a typical Catalyst setup with Catalyst::View::TT and Catalyst::Model::DBIC::Schema you'll need several things for UTF-8 to work:
add Catalyst::Plugin::Unicode::Encoding to your Catalyst app
add encoding => 'UTF-8' to your app config
add ENCODING => 'utf-8' to your TT view config
add <meta http-equiv="Content-type" content="text/html; charset=UTF-8"/> to the <head> section of your html to satisfy old IEs which don't care about the Content-Type:text/html; charset=utf-8 http header set by Catalyst::Plugin::Unicode::Encoding
make sure your text editor saves your templates in UTF-8 if they include non ASCII characters
configure your DBIC model according to DBIx::Class::Manual::Cookbook#Using Unicode
if you use Catalyst::Authentication::Store::LDAP configure your LDAP stores to return UTF-8 by adding ldap_server_options => { raw => 'dn' }
According to Catalyst::Model::DBIC::Schema#connect_info:
The old arrayref style with hashrefs for DBI then DBIx::Class options is also supported.
But you are already using the 'new' style so you shouldn't nest the dbi attributes:
connect_info => {
dsn => 'dbi:mysql:test',
user => 'user',
password => 'password',
AutoCommit => 1,
RaiseError => 1,
mysql_enable_utf8 => 1,
on_connect_do => [
'SET NAMES utf8',
],
}
This advice assumes you have fairly up to date versions of DBIC and Catalyst.
This is not necessary: on_connect_do => [ 'SET NAMES utf8' ]
Ensure the table|column charsets are UTF-8 in your DB. You can achieve things that sometimes look right even when parts are broken. The DB must be saving the character data as UTF-8 if you expect the entire chain to work.
Ensure you're using and configuring Catalyst::Plugin::Unicode::Encoding in your Catalyst app. It did have serious-ish bugs in the not too distant past so get the newest.