{"id":15502598,"url":"https://github.com/itmammoth/tamotsu","last_synced_at":"2025-09-04T21:33:11.685Z","repository":{"id":18827788,"uuid":"84649558","full_name":"itmammoth/Tamotsu","owner":"itmammoth","description":"Object-Spreadsheet Mapping for Google Apps Script","archived":false,"fork":false,"pushed_at":"2022-06-17T12:04:38.000Z","size":88,"stargazers_count":127,"open_issues_count":6,"forks_count":29,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-12-24T03:13:00.619Z","etag":null,"topics":["google-apps-script","javascript","spreadsheet"],"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/itmammoth.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}},"created_at":"2017-03-11T12:49:19.000Z","updated_at":"2024-12-02T22:10:57.000Z","dependencies_parsed_at":"2022-09-16T10:50:27.441Z","dependency_job_id":null,"html_url":"https://github.com/itmammoth/Tamotsu","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itmammoth%2FTamotsu","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itmammoth%2FTamotsu/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itmammoth%2FTamotsu/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itmammoth%2FTamotsu/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/itmammoth","download_url":"https://codeload.github.com/itmammoth/Tamotsu/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":231996827,"owners_count":18457818,"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":["google-apps-script","javascript","spreadsheet"],"created_at":"2024-10-02T09:10:27.380Z","updated_at":"2024-12-31T14:37:23.166Z","avatar_url":"https://github.com/itmammoth.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tamotsu\n\nTamotsu(保つ) is an object-spreadsheet mapping library like ActiveRecord for google apps script.\n\n![Agents](https://raw.githubusercontent.com/itmammoth/Tamotsu/master/images/Agents001.png \"Agents\")\n\n```javascript\nTamotsu.initialize();\nvar Agent = Tamotsu.Table.define({ sheetName: 'Agents' });\n\nvar agent = Agent.find(1);\nLogger.log(agent); //=\u003e  {#=1.0, First Name=Charles, Last Name=Bartowski, Gender=Male, Salary=100.0, ...}\n```\n\n# Installation\n\nTamotsu is made available as a script library. This is how you add it to your project:\n\n1. Select \"Resources\" \u003e \"Libraries...\" in the Google Apps Script editor.\n1. Enter the project key (`1OiJIgWlrg_DFHFYX_SoaEzhFJPCmwbbfEHEqYEfLEEhKRloTNVJ-3U4s`) in the \"Find a Library\" field, and choose \"Select\".\n1. Select the highest version number, and choose Tamotsu as the identifier. (Do not turn on Development Mode unless you know what you are doing. The development version may not work.)\n1. Press Save. You can now use the Tamotsu library in your code.\n\n# Usage\n\nWhen your spreadsheet is the following sheet.\n\n![Agents](https://raw.githubusercontent.com/itmammoth/Tamotsu/master/images/Agents001.png \"Agents\")\n\n__NOTICE__: Column `#` is used as id column.\n\n```javascript\n// You have to invoke this first.\nTamotsu.initialize();\n// Define your table class\nvar Agent = Tamotsu.Table.define({ sheetName: 'Agents' }, {\n  fullName: function() {\n    return [this['First Name'], this['Last Name']].join(' ');\n  },\n});\n\nAgent.first(); //=\u003e {#=1.0, First Name=Charles, ...}\nAgent.find(2); //=\u003e {#=2.0, First Name=Sarah, ...}\nAgent.last();  //=\u003e {#=3.0, First Name=John, ...}\n\nAgent.find(1).fullName(); //=\u003e \"Charles Bartowski\"\n\nAgent.where({ Gender: 'Male' })\n     .order('Salary DESC')\n     .all();  //=\u003e [{#=3.0, First Name=John, ...}, {#=1.0, First Name=Charles}]\n\nAgent.sum('Salary');  //=\u003e 600\n\nAgent.create({\n  'First Name': 'Morgan',\n  'Last Name': 'Grimes',\n  'Gender': 'Male',\n  'Salary': 50,\n}); //=\u003e {#=4.0, First Name=Morgan, ...}\n    //   and the data will be appended to the sheet.\n\nvar agent = Agent.where(function(agent) { return agent['Salary'] \u003e 150; })\n                 .first(); //=\u003e {#=2.0, First Name=Sarah, ...}\nagent['Salary'] = 250;\nagent.save(); //=\u003e The salary on the sheet will be updated.\n```\n\n# API\n\n## Tamotsu\n\n### `Tamotsu.initialize([Spreadsheet])`\n\n|Param                  |Type       |Description|\n|:----------------------|:----------|:----------|\n|Spreadsheet (optional) |Spreadsheet|A spreadsheet object you will handle. If nothing is given, it uses `SpreadsheetApp.getActive()` instead.|\n\nYou have to invoke this method before using `Tamotsu.Table`.\n\n### `Tamotsu.onInitialized(callback)`\n\n|Param   |Type    |Description|\n|:-------|:-------|:----------|\n|callback|function|A function that is to be added to the callback list.|\n\nYou can isolate table definitions from main code with this method.\n\n```javascript\nvar Agent;\nTamotsu.onInitialized(function() {\n  Agent = Tamotsu.Table.define({ sheetName: 'Agents' });\n});\n\nfunction main() {\n  Tamotsu.initialize();\n  Logger.log(Agent.first());\n}\n```\n\n## Tamotsu.Table\n\n### `Tamotsu.Table.define(classProperties[, instanceProperties])`\n\nReturns a table class extended from `Tamotsu.Table`.\n\n|Param                        |Type  |Description|\n|:----------------------------|:-----|:----------|\n|classProperties              |object|An object that is to be defined on your table class as class properties|\n|instanceProperties (optional)|object|An object that is to be defined on your table class's instance as instance properties|\n\n#### Prepared `classProperties`\n|Key                      |Type   |Description|\n|:------------------------|:------|:----------|\n|__sheetName__ (necessary)|string |A sheet name you will use as a table|\n|idColumn                 |string |Id column used as a key (Default: '#')|\n|autoIncrement            |boolean|If true, id is automatically incremented when its value is blank (Default: true)|\n|rowShift                 |number |Number of rows before the start of the table  (Default: 0)|\n|columnShift              |number |Number of columns before the start of the table (Default: 0)|\n\n#### Prepared `instanceProperties`\n|Key     |Type    |Description|\n|:-------|:-------|:----------|\n|validate|function|A validation function that is to be called before `save`.\u003cbr\u003eThe callback will be given `on` param.|\n```javascript\n...\nvalidate: function(on) {\n  // on === 'create' or 'update'\n  if (on === 'create') {\n    if (!this['First Name']) this.errors['First Name'] = \"can't be blank\";\n  } else if (on === 'update') {\n    if (this['Salary'] \u003e 500) this.errors['Salary'] = \"is too much\";\n  }\n  if (this['Gender'] !== 'Male' \u0026\u0026 this['Gender'] !== 'Female') {\n    this.errors['Gender'] = \"must be 'Male' or 'Female'\";\n  }\n},\n...\n```\n\n_An example of Tamotsu.Table.define:_\n\n```javascript\nTamotsu.initialize();\nvar Agent = Tamotsu.Table.define({\n  // classProperties\n  sheetName: 'Agents2',\n  idColumn: 'Agent No', // the column used as id\n  aClassProp: 'A class property',\n  rowShift: 1,\n  columnShift: 0,\n}, {\n  // instanceProperties\n  isMale: function() {\n    return this['Gender'] === 'Male';\n  },\n  isFemale: function() {\n    return this['Gender'] === 'Female';\n  },\n  validate: function(on) {\n    if (!this['First Name']) {\n      this.errors['First Name'] = \"can't be blank\";\n    }\n  },\n});\n\nLogger.log(Agent.aClassProp); //=\u003e 'A class property'\nvar agent = Agent.where({ Gender: 'Male' }).first();\nLogger.log(agent.isMale()); //=\u003e true\nLogger.log(agent.isFemale()); //=\u003e false\n\nLogger.log(Agent.create({ 'First Name': '' })); //=\u003e false\nLogger.log(Agent.create({ 'First Name': 'James' })); //=\u003e {Agent No=4.0, First Name=James, ...}\n```\n\n### `Tamotsu.Table.first()`\n\nRetunrs the first model in the table.\n\n### `Tamotsu.Table.last()`\n\nRetunrs the last model in the table.\n\n### `Tamotsu.Table.find(id)`\n\nReturns the model found by the given id.\n\n|Param|Type  |Description|\n|:----|:-----|:----------|\n|id   |any   |An id to find|\n\n### `Tamotsu.Table.all()`\n\nReturns the all model in the table.\n\n### `Tamotsu.Table.pluck(column)`\n\nReturns an array of the given column's values.\n\n|Param |Type  |Description|\n|:-----|:-----|:----------|\n|column|string|a column name to pluck|\n```javascript\nvar firstNames = Agent.pluck('First Name');\nLogger.log(firstNames); //=\u003e ['Charles', 'Sarah', 'John']\n```\n\n### `Tamotsu.Table.sum(column)`\n\nReturns the summed up number of the given column's values.\n\n|Param |Type  |Description|\n|:-----|:-----|:----------|\n|column|string|a column name to sum up|\n```javascript\nvar total = Agent.sum('Salary');\nLogger.log(total); //=\u003e 600\n```\n\n### `Tamotsu.Table.max(column)`\n\nReturns the maximum value of the given column's values.\n\n|Param |Type  |Description|\n|:-----|:-----|:----------|\n|column|string|a column name to examine|\n```javascript\nvar max = Agent.max('Salary');\nLogger.log(max); //=\u003e 300\n```\n\n### `Tamotsu.Table.min(column)`\n\nReturns the minimum value of the given column's values.\n\n|Param |Type  |Description|\n|:-----|:-----|:----------|\n|column|string|a column name to examine|\n```javascript\nvar min = Agent.min('Salary');\nLogger.log(min); //=\u003e 100\n```\n\n### `Tamotsu.Table.where(conditions)`\n\nReturns the relation object which meets the given conditions.\n\n|Param     |Type              |Description|\n|:---------|:-----------------|:----------|\n|conditions|object or function|a condition object or predicate function|\n\n```javascript\n// Object condition\nvar men = Agent.where({ Gender: 'Male' }).all();\nLogger.log(men); //=\u003e [{#=1.0, First Name=Charles...}, {#=3.0, First Name=John...}\n\n// Function condition\nvar highPay = Agent.where(function(agent) { return agent['Salary'] \u003e 150; })\n                   .all();\nLogger.log(highPay); //=\u003e [{#=2.0, First Name=Sarah...}, {#=3.0, First Name=John...}]\n```\n\nFor sure `where` returns not models but a relation object that is to be chained with other scope functions, so you can get the records in such an elegant way.\n\n```javascript\nAgent.where(condition1).where(condition2).order(comparator).all();\n```\n\n### `Tamotsu.Table.order(comparator)`\n\nReturns the relation object which meets the given sort order.\n\n|Param     |Type              |Description|\n|:---------|:-----------------|:----------|\n|comparator|string or function|a column name or comparator function|\n\n```javascript\n// Object comparator\nvar asc = Agent.order('Salary').all();\nLogger.log(asc); //=\u003e [{#=1.0, Salary=100...}, {#=3.0, Salary=200...}, {#=2.0, Salary=300}]\n\n// Supports ASC/DESC\nvar desc = Agent.order('Salary DESC').all();\nLogger.log(desc); //=\u003e [{#=2.0, Salary=300...}, {#=3.0, Salary=200...}, {#=1.0, Salary=100}]\n\n// Function comparator\nAgent.order(function(agent1, agent2) {\n  // complex comparator\n  return agent2['First Name'].length - agent1['First Name'].length;\n}).all();\n\n```\n\n### `Tamotsu.Table.create(modelOrAttributes)`\n\nCreates new record in the spreadsheet with the given model or attributes and returns the new model if created successfully.\n\n|Param              |Type           |Description|\n|:------------------|:--------------|:----------|\n|modelOrAttributes|model or object|Tamotsu.Table model or attribtues object|\n\n```javascript\nvar agent = new Agent({\n  'First Name': 'Morgan',\n  'Last Name': 'Grimes',\n  'Gender': 'Male',\n  'Salary': 50,\n});\nAgent.create(agent); //=\u003e {#=4.0, First Name=Morgan, ...}\n                     // and the data will be appended to the sheet.\n// or\nAgent.create({\n  'First Name': 'Morgan',\n  'Last Name': 'Grimes',\n  'Gender': 'Male',\n  'Salary': 50,\n});\n```\n\n### `Tamotsu.Table.batchCreate(modelOrAttributesArray)`\n\nCreates new recoreds in the spreadsheet with the given models or attributes and returns the new models if created successfully.\n**Better performace than `create` one by one**\n\n|Param              |Type                    |Description|\n|:------------------|:-----------------------|:----------|\n|modelsOrAttributes |array of model or object|Array of Tamotsu.Table model or attribtues object|\n\n```javascript\nvar agents = [\n  new Agent({\n    'First Name': 'Morgan',\n    'Last Name': 'Grimes',\n    'Gender': 'Male',\n    'Salary': 50,\n  }),\n  new Agent({\n    'First Name': 'Bryce',\n    'Last Name': 'Larkin',\n    'Gender': 'Male',\n    'Salary': 400,\n  }),\n];\nAgent.batchCreate(agents);  //=\u003e [{#=4.0, First Name=Morgan, ...}, {#=5.0, First Name=Bryce, ...}]\n                            // and the data will be appended to the sheet.\n// or\nAgent.batchCreate([\n  {\n    'First Name': 'Morgan',\n    'Last Name': 'Grimes',\n    'Gender': 'Male',\n    'Salary': 50,\n  },\n  {\n    'First Name': 'Bryce',\n    'Last Name': 'Larkin',\n    'Gender': 'Male',\n    'Salary': 400,\n  },\n]);\n```\n\n### `Tamotsu.Table.createOrUpdate(modelOrAttributes)`\n\n[with NOT existing id] Creates new record in the spreadsheet with the given model or attributes and returns the new model if created successfully.\n\n[with existing id] Updates existing record in the spreadsheet with the given model or attributes and returns true if updated successfully.\n\n|Param              |Type           |Description|\n|:------------------|:--------------|:----------|\n|modelOrAttributes|model or object|Tamotsu.Table model or attribtues object|\n\n```javascript\nvar agent = new Agent({\n  '#': '999',\n  'First Name': 'Morgan',\n  'Last Name': 'Grimes',\n  'Gender': 'Male',\n  'Salary': 50,\n});\n// If there is no record with id=999, the data will be appended to the sheet. Otherwise, the row with id=999 will be updated with the given data.\nAgent.createOrUpdate(agent);  //=\u003e {#=999.0, First Name=Morgan, ...}\n\n// with attributes\nAgent.createOrUpdate({\n  '#': '999',\n  'First Name': 'Morgan',\n  'Last Name': 'Grimes',\n  'Gender': 'Male',\n  'Salary': 50,\n});\n```\n\n\n## Tamotsu.Model\n\n### `Tamotsu.Model.save()`\n\nCreates or updates on the spreadsheet with the model attributes.\n\n```javascript\n// Creates\nvar newAgent = new Agent({\n  'First Name': 'Morgan',\n  'Last Name': 'Grimes',\n  'Gender': 'Male',\n  'Salary': 50,\n});\nnewAgent.save(); // The data will be appended to the last of the sheet.\n\n// Updates\nvar agent = Agent.last();\nagent['Salary'] = 10;\nagent.save(); // The data on the sheet will be updated.\n```\n\n### `Tamotsu.Model.updateAttributes(attributes)`\n\nUpdates model and spreadsheet with the given attributes and returns true if updated successfully.\n\n|Param     |Type  |Description|\n|:---------|:-----|:----------|\n|attributes|object|attribtues object (column to value)|\n\n```javascript\nvar agent = Agent.first();\nagent.updateAttributes({ 'First Name': 'Chuck', 'Salary': 500 }); // The data on the sheet will be updated.\nLogger.log(agent); //=\u003e  {#=1.0, First Name=Chuck, Last Name=Bartowski, Gender=Male, Salary=500.0, ...}\n```\n\n### `Tamotsu.Model.destroy()`\n\nDelete the model away from the spreadsheet.\n\n```javascript\nvar fired = Agent.first();\nfired.destroy(); // The data will be removed away from the sheet.\n```\n\n### `Tamotsu.Model.isValid()`\n\nReturns boolean of the model being valid/invalid.\n\n```javascript\nTamotsu.initialize();\nvar Agent = Tamotsu.Table.define({ sheetName: 'Agents' }, {\n  validate: function(on) {\n    if (!this['First Name']) {\n      this.errors['First Name'] = \"can't be blank\";\n    }\n  },\n});\n\nvar agent = new Agent();\nLogger.log(agent.isValid()); //=\u003e false\n\nagent['First Name'] = 'Morgan';\nLogger.log(agent.isValid()); //=\u003e true\n```\n\n### `Tamotsu.Model.isNewRecord()`\n\nReturns boolean of the model being new record or not.\n\n```javascript\nvar agent = new Agent();\nLogger.log(agent.isNewRecord()); //=\u003e true\n\nagent = Agent.first();\nLogger.log(agent.isNewRecord()); //=\u003e false\n```\n\n### `Tamotsu.Model.getAttributes()`\n\nReturns an object of the model attributes. (column to value)\n\n```javascript\nvar agent = new Agent();\nLogger.log(agent.getAttributes()); //=\u003e {'#': 1, 'First Name': 'Charles', 'Last Name': 'Bartowski', ...}\n```\n\n### `Tamotsu.Model.setAttributes()`\n\nSet attributes to a model.\n\n|Param     |Type  |Description|\n|:---------|:-----|:----------|\n|attributes|object|attribtues object (column to value)|\n\n```javascript\nvar agent = Agent.first();\nagent.setAttributes({ 'First Name': 'Morgan', 'Last Name': 'Grimes' });\n```\n\n# Test\n\nHere is the specs.\n\nhttps://github.com/itmammoth/Tamotsu_Test\n\n# Licence\n\nMIT Licence.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fitmammoth%2Ftamotsu","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fitmammoth%2Ftamotsu","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fitmammoth%2Ftamotsu/lists"}