{"id":16513806,"url":"https://github.com/wintercounter/chambr","last_synced_at":"2025-08-27T04:10:04.340Z","repository":{"id":148932703,"uuid":"50986969","full_name":"wintercounter/Chambr","owner":"wintercounter","description":"A new method to handle data interactions.","archived":false,"fork":false,"pushed_at":"2016-12-21T15:31:20.000Z","size":3760,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-02T06:45:07.546Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wintercounter.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2016-02-03T08:56:23.000Z","updated_at":"2018-10-17T16:03:36.000Z","dependencies_parsed_at":"2023-09-02T05:35:29.378Z","dependency_job_id":null,"html_url":"https://github.com/wintercounter/Chambr","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/wintercounter%2FChambr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wintercounter%2FChambr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wintercounter%2FChambr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wintercounter%2FChambr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wintercounter","download_url":"https://codeload.github.com/wintercounter/Chambr/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241494185,"owners_count":19971871,"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":[],"created_at":"2024-10-11T16:10:21.800Z","updated_at":"2025-03-02T11:15:38.765Z","avatar_url":"https://github.com/wintercounter.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Chambr\nThis product is in pre-alpha stage. It doesn't have much documentation and/or support. Use at your own risk.\n\n##The story behind Chambr\n\nI was always searching a better way of **storing/handling different data on the frontend**. Did many experiments about finding better **model layer** for my apps. They always had improvements at certain fields while others failed to achieve some with the chosen architecture.\n\nThen *riot.js* came into my sight. I was playing with it for a while and still using it in production,\nbut faced again the same issue: *How to correctly interact with models and data?*\nI'm still not sure **Chambr** is the right way, but feels so good to use so far.\n\n### The data as the model itself\n\nMy main goal was to simply have my **data** and **model API** available with less coding at the view layer.\nTo achieve this I tried to make my data to become the model (kinda) also.\n\n    let data = SomeChambrObject // [1,2,3]\n    data.add(4)  // [1,2,3,4]\n    data.moreComplexApiMethod('Thomas', true)\n    data.sendWithAjaxToServer().then(function(){\n    \tconsole.log(`I'm done!`)\n    })\n\n### Observable\n\nI also want to know when my data is updated in any way, because I maybe need to do some rendering. The whole model is an observable with custom events. Also the model data itself is observable, so I know when the data is changed.\n\nFor example this is what I had to do with riot (yep, that's all):\n\n    this.data.on('update', this.update)\n    \n### ShortObject\n\nWhat I did here is extracting the full model class into somewhat I call ***ShortObject***, so this model:\n\n    class MyModel extends ModelAbstract {\n        get total(){\n            return this.data.length\n        }\n        constructor(){\n            super(new Arr([1,2]))\n        }\n        create(){}\n        read(){}\n        update(){}\n        delete(){}\n    }\n\nBecomes this ***ShortObject*** through Chambr:\n\n    {\n        0      : 1,\n        1      : 2,\n        total  : 2,\n        create : function(){},\n        read   : function(){},\n        update : function(){},\n        delete : function(){}\n    }\n\n###How should I iterate through them?\nJust as usual. Everything what is not the actual data is non-enumerable.\n\n###Why not having the reference of the *Model Instance* at the view and simply call the method?\n\n1. I already showed that this way our data is the model itself.\n2. This requires another chapter in this story.\n\n### Separation from the GUI\n\nFrontend developers *Holy Grail* was always to achieve **60 FPS rendering**.\nCorrect DOM structure, correct CSS is only the half of this coin. It also matters what is your code doing behind the GUI, because many operations can block your DOM or hang the browsers main thread.\nSending requests, fetching responses, manipulate data, all these may have negative impact on our 60 FPS goal.\nI wanted to make the whole client layer as light as possible.\nFirst I was experimenting with **Workers**. I wanted to move every **non-GUI code into Worker**.\n\n### Transporting data between layers\n\nFirst I created my ***Highway.js*** library. This is a ***Pub/Sub transport layer***. It was supposed to be the *Gateway* between the main thread and the Worker. Since it's first release I've changed my mind and implemented Workers as an adapter only so it can support any kind of transport, just needs a proper adapter.\n\n### Main thread for GUI only\n\nSo on the top of **Highway** I've refactored **Chambr**. It wasn't an easy task, I've faced many pitfalls, but finally came across.\n\nAt this point *the model lives in the Worker*, on the GUI only the **ShortObject** lives, so the client side stays super-lightweight.\n\n### Always failing\n\nAwesome, works in Chrome, but what about other browsers. Workers are supported IE 10+ and it worked perfectly.\nHowever I've felt into some pitfalls again, for example there is no any permanent storage solution in workers (at least not cross-browser one).\nNo Local/SessionStorage, no Cookies, no IndexedDB, no WebSQL. Started to think about what should I do. Sync all Storage across Chambr? Store permanent data on the client side and constantly move/update data between every transport? Hell no!!!\nToo much overhead. Too much work. Not safe. And of course it's a huge bug factory.\n\n### An even better solution was born\n\nMove it to Node. Yes, Node as my Frontend's Backend :D\n\n- Best permanent storage ever: no limits, not just IndexedDB or localStorage but anything!\n- ES6 out of the box (Node 6)\n- More lightweight client code.\n\nWith this my served frontend code is 98% GUI related only! Nothing more. And with *riot* this have much more fun!\nOnly the view layer is present at the client. No need for controllers because if you build your application right,\nriot-router simply takes care of them. No more MVC or MV* just MV.\n\n### So what Chambr is?\n\nIt is a Data storage solution where:\n\n- Your model and data manipulation is separated from your main thread. (You're still able to run everything on the main thread of course).\n- Your model can live in any other JavaScript based environment (Worker, Node).\n- Your data is alive!\n- Helps to make your view much more simple.\n- Helps build borders around your codebase (strict model API, developers cannot do magic).\n\nThis was the main part I think. Now I will answer some question that may come into your mind.\n\n\n----------\n\n\n##Q/A\n\n### Why is this better than Flux and other modern data flow solutions?\n\nActually, I never said that. **Flux** is a great thing. It helps you to maintain your application in a much cleaner way. You can still take advantage of Flux with Chambr, you can make your application work with one flow direction.\n\nJust one tiny difference (which is my opinion): it is better if my model has it's own state and I handle this where I represent the model data - at a view component. Of course there are states which need to be global(ish), for example showing an indicator every time I'm making an AJAX call, but these cases have their own simple solutions.\n\n### Do you have any proof of concept?\nI have, unfortunately it's not public, but I'm working on my new personal site built on Chambr/Node.\n\n### Can I try this in production?\nYou can, but(!) there are a few tests only, there are some known issues in rare edge cases, and there is no documentation or support. There are lot of work to be done.\n\n### Are you nuts?\nIn a good sense I am. Why? Tell me where I did wrong, tell me, what is your opinion, maybe you're right and this project is totally useless.\n\n### Do you need help?\nYes, I appreciate any kind of help.\n\n### Do you have example?\nYes. I've made the TodoMVC app with Riot+Chambr: link.\nIt doesn't fulfill the LocalStorage requirement for the reasons I've mentioned before, it runs in a Worker.\nI'm also willing to make more examples and live demos if the community wants.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwintercounter%2Fchambr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwintercounter%2Fchambr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwintercounter%2Fchambr/lists"}