{"id":19024643,"url":"https://github.com/yukihirop/rue","last_synced_at":"2025-02-21T19:12:10.325Z","repository":{"id":48487735,"uuid":"335267687","full_name":"yukihirop/rue","owner":"yukihirop","description":"A micro web framework that makes you feel like you're developing on rails (Rails on Vue = Rue) Suspended because it turned out to be useless 😭","archived":false,"fork":false,"pushed_at":"2021-07-23T04:42:41.000Z","size":3226,"stargazers_count":0,"open_issues_count":14,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-02T01:42:47.917Z","etag":null,"topics":["framework","rails","repl","vue","web"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/yukihirop.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":"2021-02-02T11:34:13.000Z","updated_at":"2021-07-23T04:42:42.000Z","dependencies_parsed_at":"2022-09-03T21:12:06.973Z","dependency_job_id":null,"html_url":"https://github.com/yukihirop/rue","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/yukihirop%2Frue","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yukihirop%2Frue/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yukihirop%2Frue/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yukihirop%2Frue/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yukihirop","download_url":"https://codeload.github.com/yukihirop/rue/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240072132,"owners_count":19743526,"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":["framework","rails","repl","vue","web"],"created_at":"2024-11-08T20:37:47.236Z","updated_at":"2025-02-21T19:12:10.305Z","avatar_url":"https://github.com/yukihirop.png","language":"TypeScript","readme":"# Rue\n\nLet's develop a SPA with a [rails](https://github.com/rails/rails) API.  \n\nHowever, we have decided that we will not get much benefit and are currently suspending development. 😭\n\n## Packages\n\n- `@ruejs/rue`\n  - `@ruejs/activemodel`\n  - `@ruejs/activerecord`\n  - `@ruejs/config`\n  - `@ruejs/cli`\n\n## What it Rue ?\n\nIt is a micro library that aims to provide functions such as activerecord and activemodel of rails by borrowing the `src/rue` directory in SPA projects such as\n\nYou can start console(`rue console`) like rails ( `rails console`).\n\n\n```bash\n$ yarn console:examples\nyarn run v1.22.10\n$ cd packages/examples \u0026\u0026 yarn rue:console\n$ rue console\n[Node] Welcome to Node.js v12.7.0.\n[Node] Type \".help\" for more information.\n[Rue] Loading Rue Modules 52/112.Please override 'this._state'\n[Rue] Loading Rue Modules 56/112.\n🍛 \u003e\n```\n\nLet's get the list of loaded classes.\n\n```bash\n🍛 \u003e .loaded\n[Rue] Loading Rue Modules 1/112.Please override 'this._state'\n[Rue] Loading Rue Modules 56/112.\n[\n  'Rue',\n  'ActiveSupport$Registry$Base',\n  'ActiveSupport$Base',\n  'ActiveSupport$Info',\n  'ActiveModel$Base',\n  'ActiveModel$Impl',\n  'ActiveModel$Cachable',\n  'ActiveModel$Translation',\n  'ActiveModel$Validations',\n  'ActiveRecord$Impl',\n  'ActiveRecord$Base',\n  'ActiveRecord$Associations$Holder',\n  'ActiveRecord$Relation$Base',\n  'ActiveRecord$Relation$Holder',\n  'ActiveRecord$Relation$Impl',\n  'ActiveRecord$Associations$CollectionProxy$Base',\n  'ActiveRecord$Associations$CollectionProxy$Holder',\n  'ActiveRecord$Associations$CollectionProxy$Impl',\n  'ActiveRecord$Associations$Relation$Base',\n  'ActiveRecord$AttributeMethods',\n  'ActiveRecord$Core',\n  'ActiveRecord$Associations',\n  'ActiveRecord$Associations$Impl',\n  'ActiveRecord$Dirty',\n  'ActiveRecord$Meta',\n  'ActiveRecord$Persistence',\n  'ActiveRecord$Querying',\n  'ActiveRecord$Scoping',\n  'ActiveRecord$Scoping$Impl',\n  'ActiveRecord$Scoping$Named',\n  'ActiveRecord$Associations$Persistence',\n  'ActiveRecord$Associations$PersistenceStrategy',\n  'ActiveRecord$FinderMethods',\n  'ActiveRecord$QueryMethods',\n  'ActiveRecord$QueryMethods$Evaluator',\n  'Account',\n  'Profile',\n  'Task',\n  'TmpUser',\n  'User'\n]\n🍛 \u003e\n```\n\nYou can see that the rue class and module and ActiveRecord and ActiveModel set in exampels are loaded.\n\nLet's pay attention to the `Account` record and investigate various things.\n\nYou can see the methods that can be called for the `Account` record below.\n\n\n```bash\n🍛 \u003e .ls Account\n{\n  Account: [ 'fromName' ],\n  ActiveRecord: [],\n  'ActiveRecord$Base': [ 'fetchAll', 'resetRecordCache' ],\n  'ActiveRecord$Impl': [],\n  'ActiveModel$Base': [],\n  'ActiveRecord$Associations (RueModule)': [ 'belongsTo', 'hasOne', 'hasMany' ],\n  'ActiveRecord$Associations$Impl (RueModule)': [],\n  'ActiveRecord$Associations$Persistence (RueModule)': [],\n  'ActiveRecord$Persistence (RueModule)': [\n    'createSync',\n    'createSyncOrThrow',\n    'deleteSync',\n    'destroySync',\n    'updateSync',\n    'create',\n    'createOrThrow',\n    'delete',\n    'destroy',\n    'update',\n    'RUE_AUTO_INCREMENT_RECORD_ID',\n    'RUE_RECORD_ID',\n    'RUE_CREATED_AT',\n    'RUE_UPDATED_AT',\n    'RECORD_ALL',\n    'RECORD_META'\n  ],\n  'ActiveRecord$AttributeMethods (RueModule)': [],\n  'ActiveRecord$Dirty (RueModule)': [],\n  'ActiveRecord$Core (RueModule)': [ 'find' ],\n  'ActiveRecord$Scoping (RueModule)': [],\n  'ActiveRecord$Scoping$Impl (RueModule)': [],\n  'ActiveRecord$Scoping$Named (RueModule)': [ 'all', 'scope' ],\n  'ActiveRecord$Querying (RueModule)': [\n    'find',                  'findBy',\n    'findByOrThrow',         'take',\n    'takeOrThrow',           'first',\n    'firstOrThrow',          'last',\n    'lastOrThrow',           'isExists',\n    'isAny',                 'isMany',\n    'isNone',                'isOne',\n    'findOrCreateBy',        'findOrCreateByOrThrow',\n    'findOrInitializeBy',    'createOrFindBy',\n    'createOrFindByOrThrow', 'destroyAll',\n    'deleteAll',             'updateAll',\n    'touchAll',              'destroyBy',\n    'deleteBy',              'where',\n    'rewhere',               'order',\n    'reorder',               'offset',\n    'limit',                 'group',\n    'unscope'\n  ],\n  'ActiveRecord$Meta (RueModule)': [ 'meta', 'recordsWithMeta' ],\n  'ActiveModel$Impl': [],\n  'ActiveModel$Translation (RueModule)': [\n    'translate',\n    '$t',\n    'humanPropertyName',\n    'humanPropName',\n    'humanAttributeName'\n  ],\n  'ActiveModel$Validations (RueModule)': [ 'validates' ],\n  'ActiveModel$Cachable (RueModule)': [ 'checkUniqueKey' ],\n  Function: [ 'apply', 'bind', 'call', 'toString' ],\n  Object: [\n    'hasOwnProperty',\n    'isPrototypeOf',\n    'propertyIsEnumerable',\n    'toString',\n    'valueOf',\n    'toLocaleString'\n  ]\n}\n```\n\nYou can know the result for each class and module defined in this way.\n\nIt behaves just like the `pry-rails` of `ls Account`.\n\n```\n[1] pry(main)\u003e ls Account\nObject.methods: yaml_tag\nActiveModel::Naming#methods: model_name\nActiveSupport::Benchmarkable#methods: benchmark\nActiveSupport::DescendantsTracker#methods: descendants  direct_descendants  subclasses\nActiveRecord::ConnectionHandling#methods:\n  clear_active_connections!              clear_reloadable_connections!  connected_to_many  connection_db_config            connects_to                mysql2_connection    while_preventing_writes\n  clear_all_connections!                 connected?                     connecting_to      connection_pool                 establish_connection       primary_class?\n  clear_cache!                           connected_to                   connection         connection_specification_name   flush_idle_connections!    remove_connection\n  clear_query_caches_for_current_thread  connected_to?                  connection_config  connection_specification_name=  lookup_connection_handler  retrieve_connection\n\n......\n\n```\n\nLet's play with the Account record for a moment.\n\n\n### If you want to get a list of Account records\n\nSee here for `Account` and `Task` codes.\n\n- [Account](https://github.com/yukihirop/rue/blob/main/packages/examples/src/records/account.ts)\n- [Task](https://github.com/yukihirop/rue/blob/main/packages/examples/src/records/task.ts)\n\n```js\n🍛 \u003e accounts = await Account.all()\n[\n  Account {\n    errors: { name: [], email: [] },\n    __rue_record_id__: 1,\n    id: 1,\n    name: 'name_1',\n    email: 'name_1@example.com',\n    info: { github: 'aaa' },\n    _newRecord: false,\n    _destroyed: false,\n    _associationCache: {},\n    tasks: [Function: value],\n    profile: [Function: value],\n    __rue_created_at__: '2021-07-23T12:38:42+09:00',\n    __rue_updated_at__: '2021-07-23T12:38:42+09:00'\n  },\n  Account {\n    errors: { name: [], email: [] },\n    __rue_record_id__: 2,\n    id: 2,\n    name: 'name_2',\n    email: 'name_2@example.com',\n    info: { github: 'bbb' },\n    _newRecord: false,\n    _destroyed: false,\n    _associationCache: {},\n    tasks: [Function: value],\n    profile: [Function: value],\n    __rue_created_at__: '2021-07-23T12:38:42+09:00',\n    __rue_updated_at__: '2021-07-23T12:38:42+09:00'\n  },\n  Account {\n    errors: { name: [], email: [] },\n    __rue_record_id__: 3,\n    id: 3,\n    name: 'name_3',\n    email: 'name_3@example.com',\n    info: { github: 'ccc' },\n    _newRecord: false,\n    _destroyed: false,\n    _associationCache: {},\n    tasks: [Function: value],\n    profile: [Function: value],\n    __rue_created_at__: '2021-07-23T12:38:42+09:00',\n    __rue_updated_at__: '2021-07-23T12:38:42+09:00'\n  },\n  Account {\n    errors: { name: [], email: [] },\n    __rue_record_id__: 4,\n    id: 4,\n    name: 'name_4',\n    email: 'name_4@example.com',\n    info: { github: 'ddd' },\n    _newRecord: false,\n    _destroyed: false,\n    _associationCache: {},\n    tasks: [Function: value],\n    profile: [Function: value],\n    __rue_created_at__: '2021-07-23T12:38:42+09:00',\n    __rue_updated_at__: '2021-07-23T12:38:42+09:00'\n  },\n  Account {\n    errors: { name: [], email: [] },\n    __rue_record_id__: 5,\n    id: 5,\n    name: 'name_5',\n    email: 'name_5@example.com',\n    info: { github: 'eee' },\n    _newRecord: false,\n    _destroyed: false,\n    _associationCache: {},\n    tasks: [Function: value],\n    profile: [Function: value],\n    __rue_created_at__: '2021-07-23T12:38:42+09:00',\n    __rue_updated_at__: '2021-07-23T12:38:42+09:00'\n  },\n  Account {\n    errors: { name: [], email: [] },\n    __rue_record_id__: 6,\n    id: 6,\n    name: 'name_6',\n    email: 'name_6@example.com',\n    info: { github: 'fff' },\n    _newRecord: false,\n    _destroyed: false,\n    _associationCache: {},\n    tasks: [Function: value],\n    profile: [Function: value],\n    __rue_created_at__: '2021-07-23T12:38:42+09:00',\n    __rue_updated_at__: '2021-07-23T12:38:42+09:00'\n  },\n  Account {\n    errors: { name: [], email: [] },\n    __rue_record_id__: 7,\n    id: 7,\n    name: 'name_7',\n    email: 'name_7@example.com',\n    info: { github: 'yukihirop' },\n    _newRecord: false,\n    _destroyed: false,\n    _associationCache: {},\n    tasks: [Function: value],\n    profile: [Function: value],\n    __rue_created_at__: '2021-07-23T12:38:42+09:00',\n    __rue_updated_at__: '2021-07-23T12:38:42+09:00'\n  }\n]\n```\n\n### If you want to get a relationship\n\n`Account` has relationships to `Profile` and `Task` records. Let's examine it.\n\n```ts\n// Register Relashionships\nAccount.hasMany\u003cTask\u003e('tasks', { klass: Task, foreignKey: 'accountId' });\nAccount.hasOne\u003cProfile\u003e('profile', { klass: Profile, foreignKey: 'accountId' });\n```\n\n```js\n🍛 \u003e accounts[0].tasks()\nTask$ActiveRecord_Associations_CollectionProxy [Promise] {\n  {\n    holder: ActiveRecord$Associations$CollectionProxy$Holder {\n      isHolder: true,\n      recordKlass: [Function],\n      scope: [],\n      _defaultScopeParams: [Object],\n      scopeParams: [Object],\n      groupedRecords: {},\n      errors: [],\n      proxy: [],\n      flags: [Object],\n      associationData: [Object],\n      foreignKeyData: [Object]\n    },\n    scope: Task$ActiveRecord_AssociationRelation [Promise] { \u003cpending\u003e }\n  },\n  where: [Function],\n  rewhere: [Function],\n  order: [Function],\n  reorder: [Function],\n  reverseOrder: [Function],\n  offset: [Function],\n  limit: [Function],\n  group: [Function],\n  unscope: [Function],\n  find: [Function],\n  first: [Function],\n  isInclude: [Function],\n  last: [Function],\n  recordKlass: [Function: Task] { fromStatus: [Function: value] }\n}\n```\n\nIn terms of `rails`, it looks like the following.\n\n```ruby\nirb(main):001:0\u003e account = Account.first\n  Account Load (0.4ms)  SELECT \"accounts\".* FROM \"accounts\" ORDER BY \"accounts\".\"id\" ASC LIMIT $1  [[\"LIMIT\", 1]]\n=\u003e #\u003cAccount id: 1, fullName: \"name_1\", email: \"name_1@example.com\", github: \"Ted Casper\", created_at: \"2021-07-23 04:05:13.729072000 +0000\", updated_at: \"2021-07-23 04:05:13.729072000 +0000\"\u003e\n\nirb(main):002:0\u003e task = account.tasks\n  Task Load (0.6ms)  SELECT \"tasks\".* FROM \"tasks\" WHERE \"tasks\".\"account_id\" = $1 /* loading for inspect */ LIMIT $2  [[\"account_id\", 1], [\"LIMIT\", 11]]\n=\u003e #\u003cActiveRecord::Associations::CollectionProxy [#\u003cTask id: 1, content: \"aut\", status: \"wip\", account_id: 1, created_at: \"2021-07-23 04:05:13.752600000 +0000\", updated_at: \"2021-07-23 04:05:13.752600000 +0000\"\u003e, #\u003cTask id: 2, content: \"rerum\", status: \"wip\", account_id: 1, created_at: \"2021-07-23 04:05:13.758269000 +0000\", updated_at: \"2021-07-23 04:05:13.758269000 +0000\"\u003e]\u003e\n\nirb(main):003:0\u003e task.class\n=\u003e Task::ActiveRecord_Associations_CollectionProxy\n```\n\nAn instance of CollectionProxy is returning and has not yet been evaluated.  \nYou need to call or await the promise then to evaluate.\n\n```js\n🍛 \u003e await accounts[0].tasks()\n[\n  Task {\n    errors: { content: [], status: [] },\n    __rue_record_id__: 1,\n    id: 1,\n    content: 'Create @ruejs of web micro framework',\n    status: 'wip',\n    accountId: 1,\n    _newRecord: false,\n    _destroyed: false,\n    _associationCache: {},\n    account: [Function: value],\n    __rue_created_at__: '2021-07-23T12:42:00+09:00',\n    __rue_updated_at__: '2021-07-23T12:42:00+09:00'\n  },\n  Task {\n    errors: { content: [], status: [] },\n    __rue_record_id__: 2,\n    id: 2,\n    content: 'Update r2-oas gem',\n    status: 'success',\n    accountId: 1,\n    _newRecord: false,\n    _destroyed: false,\n    _associationCache: {},\n    account: [Function: value],\n    __rue_created_at__: '2021-07-23T12:42:00+09:00',\n    __rue_updated_at__: '2021-07-23T12:42:00+09:00'\n  }\n]\n```\n\nFor example, what happens if you try to get the one when the status is `wip` ?\n\n```js\n🍛 \u003e await accounts[0].tasks().where({status: 'wip'})\n[\n  Task {\n    errors: { content: [], status: [] },\n    __rue_record_id__: 1,\n    id: 1,\n    content: 'Create @ruejs of web micro framework',\n    status: 'wip',\n    accountId: 1,\n    _newRecord: false,\n    _destroyed: false,\n    _associationCache: {},\n    account: [Function: value],\n    __rue_created_at__: '2021-07-23T12:42:00+09:00',\n    __rue_updated_at__: '2021-07-23T12:42:00+09:00'\n  }\n]\n```\n\n```js\n🍛 \u003e task = await accounts[0].tasks().findBy({status: 'wip'})\nTask {\n  errors: { content: [], status: [] },\n  __rue_record_id__: 1,\n  id: 1,\n  content: 'Create @ruejs of web micro framework',\n  status: 'wip',\n  accountId: 1,\n  _newRecord: false,\n  _destroyed: false,\n  _associationCache: {},\n  account: [Function: value],\n  __rue_created_at__: '2021-07-23T12:42:00+09:00',\n  __rue_updated_at__: '2021-07-23T12:42:00+09:00'\n}\n```\n\nOn the contrary, since the `BelongsTo` association is pasted from `Task` to `Account`, you can get it.\n\n```\n🍛 \u003e task.account()\nThrown:\nTypeError: Cannot read property 'findBy' of undefined\n    at Task.value [as account] (/Users/yukihirop/JavaScriptProjects/rue/packages/activerecord/src/records/modules/associations/core.ts:355:24)\n    at relationFn (/Users/yukihirop/JavaScriptProjects/rue/packages/activerecord/src/records/modules/associations/core.ts:68:14)\n```\n\nOops ... a little buggy now 😅\n\n### If you want to see the implementation of `ActiveRecord$Base`\n\nYou can see the implementation using the `.show` command.\n\n```ts\n🍛 \u003e .show ActiveRecord$Base\n\nFrom: rue/packages/activerecord/src/records/base.ts#L17-60\nOwner: ActiveRecord$Base\nNumber of lines: 44\nUpdated At: 2021/07/23 13:32:04 +09:00\n\nexport class ActiveRecord$Base\u003cP extends t.Params = t.Params\u003e extends ActiveRecord$Impl\u003cP\u003e {\n  public errors: t.Validations$Errors;\n\n  constructor(data?: Partial\u003cP\u003e) {\n    super();\n\n    (this as any)[RUE_RECORD_ID] = undefined;\n\n    if (data) {\n      Object.keys(data).forEach((key) =\u003e {\n        (this as any)[key] = data[key];\n      });\n    }\n\n    this._newRecord = true;\n    this._destroyed = false;\n    this._associationCache = {};\n\n    ActiveRecord$Impl.defineAssociations(this);\n  }\n\n  // override\n  static get objType(): t.ObjType {\n    return 'record';\n  }\n\n  protected fetchAll(): Promise\u003cP[] | [] | { all?: P[]; meta?: any }\u003e {\n    throw \"Please implement 'fetchAll' in Inherited Class\";\n  }\n\n  // All starting points\n  protected static fetchAll(): Promise\u003ct.Params[] | { all?: t.Params[]; meta?: any }\u003e {\n    const instance = new this();\n    return instance.fetchAll();\n  }\n\n  static resetRecordCache() {\n    const cacheKey = this.uniqueKey;\n    RecordCache.destroy(cacheKey);\n    RecordCache.create(cacheKey, RECORD_ALL, []);\n    RecordCache.create(cacheKey, RECORD_META, {});\n    RecordCache.create(cacheKey, RUE_AUTO_INCREMENT_RECORD_ID, 1);\n  }\n}\n```\n\nYou can see any class provided by `rue` displayed in `.loaded`.\n\n\u003cdetails\u003e\n\n```ts\n🍛 \u003e .show ActiveRecord$Associations$CollectionProxy$Base\n\nFrom: rue/packages/activerecord/src/records/associations/collection_proxy/base.ts#L24-671\nOwner: ActiveRecord$Associations$CollectionProxy$Base\nNumber of lines: 648\nUpdated At: 2021/07/23 13:32:05 +09:00\n\nexport class ActiveRecord$Associations$CollectionProxy$Base\u003c\n  T extends ActiveRecord$Base\n\u003e extends ActiveRecord$Associations$CollectionProxy$Impl\u003cT\u003e {\n  /**\n   * @see https://gist.github.com/domenic/8ed6048b187ee8f2ec75\n   * @description Method for getting results. Do not call it in any other method.\n   */\n  rueThen(\n    onFulfilled: rt.PromiseResolve\u003cT, ActiveRecord$Relation\u003cT\u003e\u003e,\n    onRejected?: rt.PromiseReject\u003cany\u003e\n  ) {\n    return this.superThen((value) =\u003e {\n      /**\n       * If you use the `ActiveRecord$QueryMethods` methods, it will enter this branch\n       * There are times when 「 value['holder'] instanceof ActiveRecord$Relation$Holder 」 cannot evaluate correctly. (Cause unknown)\n       */\n      if (\n        typeof value === 'object' \u0026\u0026\n        value != null \u0026\u0026\n        value['holder'] \u0026\u0026\n        value['holder']['isHolder']\n      ) {\n        const { holder, scope } = value;\n\n        if (scope instanceof Promise) {\n          scope.rueThen((r) =\u003e {\n            holder.scope = r as T[];\n            Evaluator.all(holder);\n\n            if (Object.keys(holder.groupedRecords).length \u003e 0) {\n              return onFulfilled(holder.groupedRecords);\n            } else {\n              return onFulfilled(holder.scope);\n            }\n          });\n        } else {\n          holder.scope = scope as T[];\n\n          Evaluator.all(holder);\n\n          if (Object.keys(holder.groupedRecords).length \u003e 0) {\n            return onFulfilled(holder.groupedRecords);\n          } else {\n            return onFulfilled(holder.scope);\n          }\n        }\n      } else {\n        /**\n         * value is records ((e.g.) T | T[])\n         * @description type error\n         */\n        // @ts-expect-error\n        return onFulfilled(value);\n      }\n    }, onRejected);\n  }\n\n  /**\n   * @description All methods are delegated to this instance\n   * @see https://github.com/rails/rails/blob/5aaaa1630ae9a71b3c3ecc4dc46074d678c08d67/activerecord/lib/active_record/associations/collection_proxy.rb#L1100-L1109\n   */\n  scope(): ActiveRecord$Associations$Relation\u003c\n    T,\n    ActiveRecord$Associations$CollectionProxy$Holder\u003cT\u003e,\n    ActiveRecord$Relation\u003cT\u003e\n  \u003e {\n    return this.superThen(({ holder, scope }) =\u003e {\n      return { scope, holder };\n    });\n  }\n\n  /**\n   * @see https://github.com/rails/rails/blob/5aaaa1630ae9a71b3c3ecc4dc46074d678c08d67/activerecord/lib/active_record/associations/collection_proxy.rb#L1100-L1109\n   */\n  scoping\u003cU\u003e(\n    callback: (holder: ActiveRecord$Associations$CollectionProxy$Holder\u003cT\u003e) =\u003e U | Promise\u003cU\u003e\n  ): Promise\u003cU\u003e {\n    return this.superThen(({ holder, scope }) =\u003e {\n      if (scope instanceof Promise) {\n        return scope.rueThen((records) =\u003e {\n          holder.scope = records as T[];\n          /**\n           * @description Pass by value so that 「proxy === record」 does not occur\n           */\n          if (Object.keys(holder.proxy).length === 0) holder.proxy = Array.from(records as T[]);\n          Evaluator.all(holder);\n          return callback(holder);\n        });\n      } else {\n        holder.scope = scope as T[];\n        /**\n         * @description Pass by value so that 「proxy === record」 does not occur\n         */\n        if (Object.keys(holder.proxy).length === 0) holder.proxy = Array.from(scope);\n        Evaluator.all(holder);\n        return callback(holder);\n      }\n    });\n  }\n\n  /**\n   * @description delegate to `scope`\n   */\n  where = \u003cU extends it.Record$Params\u003e(params: Partial\u003cU\u003e): this =\u003e {\n    // @ts-expect-error\n    this.scope().superThen(({ holder }) =\u003e {\n      Object.assign(holder.scopeParams.where, params || {});\n    });\n\n    return this;\n  };\n\n  /**\n   * @description delegate to `scope`\n   */\n  rewhere = \u003cU extends it.Record$Params\u003e(params: Partial\u003cU\u003e): this =\u003e {\n    // @ts-expect-error\n    this.scope().superThen(({ holder }) =\u003e {\n      holder.scopeParams.where = params || {};\n      Object.assign(params, holder.foreignKeyData);\n    });\n\n    return this;\n  };\n\n  /**\n   * @description delegate to `scope`\n   */\n  order = \u003cU = { [key: string]: rmt.QueryMethods$Directions }\u003e(params: U): this =\u003e {\n    // @ts-expect-error\n    this.scope().superThen(({ holder }) =\u003e {\n      Object.assign(holder.scopeParams.order, params || {});\n    });\n\n    return this;\n  };\n\n  /**\n   * @description delegate to `scope`\n   */\n  reorder = \u003cU = { [key: string]: rmt.QueryMethods$Directions }\u003e(params: U): this =\u003e {\n    // @ts-expect-error\n    this.scope().superThen(({ holder }) =\u003e {\n      holder.scopeParams.order = params || {};\n    });\n\n    return this;\n  };\n\n  /**\n   * @description delegate to `scope`\n   */\n  reverseOrder = (): this =\u003e {\n    // @ts-expect-error\n    this.scope().superThen(({ holder }) =\u003e {\n      const orderParams = holder.scopeParams.order;\n      if (isPresent(orderParams)) {\n        Object.keys(orderParams).forEach((propName) =\u003e {\n          const direction = orderParams[propName];\n          if (['desc', 'DESC'].includes(direction)) {\n            holder.scopeParams.order[propName] = 'asc';\n          } else if (['asc', 'ASC'].includes(direction)) {\n            holder.scopeParams.order[propName] = 'desc';\n          }\n        });\n      } else {\n        holder.scopeParams.order['id'] = 'asc';\n      }\n    });\n\n    return this;\n  };\n\n  /**\n   * @description delegate to `scope`\n   */\n  offset = (value: number): this =\u003e {\n    // @ts-expect-error\n    this.scope().superThen(({ holder }) =\u003e {\n      holder.scopeParams.offset = value;\n    });\n\n    return this;\n  };\n\n  /**\n   * @description delegate to `scope`\n   */\n  limit = (value: number): this =\u003e {\n    // @ts-expect-error\n    this.scope().superThen(({ holder }) =\u003e {\n      holder.scopeParams.limit = value;\n    });\n\n    return this;\n  };\n\n  /**\n   * @description Behavior is different from rails group\n   * @description delegate to `scope`\n   */\n  group = \u003cU = { [key: string]: any }\u003e(...props: Array\u003ckeyof U\u003e): this =\u003e {\n    // @ts-expect-error\n    this.scope().superThen(({ holder }) =\u003e {\n      // @ts-expect-error\n      holder.scopeParams.group = props;\n    });\n\n    return this;\n  };\n\n  /**\n   * @description delegate to `scope`\n   */\n  unscope = (...scopeMethods: rmt.QueryMethods$ScopeMethods[]): this =\u003e {\n    const { SCOPE_METHODS } = ActiveRecord$QueryMethods;\n\n    // @ts-expect-error\n    this.scope().superThen(({ holder }) =\u003e {\n      if (scopeMethods.length === 0) {\n        const err = errObj({\n          code: ErrCodes.ARGUMENT_IS_INVALID,\n          message: `'unscope()' must contain arguments.`,\n        });\n        holder.errors.push(err);\n      } else if (isSuperset(SCOPE_METHODS, scopeMethods)) {\n        scopeMethods.forEach((scopeMethod) =\u003e {\n          // @ts-expect-error\n          if (holder._defaultScopeParams[scopeMethod]) {\n            holder.scopeParams[scopeMethod] = Object.assign(\n              {},\n              // @ts-expect-error\n              JSON.parse(JSON.stringify(holder._defaultScopeParams[scopeMethod]))\n            );\n          } else {\n            holder.scopeParams[scopeMethod] = undefined;\n          }\n        });\n      } else {\n        const err = errObj({\n          code: ErrCodes.ARGUMENT_IS_INVALID,\n          message: `Called 'unscope()' with invalid unscoping argument '[${scopeMethods}]'. Valid arguments are '[${SCOPE_METHODS}]'.`,\n        });\n        holder.errors.push(err);\n      }\n    });\n\n    return this;\n  };\n\n  /**\n   * @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html#method-i-empty-3F\n   * @description use holder.proxy\n   */\n  isEmpty(): Promise\u003cboolean\u003e {\n    return this.scoping\u003cboolean\u003e((holder) =\u003e {\n      return holder.proxy.length === 0;\n    });\n  }\n\n  /**\n   * @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html#method-i-find\n   */\n  find = \u003cU extends it.Record$Params\u003e(...ids: it.Record$PrimaryKey[]): Promise\u003cT | T[]\u003e =\u003e {\n    if (ids.length === 0) {\n      throw errObj({\n        code: ErrCodes.RECORD_NOT_FOUND,\n        // @ts-expect-error\n        message: `Could'nt find '${this.recordKlass.uniqueKey}' without an 'id'`,\n      });\n    } else {\n      // @ts-expect-error\n      return this.where\u003cU\u003e({ id: ids }).scoping((holder) =\u003e {\n        if (holder.scope.length === 0) {\n          if (ids.length === 1) {\n            throw errObj({\n              code: ErrCodes.RECORD_NOT_FOUND,\n              params: {\n                // @ts-expect-error\n                resource: this.recordKlass.uniqueKey,\n                id: ids[0],\n              },\n            });\n          } else {\n            throw errObj({\n              code: ErrCodes.RECORD_NOT_FOUND,\n              // @ts-expect-error\n              message: `Could't find all '${this.recordKlass.uniqueKey}' with 'id': [${ids}] (found 0 results, but was looking for ${ids.length})`,\n            });\n          }\n        } else if (holder.scope.length === 1) {\n          return holder.scope[0];\n        } else {\n          return holder.scope;\n        }\n      });\n    }\n  };\n\n  /**\n   * @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html#method-i-any-3F\n   * @description use holder.proxy\n   */\n  isAny(filter?: (record: T) =\u003e boolean): Promise\u003cboolean\u003e {\n    return this.scoping\u003cboolean\u003e((holder) =\u003e {\n      return holder.proxy.filter(filter || Boolean).length \u003e 0;\n    });\n  }\n\n  /**\n   * @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html#method-i-build\n   * @description use holder.proxy\n   */\n  build\u003cU\u003e(params?: Partial\u003cU\u003e | Array\u003cPartial\u003cU\u003e\u003e, yielder?: (self: T) =\u003e void): Promise\u003cT | T[]\u003e {\n    return this.scoping((holder) =\u003e {\n      holder.flags.useProxy = true;\n      if (Array.isArray(params)) {\n        return params.map((param) =\u003e {\n          const merged = Object.assign(param || {}, holder.foreignKeyData);\n          const record = new this.recordKlass(merged);\n          if (yielder) yielder(record);\n          holder.proxy.push(record);\n          return record;\n        });\n      } else {\n        const merged = Object.assign(params || {}, holder.foreignKeyData);\n        const record = new this.recordKlass(merged);\n        if (yielder) yielder(record);\n        holder.proxy.push(record);\n        return record;\n      }\n    });\n  }\n\n  /**\n   * @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html#method-i-create\n   * @description use holder.proxy\n   */\n  create\u003cU\u003e(\n    params?: Partial\u003cU\u003e | Array\u003cPartial\u003cU\u003e\u003e,\n    yielder?: (self: T) =\u003e void\n  ): Promise\u003cT | T[]\u003e {\n    return this.scoping((holder) =\u003e {\n      const merged = Object.assign(params || {}, holder.foreignKeyData);\n      // @ts-ignore\n      return this.recordKlass.create(merged, (self) =\u003e {\n        if (yielder) yielder(self);\n        holder.scope.push(self);\n        holder.proxy.push(self);\n      });\n    });\n  }\n\n  /**\n   * @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html#method-i-create-21\n   * @description use holder.proxy\n   */\n  createOrThrow\u003cU\u003e(\n    params?: Partial\u003cU\u003e | Array\u003cPartial\u003cU\u003e\u003e,\n    yielder?: (self: T) =\u003e void\n  ): Promise\u003cT\u003e {\n    return this.scoping((holder) =\u003e {\n      const merged = Object.assign(params || {}, holder.foreignKeyData);\n      // @ts-expect-error\n      return this.recordKlass.createOrThrow(merged, (self) =\u003e {\n        if (yielder) yielder(self);\n        holder.scope.push(self);\n        holder.proxy.push(self);\n      });\n    });\n  }\n\n  /**\n   * The return value type is different from that of rails.\n   * @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html#method-i-delete\n   */\n  delete(...recordsOrIds: T[] | it.Record$PrimaryKey[]): Promise\u003cT[]\u003e {\n    return this.scoping\u003cT[]\u003e((holder) =\u003e {\n      let recordIds: it.Record$PrimaryKey[] = [];\n\n      recordsOrIds.forEach((recordOrId) =\u003e {\n        if (recordOrId instanceof ActiveRecord$Base) {\n          recordIds.push((recordOrId as T).id);\n        } else {\n          recordIds.push(recordOrId);\n        }\n      });\n\n      return this.find\u003cT\u003e(...recordIds).then((records: T[]) =\u003e {\n        const foreignKey = Object.keys(holder.foreignKeyData)[0];\n        const deletedRecords = records.map((record) =\u003e {\n          // dependent: 'nullify'\n          record.update({ [foreignKey]: undefined });\n          return record;\n        });\n        const destroyedIds = deletedRecords.map((r) =\u003e r.id);\n        const newScope = Array.from(holder.scope).reduce((acc, record) =\u003e {\n          if (!destroyedIds.includes(record.id)) {\n            acc.push(record);\n          }\n          return acc;\n        }, []);\n        holder.scope = Array.from(newScope);\n        holder.proxy = Array.from(newScope);\n        return deletedRecords;\n      });\n    });\n  }\n\n  /**\n   * @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html#method-i-destroy\n   * @description use holder.proxy\n   */\n  destroy(...recordsOrIds: T[] | it.Record$PrimaryKey[]): Promise\u003cT[] | null\u003e {\n    return this.scoping((holder) =\u003e {\n      let recordIds: it.Record$PrimaryKey[] = [];\n\n      recordsOrIds.forEach((recordOrId) =\u003e {\n        if (recordOrId instanceof ActiveRecord$Base) {\n          recordIds.push((recordOrId as T).id);\n        } else {\n          recordIds.push(recordOrId);\n        }\n      });\n\n      if (recordIds.length === 0) {\n        /**\n         * @description Make it behave the same as rails\n         */\n        return null;\n      } else {\n        return this.find\u003cT\u003e(...recordIds).then((records: T[]) =\u003e {\n          return Promise.all(records.map((record) =\u003e record.destroy\u003cT\u003e())).then(\n            (destroyedRecords) =\u003e {\n              const destroyedIds = destroyedRecords.map((r) =\u003e r.id);\n              const newScope = Array.from(holder.scope).reduce((acc, record) =\u003e {\n                if (!destroyedIds.includes(record.id)) {\n                  acc.push(record);\n                }\n                return acc;\n              }, []);\n              holder.scope = Array.from(newScope);\n              holder.proxy = Array.from(newScope);\n              return destroyedRecords;\n            }\n          );\n        });\n      }\n    });\n  }\n\n  /**\n   * @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html#method-i-pluck\n   * @description use holder.proxy\n   */\n  pluck\u003cU extends it.Record$Params\u003e(...propNames: Array\u003ckeyof U\u003e): Promise\u003cArray\u003cct.valueOf\u003cU\u003e\u003e\u003e {\n    return this.scoping\u003cArray\u003cct.valueOf\u003cU\u003e\u003e\u003e((holder) =\u003e {\n      const plucked = holder.proxy.map((record) =\u003e {\n        let result;\n\n        if (propNames.length === 0) {\n          result = Object.keys(record).reduce((acc, propName: string) =\u003e {\n            if (\n              !propName.startsWith('_') \u0026\u0026\n              !(typeof record[propName] === 'function') \u0026\u0026\n              !(propName == 'errors')\n            ) {\n              acc.push(record[propName]);\n            }\n            return acc;\n          }, [] as Array\u003cct.valueOf\u003cU\u003e\u003e);\n        } else {\n          result = propNames.reduce((acc, propName: string) =\u003e {\n            acc.push(record[propName]);\n            return acc;\n          }, [] as Array\u003cct.valueOf\u003cU\u003e\u003e);\n        }\n        return result;\n      });\n\n      if (propNames.length === 1) {\n        return plucked.flat();\n      } else {\n        return plucked;\n      }\n    });\n  }\n\n  /**\n   * @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html#method-i-count\n   */\n  count\u003cU extends it.Record$Params\u003e(\n    propName?: keyof U,\n    filter?: (self: T) =\u003e boolean\n  ): Promise\u003cnumber | { [key: string]: number }\u003e {\n    return this.superThen(({ holder }) =\u003e {\n      // @ts-expect-error\n      return this.recordKlass.all().superThen(({ holder: newHolder }) =\u003e {\n        // deep coppy\n        newHolder.scopeParams = Object.assign({}, JSON.parse(JSON.stringify(holder.scopeParams)));\n        Object.assign(newHolder.scopeParams.where, holder.foreignKeyData);\n        Evaluator.all(newHolder);\n\n        if (isPresent(newHolder.groupedRecords)) {\n          return Object.keys(newHolder.groupedRecords).reduce((acc, key) =\u003e {\n            const records = newHolder.groupedRecords[key];\n            acc[key] = records.length;\n            return acc;\n          }, {});\n        } else {\n          let result;\n\n          if (propName) {\n            result = newHolder.scope.filter((record) =\u003e record[propName]);\n          } else {\n            result = newHolder.scope;\n          }\n\n          if (filter) result = result.filter(filter);\n          return result.length;\n        }\n      });\n    });\n  }\n\n  /**\n   * @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html#method-i-first\n   * @description use holder.proxy\n   */\n  first = (limit?: number): Promise\u003cT | T[]\u003e =\u003e {\n    if (!limit) limit = 1;\n    return this.scoping((holder) =\u003e {\n      const records = holder.proxy;\n      if (records.length === 0) {\n        return null;\n      } else {\n        const slicedRecords = records.slice(0, limit);\n\n        if (limit === 1) return slicedRecords[0];\n        return slicedRecords;\n      }\n    });\n  };\n\n  /**\n   * @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html#method-i-include-3F\n   * @description use holder.proxy\n   */\n  isInclude = (record: T | T[] | Promise\u003cT | T[]\u003e): Promise\u003cboolean\u003e =\u003e {\n    return this.scoping\u003cboolean\u003e((holder) =\u003e {\n      const allRecordIds = holder.proxy.map((r) =\u003e r['id']);\n      if (record instanceof Promise) {\n        return record.then((recordVal) =\u003e {\n          if (recordVal \u0026\u0026 !Array.isArray(recordVal)) {\n            return allRecordIds.includes(recordVal['id']);\n          } else {\n            /**\n             * @description Same specifications as rails\n             */\n            return false;\n          }\n        });\n      } else {\n        if (record \u0026\u0026 !Array.isArray(record)) {\n          return allRecordIds.includes(record['id']);\n        } else {\n          /**\n           * @description Same specifications as rails\n           */\n          return false;\n        }\n      }\n    });\n  };\n\n  /**\n   * @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html#method-i-last\n   * @description use holder.proxy\n   */\n  last = (limit?: number): Promise\u003cT | T[]\u003e =\u003e {\n    if (!limit) limit = 1;\n    return this.scoping((holder) =\u003e {\n      const records = holder.proxy;\n      if (records.length === 0) {\n        return null;\n      } else {\n        const slicedRecords = records.reverse().slice(0, limit).reverse();\n\n        if (limit === 1) return slicedRecords[0];\n        return slicedRecords;\n      }\n    });\n  };\n\n  /**\n   * @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html#method-i-many-3F\n   * @description use holder.proxy\n   */\n  isMany(filter?: (record: T) =\u003e boolean): Promise\u003cboolean\u003e {\n    return this.scoping\u003cboolean\u003e((holder) =\u003e {\n      return holder.proxy.filter(filter || Boolean).length \u003e 1;\n    });\n  }\n\n  /**\n   * @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html#method-i-size\n   * @description use holder.proxy\n   */\n  size(): Promise\u003cnumber | { [key: string]: number }\u003e {\n    return this.scoping\u003cnumber | { [key: string]: number }\u003e((holder) =\u003e {\n      if (isPresent(holder.groupedRecords)) {\n        return Object.keys(holder.groupedRecords).reduce((acc, key) =\u003e {\n          const records = holder.groupedRecords[key];\n          acc[key] = records.length;\n          return acc;\n        }, {});\n      } else {\n        return holder.proxy.length;\n      }\n    });\n  }\n\n  /**\n   * @see https://api.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-to_a\n   */\n  toA(): Promise\u003cT[]\u003e {\n    return this.scoping((holder) =\u003e {\n      return holder.scope;\n    });\n  }\n\n  /**\n   * @see https://api.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-to_a\n   */\n  toArray(): Promise\u003cT[]\u003e {\n    return this.toA();\n  }\n\n  _currentScope(): Promise\u003cT[]\u003e {\n    return this.scoping((holder) =\u003e {\n      if (holder.flags.useProxy) {\n        return holder.proxy;\n      } else {\n        return holder.scope;\n      }\n    });\n  }\n}\n```\n\n\u003c/details\u003e\n\n### Other things you can do\n\nYou can check it with the `.help` command.\n\n```\n🍛 \u003e .help\n.ancs     [Rue] Display ancestors (like Ruby)\n.break    Sometimes you get stuck, this gets you out\n.clear    Break, and also clear the local context\n.desc     [Rue] Display Object.getOwnPropertyDescriptors result\n.editor   Enter editor mode\n.exit     Exit the repl\n.help     Print this help message\n.load     Load JS from a file into the REPL session\n.loaded   [Rue] Display loaded Classes or RueModules\n.lp       [Rue] Display property list\n.ls       [Rue] Display method list\n.proto    [Rue] Display Object.getPrototypeOf result\n.save     Save all evaluated commands in this REPL session to a file\n.show     [Rue] Display method definition (format: \u003cClass\u003e or \u003cClass\u003e.\u003cstaticMethod\u003e or \u003cClass\u003e.prototype.\u003cinstanceMethod\u003e)\n\nPress ^C to abort current expression, ^D to exit the repl\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyukihirop%2Frue","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyukihirop%2Frue","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyukihirop%2Frue/lists"}