electronic.alchemy :: ModelDemo
electronic.alchemy
where the past meets the future
pike > Fins > Developer > ModelDemo

ModelDemo

Created by hww3. Last updated by hww3, 13 years ago. Version #17.

Below is a simple example of the steps for setting up a simple database driven application using Fins. Commands that should be typed are listed in bold.

1. Download Fins, unpack and change into the Fins directory.

hera:/tmp hww3$ curl -o Fins.zip http://hww3.riverweb.com/dist/Fins/Fins-0.2.zip
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 760k 100 760k 0 0 83665 0 0:00:09 0:00:09 ::-- 98487
hera:/tmp hww3$ unzip Fins.zip
Archive: Fins.zip
hera:/tmp hww3$ cd Fins-0.2/

2. Create a new Fins application, called "widgetco".

hera:/tmp/Fins-0.2 hww3$ pike -Mlib -x fins create widgetco
/18:35:17 INFO - CreateApplication module loading
18:35:17 INFO - CreateApplication module running.
18:35:17 INFO - Creating application widgetco in /private/tmp/Fins-0.2.
18:35:17 INFO - Be sure to edit config/*.cfg to specify the application's datasource

3. Create a new database and grant a user permission to that new database.

hera:/tmp/Fins-0.2 hww3$ mysqladmin -p create -uroot foo
Enter password:
hera:/tmp/Fins-0.2 hww3$ mysql -uroot foo -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or g.
Your MySQL connection id is 18 to server version: 5.0.27-standard

Type 'help;' or 'h' for help. Type 'c' to clear the buffer. mysql> grant all on foo.* to foo identified by 'foo4u'; Query OK, 0 rows affected (0.01 sec)

4. We want to store records about Manufacturers and Widgets in our database, so let's create those tables in our new database.

mysql> create table widgets(
id int(11) not null auto_increment primary key,
name char(32),
on_hand int(11) ,
manufacturer_id int(11));
Query OK, 0 rows affected (0.00 sec)

mysql> create table manufacturers( id int(11) not null auto_increment primary key, name char(32), phone_number char(16) ); Query OK, 0 rows affected (0.00 sec)

mysql> quit Bye

5. Edit the configuration file so that the model knows what database to connect to. Set the sqlurl of the database you just created in the "datasource" configuration value of the "model" section so that fins can connect to your new database.

hera:/tmp/Fins-0.2 hww3$ vi widgetco/config/dev.cfg

6. Now, let's set up the FINS_HOME environment variable so that we can use the scripts in the application directory. You could type the full commands out, but using the scripts will save typing.

hera:/tmp/Fins-0.2 hww3$ cd widgetco
hera:/tmp/Fins-0.2/widgetco hww3$ export FINS_HOME=/tmp/Fins-0.2

7. Let's start the app up, just to see what happens. You should get a message indicating the application is ready for business. If you don't or you get an error, double check your typing on the previous steps. You can stop the application by typing control-c.

hera:/tmp/Fins-0.2/widgetco hww3$ bin/start.sh
18:43:08 INFO - FinServe starting on port 8080
18:43:08 INFO - Starting Session Manager.
18:43:08 INFO - FinServe loading application widgetco using configuration dev
18:43:08 INFO - Starting Cache.
18:43:08 INFO - loading model from model
18:43:08 INFO - Application widgetco loaded.
18:43:08 INFO - Application ready for business.
<CTRL-C>
hera:/tmp/Fins-0.2/widgetco hww3$

8. Let's tell fins about the new manufacturer and widget tables we've created in the database.

hera:/tmp/Fins-0.2/widgetco hww3$ bin/fins.sh model add manufacturers widgets

9. Now that our model is connected to the database and we've created some basic data model mapping definitions, let's add some new records, using these newly created mapping definitions:

hera:/tmp/Fins-0.2/widgetco hww3$ bin/start.sh --hilfe
18:52:33 INFO - Application widgetco loaded.
Starting interactive interpreter...
Fins 0.4 running Pike v7.8 release 352 / Hilfe v3.5 (Incremental Pike Frontend)
> object m = widgetco.Objects.Manufacturer();
> m["name"] = "Spacely Sprockets";
(2) Result: "Spacely Sprockets"
> m->save();
(3) Result: 0
> object m = widgetco.Objects.Manufacturer();
> m["name"] = "Cogswell Cogs";
(4) Result: "Cogswell Cogs"
> __m->save();
(5) Result: 0
> object w = widgetco.Objects.Widget();
> w["name"] = "SuperCog 2000";
(6) Result: "SuperCog 2000"
> w["Manufacturer"] = m;
(7) Result: Manufacturer(id=2)
> w["on_hand"] = 12;
(8) Result: 12
> w->save();
(9) Result: 0
> indices(w);
(10) Result: ({ /* 4 elements */
"on_hand",
"Manufacturer",
"name",
"id"
})
> w["Manufacturer"];
(1) Result: Manufacturer(id=2/2)
> w["Manufactuer"]["name"];
(11) Result: "Cogswell Cogs"

10. That's all very nice, but the description of the object is a little unfriendly. let's fix that by setting an "alternate key" field. setting an alternate key field allows us to query by that field easily, and the value of the alternate key is used in debug displays. Note that Fins doesn't enforce uniqueness of the alternate key; you should use database level techniques to do this (for example, using the UNIQUE key type).

To set an alternate key field, select the field you'd like to use, and edit that object type's model definition (in this example, modules/widgetco.pmod/Model.pmod/Manufacturer.pike:

hera:/tmp/Fins-0.2/widgetco hww3$ vi modules/widgetco.pmod/DataMappings.pmod/Manufacturer.pike

11. To the Manufacturer.pike class, we want to add a post_define() method, and set the alternate key field.

void post_define() { set_alternate_key("name"); }

12. Now, let's try a little of that again:

hera:/tmp/Fins-0.2/widgetco hww3$ bin/start.sh --hilfe
20:24:22 INFO - FinServe starting on port 8080
...
Starting interactive interpreter...
Fins 0.4 running Pike v7.8 release 352/ Hilfe v3.5 (Incremental Pike Frontend)
> Fins.Model.find.manufacturers_by_id(1);
(1) Result: Manufacturer(name=Spacely Sprockets)
> Fins.Model.find.manufacturers_by_alternate("Cogswell Cogs");
(4) Result: Manufacturer(name=Cogswell Cogs)

13. Next, let's add some validation to our model. Perhaps we should make sure that phone numbers are provided in a particular format. We can use validation to perform this task. Add the validation by editing modules/widgetco.pmod/DataMappings.pmod/Manufacturer.pike. Add the following lines of code and save the file:

// our simple phone number regexp. 
constant pnf = "[0-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]"; 
void validate(mapping changes, object errors, object i) 
{ 
  if(changes->phone_number) 
  { 
    object r = Regexp.SimpleRegexp(pnf); 
    if(!r->match(changes->phone_number))  // if we're not a valid phone number, complain.
      errors->add("phone_number", "must be in the format nnn-nnn-nnnn."); 
  } 
}

14. Now, let's test the validation:

hera:/tmp/Fins-0.2/widgetco hww3$ bin/start.sh --hilfe
20:24:22 INFO - FinServe starting on port 8080
...
Starting interactive interpreter...
Fins 0.4 running Pike v7.8 release 352 / Hilfe v3.5 (Incremental Pike Frontend)
> object m = Fins.Model.find.manufacturers_by_id(1);
(1) Result: Manufacturer(name=Spacely Sprockets)
> m["phone_number"] = "8885551212";
Data Validation Error
Phone number must be in the format nnn-nnn-nnnn.
lib/Fins.pmod/Model.pmod/DataObject.pike:664:
wooga.Model.Manufacturer()->set("phone_number","1234567890",UNDEFINED,Manufac
turer(name=Spacely Sprockets))
lib/Fins.pmod/Model.pmod/DataObjectInstance.pike:223:
Manufacturer(name=Spacely Sprockets)->`[]=("phone_number","1234567890")
>

15. Finally, a web interface for adding and editing objects would be nice to have. We can use the ScaffoldController to do some of the setup work for us.

Start by editing the master controller, classes/controller.pike, and add the following code, in order to make our widget and manufacturer controllers available off the root of the application:

object manufacturers; 
object widgets; 
void start() 
{ 
  manufacturers = load_controller("mfg_controller"); 
  widgets = load_controller("widget_controller"); 
}

16. Then, add the files classes/mfg_controller.pike and classes/widget_controller.pike that will provide our data editors. In the first file, classes/mfg_controller.pike, add the following lines:

inherit Fins.ScaffoldController;

// What data type shall we build an editor scaffolding for? string model_component = "Manufacturer";

In the second file, classes/widget_controller.pike, add the following lines:

inherit Fins.ScaffoldController;

// What data type shall we build an editor scaffolding for? string model_component = "Widget";

17. Then, simply restart and point your web browser to /manufacturers or /widgets to edit data in your model. You'll notice that validation code will be run and any errors will be passed up to the client in a friendly way.

Not categorized | RSS Feed | BackLinks

comments powered by Disqus