{"id":15107634,"url":"https://github.com/rko281/restoreforpharo","last_synced_at":"2025-10-23T02:31:21.809Z","repository":{"id":42665463,"uuid":"225833006","full_name":"rko281/ReStoreForPharo","owner":"rko281","description":"Relational database persistence for Pharo objects","archived":false,"fork":false,"pushed_at":"2024-07-08T13:24:04.000Z","size":1600,"stargazers_count":32,"open_issues_count":6,"forks_count":7,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-01-30T17:11:24.543Z","etag":null,"topics":["pharo"],"latest_commit_sha":null,"homepage":null,"language":"Smalltalk","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rko281.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-12-04T09:49:40.000Z","updated_at":"2024-09-05T12:42:02.000Z","dependencies_parsed_at":"2024-07-08T16:08:15.393Z","dependency_job_id":null,"html_url":"https://github.com/rko281/ReStoreForPharo","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rko281%2FReStoreForPharo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rko281%2FReStoreForPharo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rko281%2FReStoreForPharo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rko281%2FReStoreForPharo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rko281","download_url":"https://codeload.github.com/rko281/ReStoreForPharo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":237769067,"owners_count":19363250,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["pharo"],"created_at":"2024-09-25T21:40:35.667Z","updated_at":"2025-10-23T02:31:21.517Z","avatar_url":"https://github.com/rko281.png","language":"Smalltalk","funding_links":[],"categories":[],"sub_categories":[],"readme":"# What is ReStore?\nReStore is a framework enabling Pharo objects to be stored in and read from relational databases (SQLite, PostgreSQL etc.). ReStore aims to make relational persistency as simple as possible, creating and maintaining the database structure itself and providing access to stored objects via familiar Smalltalk messages. \n\n# Getting Started \n:warning: If you are using Pharo 11 or earlier please use the [pharo11](https://github.com/rko281/ReStoreForPharo/tree/pharo11) branch.\n\nTo load ReStore into your Pharo 12 image, evaluate:\n\n```smalltalk\nMetacello new\n    repository: 'github://rko281/ReStoreForPharo';\n    baseline: 'ReStore';\n    load: 'all'\n```\n\nThis will load ReStore with support for SQLite and MySQL (via [pharo-rdbms](https://github.com/pharo-rdbms/Pharo-SQLite3)), PostgreSQL (via [P3](https://github.com/svenvc/P3)), plus the example classes we'll discuss next.\n\n# A Simple Example\nLet's consider a simple customer order application with the following classes (found in the `SSW ReStore Examples` package):\n\n```smalltalk\nObject subclass: #Customer\n\tinstanceVariableNames: 'firstName surname emailAddress dateOfBirth address orders'\n\nObject subclass: #CustomerOrder\n\tinstanceVariableNames: 'orderDate customer items totalPrice'\n\t\nObject subclass: #CustomerOrderItem\n\tinstanceVariableNames: 'order product quantity'\n    \nObject subclass: #Product\n\tinstanceVariableNames: 'name description price'\n```\n\nThe first step in adding persistency with ReStore is to define the structure of the classes. This is done with the class method reStoreDefinition - for the Customer class this is:\n\n```smalltalk\nreStoreDefinition\n\n\t^super reStoreDefinition\n\t\tdefine: #surname as: (String maxSize: 100);\n\t\tdefine: #firstName as: (String maxSize: 100);\n\t\tdefine: #emailAddress as: (String maxSize: 100);\n\t\tdefine: #dateOfBirth as: Date;\n\t\tdefine: #address as: Address dependent;\n\t\tdefine: #orders as: (OrderedCollection of: CustomerOrder dependent owner: #customer);\n\t\tyourself.\n```\n\nA couple of things worth highlighting here:\n - `define: #address as: Address dependent` - adding `dependent` to a definition means that object is dependent on the originating object for its existence. In this case the customer's address object is dependent on the owning customer - this means any changes to the address will be saved along with the customer, and the address will be deleted if the customer is deleted. \n - `define: #orders as: (OrderedCollection of: CustomerOrder dependent owner: #customer)` - this is an example of an owned collection definition. An owned collection is one where the elements of the collection contain a reference to the owner of the collection. In this case instances of Order refer to their owning customer via their customer instance variable. \n\n# Creating the Database\nWith ReStore definitions created for all classes we can now connect to the database and create the database structure:\n\n```smalltalk\nReStore\n\tconnection: (SSWSQLite3Connection on: (Smalltalk imageDirectory / 'test.db') fullName);\n\tconnect;\n\taddClasses: {Customer. Address. CustomerOrder. CustomerOrderItem. Product};\n\tsynchronizeAllClasses.\n```\n\n - for simplicity we're using [SQLite](https://www.sqlite.org/); please ensure the SQLite3 library/DLL is available to your image. If you'd rather use PostgreSQL you'll need to specify a connection similar to this:   \n `(SSWP3Connection new url: 'psql://user:pwd@192.168.1.234:5432/database')`   \n or for MySQL you'll need a connection similar to this:    \n `(SSWMySQLConnection new connectionSpec: (MySQLDriverSpec new db: 'database'; host: '192.168.1.234'; port: 3306; user: 'user'; password: 'pwd'; yourself); yourself)`\n- `synchronizeAllClasses` prompts ReStore to create the necessary database tables for the classes `Customer`, `Address`, `Order` and `Product`. If you subsequently modify these classes (and their ReStore definitions) you can run `synchronizeAllClasses` again to prompt ReStore to automatically update the table definitions (add or remove columns from the tables) to match the updated class definitions.\n\n# Storing Objects\nWith the database setup we can now create and persist objects using the `store` message:\n\n```smalltalk\nCustomer new\n\tfirstName: 'John';\n\tsurname: 'Smith';\n\taddress: (Address new country: 'UK'; yourself);\n\tstore.\n\nCustomer new\n\tfirstName: 'Jen';\n\tsurname: 'Smith';\n\taddress: (Address new country: 'France'; yourself);\n\tstore.\n````\n\n# Reading Objects\nNow we have some objects in the database we need a way to find and read them. ReStore does this via the message `storedInstances` which gives a virtual collection of instances of a particular class stored in the database. The virtual collection can then be queried using the familiar Smalltalk collection enumeration messages `select:`, `detect:` etc.\n\n```smalltalk\n\"All Smiths\"\nCustomer storedInstances select: [ :each | each surname = 'Smith'].\n\n\"John Smith\"\nCustomer storedInstances select: [ :each | (each firstName = 'John') \u0026 (each surname = 'Smith')].\n\"alternatively:\"\nCustomer storedInstances select: [ :each | each fullName = 'John Smith'].\n\n\"Customers in France\"\nCustomer storedInstances select: [ :each | each address country = 'France'].\n```\n\nReStore analyses the block argument to `select:`, `detect:` etc., translating this to SQL which is used to query the database. In this way required objects can be efficiently located without having to read all objects into the image. If all objects are required this can be done by converting the `storedInstances` virtual collection to a real collection:\n\n```smalltalk\nCustomer storedInstances asOrderedCollection\n```\n\n# Updating Objects\nPersisting changes to objects is also done using the `store` message:\n\n```smalltalk\n\"Updating a Customer\"\njohnSmith := Customer storedInstances detect: [ :each | each fullName = 'John Smith'].\njohnSmith dateOfBirth: (Date newDay: 1 monthIndex: 2 year: 1983).\njohnSmith address postcode: 'W1 1AA'.\njohnSmith store.\n\n\"Check it:\"\nCustomer storedInstances detect: [ :each | each address postcode = 'W1 1AA'].\n\n\"Creating an Order - first we need a product\"\nwidget := Product new name: 'Widget'; price: 2.5s2; store; yourself.\n\njohnSmith \n    addOrder: \n        (CustomerOrder new \n            orderDate: Date today;\n            addItem: \n\t    \t(CustomerOrderItem new\n\t\t\tproduct: widget;\n\t\t\tquantity: 4;\n\t\t\tyourself);\n            yourself);\n    store.\n\n\"Check it:\"\nCustomer storedInstances detect: [ :each | each orders anySatisfy: [ :order | order totalPrice = 10]].\n```\n\n# Next Steps\nThis is just a sample of what ReStore can do, with an empahsis on simplicity. ReStore also supports more sophisticated usage patterns including:\n - transactions with automatic change-tracking\n - multi-user update clash detection and resolution\n - persistent class hierarchies\n - multiple, independent ReStore instances, including per-process and per-session support\n - query-by-example (template queries)\n - customisable Smalltalk-to-SQL conversion\n \nA complete user manual can be found in the [Documentation folder](Documentation) (or [view online here](https://drive.google.com/file/d/1nDzLyokK8ZUAJuCh4J_H8MT_f3ZogMxy/view?usp=sharing)). Please also see the included SUnits for more examples. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frko281%2Frestoreforpharo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frko281%2Frestoreforpharo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frko281%2Frestoreforpharo/lists"}