Skip to content

Commit

Permalink
Merge pull request #91 from sebastianconcept/Improve-README
Browse files Browse the repository at this point in the history
First wave of improving the readme
  • Loading branch information
sebastianconcept committed Apr 7, 2022
2 parents 647bb6b + e2519b5 commit 1dbb668
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 157 deletions.
9 changes: 9 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
MIT License

Copyright (c) 2014 Sebastian Sastre

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
223 changes: 67 additions & 156 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,177 +1,88 @@
Mapless
=======
# Mapless

*Obscenely simple persistence for Smalltalk.* Mapless is a small framework for storing objects in a key->value fashion (i.e.: noSQL databases) without requiring any kind of mapping definitions.
Schema-less persistence for Smalltalk with support for multiple backends.

Currently supported backends are `MongoDB` and `Redis`.
<p align="left">
<a href="https://github.com/sebastianconcept/Mapless/releases" alt="Releases">
<img src="https://img.shields.io/github/v/tag/sebastianconcept/Mapless?label=release" /></a>
<img src="https://img.shields.io/badge/tests-178-green" />
<a href="https://github.com/sebastianconcept/Mapless/blob/develop/LICENSE" alt="License">
<img src="https://img.shields.io/github/license/sebastianconcept/Mapless" /></a>
<img src="https://img.shields.io/github/stars/sebastianconcept/Mapless?style=social" />
<img src="https://img.shields.io/github/forks/sebastianconcept/Mapless?style=social" />
</p>

**Current best version tagged as:** v0.5.1-alpha
___

## Loading
## Features
- Intuitive API for frictionless persistence.
- No need to create and maintain schemas.
- Composable.
- JSON friendly.
- No need to create accessors and mutators.
- Multiple backends to chose from.
- Enables smooth data migration/interoperation among backends.
- Via Redis PUB/SUB, scalable observer-pattern functionality across images.

By default `Mapless` will load `Memory`, `Mongo` and `Redis` backends:
## Ambition

Metacello new
baseline: 'Mapless';
repository: 'github://sebastianconcept/Mapless:master/src';
load.

## Design philosophy
Without loosing pragmatism, here is a notion used as source of inspiration for Mapless' design:

*There are no instVars...*
*There are no accessors...*
*There is no object-mapping impedance...*

*Only persistence.*

### Applicability

Mapless can be used for Model persistence, object oriented shared cache, observer pattern across images and network JSON communication.

### Motivation
> "I wanted to persist objects with *low friction* and *low maintenance* but *high scaling* and *availability* capabilities so Mapless is totally biased towards that. This framework is what I came up with after incorporating my experience with [Aggregate](https://github.com/sebastianconcept/Aggregate) in [airflowing](http://airflowing.com)." ~ [Sebastian Sastre](http://sebastiansastre.co)


### How does it look?

You can store and retrieve your Mapless models on MongoDB like a breeze.
#### Getting the connection

odb := MongoPool instance databaseAt: 'YourMongoDatabase'.

#### Create a new model

Just subclass MaplessModel with your app "business objects". Here we use a Task model:

task := Task new description: 'Try Mapless'; beIncomplete.

#### Save it

In Mapless you do things sending a #do: with a closure which Mapless uses to automatically discern which MongoDB database and collection has to be used. It also will know if it needs to do an insert or an update. As a bonus, you get the collection *and* the database created lazily.

Want to save something? Zero bureaucracy, just tell that to the model:
odb do:[ task save ].

The spirit of the project is to preserve developer *low-friction* when using persistence.
#### Fetching data

Getting all models of a given class:
odb do:[ Task findAll ].

Getting a specific model:
odb do: [ Task findId: 'c472099f-79b9-8ea2-d61f-e5df34b3ed06' ].
Mapless gives you performant state plasticity and high availability in a scale that goes beyond one Smalltalk image and without backend vendor locking nor object-mapping impedance mismatch.

For getting efficiently a (sub)group of models, you write your own Mapless class side getters. They should act on the MongoDB indices with the parameters of your query. You'll get your models in a breeze:
## Supported backends
1. MongoDB
2. Redis
3. Memory
4. PostgreSQL
5. UnQLite

odb do:[ Task findAtUsername: 'awesomeguy' ].
## Examples

#### Adding something to a model

You can use Mapless models pretending they are [prototypical](http://en.wikipedia.org/wiki/Prototype-based_programming) like in [Self](http://en.wikipedia.org/wiki/Self_(programming_language)) or [Javascript](http://en.wikipedia.org/wiki/JavaScript) objects. No instVars. No setters. No getters. All just works:
```Smalltalk
"Instanciates a mapless object."
genius := DummyPerson new
firstName: 'Aristotle';
yourself.
odb do: [
task
deadline: Date tomorrow;
notes: 'Wonder how it feels to use this thing, hmm...';
save ].
"Saves it."
repository save: genius.
```

#### Need composition?
```Smalltalk
"Loads one by known ID."
identified := repository findOne: DummyPerson atId: genius id.
```

Of course you do! The only thing you need is to save the children first
odb do: [ | anAlert |
anAlert := Alert new
duration: 24 hours;
message: 'You will miss the target!';
save.
task
alert: anAlert;
save ]
```Smalltalk
"Loads all instances of that class that were stored in that database."
allOrEmpty := repository findAll: DummyPerson.
```

This is what I like to call *low-maintenance*.
#### Navigating the object graph
```Smalltalk
"Query to load all the instances that match the condition."
someOrEmpty := repository findAll: DummyPerson where: [ :each | each lastName = 'Peterson' ].
```

Mapless embraces an aggressive *lazy approach*. When you query for models you get them. But if they are composed by other (sub)Mapless, they are going to be references and *only* reify into the proper Mapless model *if* you send them a message and you can do this with arbitrary depth:
```Smalltalk
"Conditionally loading the first matching instance."
oneOrNil := repository findOne: DummyPerson where: [ :each | each lastName = 'Peterson' ].
```

odb do: [ task alert message ].
## Installation

odb do: [ task alert class = MaplessReference ]. "<- true"
Open a Pharo workspace and evaluate:

odb do: [ task alert description ]. "<- 'You will miss the target!'"

#### Persisting different models

Say for your app you now need to store different kind of models, say `List` or `User` or anything else, how you typically proceed?

This is what happens to you with Mapless:

1. Create a subclass for them.
2. <del>Know what attributes they will need in advance.</del>
3. <del>Create its attributes' instVars.</del>
4. <del>Make accessors for them.</del>
5. <del>Elegantly map their type for each property so it all fits in the database.</del>
6. <del>Patiently re-map them every time you need to change its design.</del>
7. <del>Patiently migrate when you need production data to adopt the new design.</del>
8. Profit.

### How does it look? on Redis (this is work in prgress)

Using Redis supported Mapless models are interesting for:

1. **Caching**. It will hold the data in RAM so you get great response times.
2. **Shared caching**. If your app scales load horizontally, you can use Mapless on Redis as a cache shared across your service images.
3. **Reactivity**. Mapless uses Redis [PUB/SUB](http://redis.io/topics/pubsub) feature to observe/react models among N Pharo worker images enabling to use the [Observer pattern](https://en.wikipedia.org/wiki/Observer_pattern) with [horizontal scaling](http://en.wikipedia.org/wiki/Scalability#Horizontal_and_vertical_scaling).

Here is a workspace for Redis based Mapless models:

redis := RedisPool instance.
guy := DummyPerson new
firstName: 'John';
lastName: 'Q';
yourself.
redis do: [ :c | guy save].
redis do: [ :c | DummyPerson findAt: '38skolpqv0y9lazmk772hmsed' ].
redis do: [ :c | (DummyPerson findAt: '38skolpqv0y9lazmk772hmsed') lastName]

### State

This project code is considered beta. Check tests and please contribute!

### Contributions

...are *very* welcomed, send that push request and hopefully we can review it together.

### Direction?

1. We would love to see more unit tests and iterate the reactive features so you can ultimately get an a model in one image being observed in regard to one event by something in another image and that something reacting upon that event. Broadcast and multicast of events would be also a nice feat and not too hard to do. Have design suggestions? Let's have a chat! (find me in Pharo's Discord server)
2. For MongoDB-based Mapless, a nice feature would be to have <code>UserModel ensureIndex: '{ key1: 1, key2: -1 }'</code>

### *Pharo Smalltalk
Getting a fresh Pharo Smalltalk image and its virtual machine is as easy as running in your terminal:

wget -O- https://get.pharo.org | bash

_______

MIT License

Copyright (c) 2014 [Sebastian Sastre](http://sebastiansastre.co)
Metacello new
baseline: 'Mapless';
repository: 'github://sebastianconcept/Mapless:v0.5.0-alpha/src';
load

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
## Include as dependency
In BaselineOf or ConfigurationOf it can be added in this way:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
spec
baseline: 'Mapless'
with: [ spec
repository: 'github://sebastianconcept/Mapless:v0.5.0-alpha/src';
loads: #('Core' 'Mongo' 'Memory' 'Redis' 'UnQLite') ]

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
3 changes: 2 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
April 6, 2022
===================================
* Removed the docker directory with a replica set docker-compose.yml. This is better done when using your own or https://github.com/sebastianconcept/mongo-rs
* Removed the docker directory with a replica set docker-compose.yml. This is better done when using your own or https://github.com/sebastianconcept/mongo-rs.
* Starting to improve README based on https://github.com/sebastianconcept/Mapless/issues/87.

April 3, 2022
===================================
Expand Down

0 comments on commit 1dbb668

Please sign in to comment.