{"id":19578465,"url":"https://github.com/infinitered/cdq","last_synced_at":"2025-04-27T06:33:32.641Z","repository":{"id":9401700,"uuid":"11267099","full_name":"infinitered/cdq","owner":"infinitered","description":"Core Data Query for RubyMotion","archived":false,"fork":false,"pushed_at":"2019-11-11T21:14:28.000Z","size":297,"stargazers_count":171,"open_issues_count":23,"forks_count":36,"subscribers_count":26,"default_branch":"master","last_synced_at":"2025-04-04T22:41:36.505Z","etag":null,"topics":["core-data","database","rubymotion"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"twbs/bootstrap","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/infinitered.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}},"created_at":"2013-07-08T22:29:06.000Z","updated_at":"2024-12-31T15:26:12.000Z","dependencies_parsed_at":"2022-08-20T08:22:23.800Z","dependency_job_id":null,"html_url":"https://github.com/infinitered/cdq","commit_stats":null,"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinitered%2Fcdq","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinitered%2Fcdq/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinitered%2Fcdq/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinitered%2Fcdq/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/infinitered","download_url":"https://codeload.github.com/infinitered/cdq/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251099351,"owners_count":21536146,"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":["core-data","database","rubymotion"],"created_at":"2024-11-11T07:11:26.970Z","updated_at":"2025-04-27T06:33:32.323Z","avatar_url":"https://github.com/infinitered.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Streamlined Core Data for RubyMotion\n\nCore Data Query (CDQ) is a library to help you manage your Core Data stack\nwhile using RubyMotion.  It uses a data model file, which you can generate in\nXCode, or you can use [ruby-xcdm](https://github.com/infinitered/ruby-xcdm).\n\n[![Build Status](https://travis-ci.org/infinitered/cdq.png?branch=master)](https://travis-ci.org/infinitered/cdq)\n[![Gem Version](https://badge.fury.io/rb/cdq.png)](http://badge.fury.io/rb/cdq)\n\nCDQ is maintained by [Infinite Red](http://infinite.red), a web and mobile development company based in Portland, OR and San Francisco, CA.\n\n## Get Started\n1. [Introducing CDQ](#introducingCDQ)\n2. [Greenfield Quick Start Tutorial](https://github.com/infinitered/cdq/wiki/Greenfield-Quick-Start)\n3. [Cheat Sheet](https://github.com/infinitered/cdq/wiki/CDQ-Cheat-Sheet)\n4. [API docs](http://rubydoc.info/github/infinitered/cdq)\n\n## Introducing CDQ\n\nCDQ began its life as a fork of\n[MotionData](https://github.com/alloy/MotionData), but it became obvious I\nwanted to take things in a different direction, so I cut loose and ended up\nrewriting almost everything.  If you pay attention, you can still find the\ngenetic traces, so thanks to @alloy for sharing his work and letting me learn\nso much.\n\nCDQ aims to streamline the process of getting you up and running Core Data, while\navoiding too much abstraction or method pollution on top of the SDK.  While it\nborrows many ideas from ActiveRecord (especially AREL), it is designed to\nharmonize with Core Data's way of doing things first.\n\nI am actively developing and improving CDQ (updated February 2015) so if you have\ntrouble or find a bug, please open a ticket!\n\n### Why use a static Data Model?\n\nBy using a real data model file that gets compiled and included in your bundle,\nyou can take advantage of automatic migration, which simplifies managing your\nschema as it grows, if you can follow a few [simple rules](https://developer.apple.com/library/ios/documentation/cocoa/conceptual/CoreDataVersioning/Articles/vmLightweightMigration.html#//apple_ref/doc/uid/TP40004399-CH4-SW2).\n\n## Installing\n\n```bash\n$ gem install cdq\n$ motion create my_app # if needed\n$ cd my_app\n$ cdq init\n```\n\nThis way assumes you want to use ruby-xcdm.  Run `cdq -h` for list of more generators.\n\n### Using Bundler:\n\n```ruby\ngem 'cdq'\n```\n\nIf you want to see bleeding-edge changes, point Bundler at the git repo:\n\n```ruby\ngem 'cdq', git: 'git://github.com/infinitered/cdq.git'\n```\n\n## Setting up your stack\n\nYou will need a data model file.  If you've created one in XCode, move or copy\nit to your resources file and make sure it's named the same as your RubyMotion\nproject.  If you're using `ruby-xcdm` (which I highly recommend) then it will\ncreate the datamodel file automatically and put it in the right place.\n\nNow include the setup code in your `app_delegate.rb` file:\n\n```ruby\nclass AppDelegate\n  include CDQ\n\n  def application(application, didFinishLaunchingWithOptions:launchOptions)\n    cdq.setup\n    true\n  end\nend\n```\n\nThat's it!  You can create specific implementation classes for your entities if\nyou want, but it's not required.  You can start running queries on the console or\nin your code right away.\n\n## Schema\n\nThe best way to use CDQ is together with ruby-xcdm, which is installed as a\ndependency.  For the full docs, see its [github page](http://github.com/infinitered/ruby-xcdm),\nbut here's a taste.  Schema files are found in the \"schemas\" directory within your\napp root, and they are versioned for automatic migrations, and this is what they look like:\n\n```ruby\n  schema \"0001 initial\" do\n\n    entity \"Article\" do\n      string    :body,        optional: false\n      integer32 :length\n      boolean   :published,   default: false\n      datetime  :publishedAt, default: false\n      string    :title,       optional: false\n\n      belongs_to :author\n    end\n\n    entity \"Author\" do\n      float :fee\n      string :name, optional: false\n\n      # Deleting an author will delete all associated articles\n      has_many :articles, deletionRule: \"Cascade\"\n    end\n\n  end\n```\n\nRuby-xcdm translates these files straight into the XML format that Xcode uses for datamodels.\n\n### Boolean Values\n\nSince CoreData stores boolean values as an `NSNumber`, cdq provides helper\nmethods to allow you to get the boolean value of the property. Take the `Article`\nmodel from above with the `boolean`:`published`. If you call `published` directly\nyou'll get the `NSNumber` `0` or `1`. If you call `published?` you'll get a\nboolean `true` or `false`\n\n```ruby\narticle_1 = Article.create(published: true)\narticle_2 = Article.create(published: false)\n\narticle_1.published # =\u003e 1\narticle_2.published # =\u003e 0\n\narticle_1.published? # =\u003e true\narticle_2.published? # =\u003e false\n```\n\n## Context Management\n\nManaging NSManagedObjectContext objects in Core Data can be tricky, especially\nif you are trying to take advantage of nested contexts for better threading\nbehavior.  One of the best parts of CDQ is that it handles contexts for you\nrelatively seamlessly.  If you have a simple app, you may never need to worry\nabout contexts at all.\n\n### Nested Contexts\n\nFor a great discussion of why you might want to use nested contexts, see [here](http://www.cocoanetics.com/2012/07/multi-context-coredata/).\n\nCDQ maintains a stack of contexts (one stack per thread), and by default, all\noperations on objects use the topmost context.  You just call `cdq.save`\nand it saves the whole stack.  Or you can get a list of all the contexts in\norder with `cdq.contexts.all` and do more precise work.\n\nTo access the `cdq` object from a class method inside a class that is not a `CDQManagedObject`\nsubclass, make sure to include the `CDQ` module in your class like this:\n\n```ruby\nclass MyClass\n  class \u003c\u003c self\n    include CDQ\n\n    def my_class_method\n      # Do something\n      cdq.save\n    end\n  end\nend\n\n# Elsewhere\nMyClass.my_class_method\n```\n\nSettings things up the way you want is easy.  Here's how you'd set it up for asynchronous\nsaves:\n\n```ruby\n  cdq.contexts.push(:root)\n  cdq.contexts.push(:main)\n```\n\nThis pushes a private queue context onto the bottom of the stack, then a main queue context on top of it.\nSince the main queue is on top, all your data operations will use that.  `cdq.save` then saves the\nmain context, and schedules a save on the root context.\n\nIn addition, since these two contexts are globally important, it makes them available at `cdq.contexts.main` and\n`cdq.contexts.root`.\n\n### Temporary Contexts\n\nFrom time to time, you may need to use a temporary context.  For example, on\nimporting a large amount of data from the network, it's best to process and\nload into a temporary context (possibly in a background thread) and then move\nall the data over to your main context all at once.  CDQ makes that easy too:\n\n```ruby\n  cdq.background do\n\n    # Your work here\n\n    cdq.save\n  end\n```\n\n## Object Lifecycle\n\n### Creating\n```ruby\n  Author.create(name: \"Le Guin\", publish_count: 150, first_published: 1970)\n  Author.create(name: \"Shakespeare\", publish_count: 400, first_published: 1550)\n  Author.create(name: \"Blake\", publish_count: 100, first_published: 1778)\n  cdq.save\n```\n\nCDQ will automatically set the object's property `created_at` to `Time.now` if it exists. If you want to use this ActiveRecord-like automatic attribute, make sure to add `datetime :created_at` to your schema's model definition.\n\n### Reading\n\n```ruby\n  author = Author.create(name: \"Le Guin\", publish_count: 150, first_published: 1970)\n  author.name # =\u003e \"Le Guin\"\n  author.publish_count # =\u003e 150\n  author.attributes # =\u003e { \"name\" =\u003e \"Le Guin\", \"publish_count\" =\u003e 150, \"first_published\" =\u003e 1970 }\n```\n\n### Updating\n```ruby\n  author = Author.first\n  author.name = \"Ursula K. Le Guin\"\n  cdq.save\n```\n\nYou can also update multiple attributes of a single object:\n\n```ruby\n  author = Author.first\n  author.update(name: \"Mark Twain\", publish_count: 30, first_published: 1865)\n  cdq.save\n```\n\nThe update command will raise an `UnknownAttributeError` if you try and set an attribute that doesn't exist on the object so it's good practice to sanitize the data before you call `update`:\n\n```ruby\n  new_author_data = {\n    name: \"Mark Twain\",\n    publish_count: 30,\n    first_published: 1865,\n    some_attribute_that_doesnt_exist_on_author: \"balderdash!\"\n  }  \n  sanitized = new_author_data.keep_if{|k,_| Author.attribute_names.include?(k) }\n\n  author = Author.first\n  author.update(sanitized)\n  cdq.save\n```\n\n**NOTE** Custom class methods will have to `include CDQ` in order to have access to the `cdq` object. If you're calling `cdq` from a class method, you also have to `extend CDQ`.\n\nCDQ will automatically set the object's property `updated_at` to `Time.now` if it exists. If you want to use this ActiveRecord-like automatic attribute, make sure to add `datetime :updated_at` to your schema's model definition.\n\n### Deleting\n```ruby\n  author = Author.first\n  author.destroy\n  cdq.save\n```\n\n## Queries\n\nA quick aside about queries in Core Data.  You should avoid them whenever\npossible in your production code.  Core Data is designed to work efficiently\nwhen you hang on to references to specific objects and use them as you would\nany in-memory object, letting Core Data handle your memory usage for you.  If\nyou're coming from a server-side rails background, this can be pretty hard to\nget used to, but this is a very different environment.  So if you find yourself\nrunning queries that only return a single object, consider rearchitecting.\nThat said, queries are sometimes the only solution, and it's very handy to be\nable to use them easily when debugging from the console, or in unit tests.\n\nAll of these queries are infinitely daisy-chainable, and almost everything is\npossible to do using only chained methods, no need to drop into NSPredicate format\nstrings unless you want to.\n\nHere are some examples.  **See the [cheat sheet](https://github.com/infinitered/cdq/wiki/CDQ-Cheat-Sheet) for a complete list.**\n\n### Conditions\n\n```ruby\n  Author.where(:name).eq('Shakespeare')\n  Author.where(:publish_count).gt(10)\n  Author.where(name: 'Shakespeare', publish_count: 15)\n  Author.where(\"name LIKE %@\", '*kesp*')\n  Author.where(\"name LIKE %@\", 'Shakespear?')\n```\n\n### Sorts, Limits and Offsets\n\n```ruby\n  Author.sort_by(:created_at).limit(1).offset(10)\n  Author.sort_by(:created_at, order: :descending)\n  Author.sort_by(:created_at, case_insensitive: true)\n```\n\n### Conjunctions\n\n```ruby\n  Author.where(:name).eq('Blake').and(:first_published).le(Time.local(1700))\n\n  # Multiple comparisons against the same attribute\n  Author.where(:created_at).ge(yesterday).and.lt(today)\n```\n\n#### Nested Conjunctions\n\n```ruby\n  Author.where(:name).contains(\"Emily\").and(cdq(:pub_count).gt(100).or.lt(10))\n```\n\n### Calculations\n\n```ruby\n  Author.sum(:fee)\n  Author.average(:fee)\n  Author.min(:fee)\n  Author.max(:fee)\n  Author.where(:name).eq(\"Emily\").sum(:fee)\n```\n\n### Fetching\n\nLike ActiveRecord, CDQ will not run a fetch until you actually request specific\nobjects.  There are several methods for getting at the data:\n\n * `array`\n * `first`\n * `last`\n * `each`\n * `[]`\n * `map`\n * Anything else in `Enumerable`\n\n## Dedicated Models\n\nIf you're using CDQ in a brand new project, you'll probably want to use\ndedicated model classes for your entities.\nfamiliar-looking and natural syntax for queries and scopes:\n\n```ruby\n  class Author \u003c CDQManagedObject\n  end\n```\n\n## Named Scopes\n\nYou can save up partially-constructed queries for later use using named scopes, even\ncombining them seamlessly with other queries or other named scopes:\n\n```ruby\n  class Author \u003c CDQManagedObject\n    scope :a_authors, where(:name).begins_with('A')\n    scope :prolific, where(:publish_count).gt(99)\n  end\n\n  Author.prolific.a_authors.limit(5)\n```\n\n## Using CDQ with a pre-existing model\n\nIf you have an existing app that already manages its own data model, you can\nstill use CDQ, overriding its stack at any layer:\n\n```ruby\ncdq.setup(context: App.delegate.mainContext) # don't set up model or store coordinator\ncdq.setup(store: App.delegate.persistentStoreCoordinator) # Don't set up model\ncdq.setup(model: App.delegate.managedObjectModel) # Don't load model\n```\n\nYou cannot use CDQManagedObject as a base class when overriding this way,\nyou'll need to use the master method, described below.  If you have an\nexisting model and want to use it with CDQManagedObject without changing its\nname, You'll need to use a \u003ctt\u003ecdq.yml\u003c/tt\u003e config file.  See\n[CDQConfig](http://github.com/infinitered/cdq/tree/master/motion/cdq/config.rb).\n\n### Working without model classes using the master method\n\nIf you need or want to work without using CDQManagedObject as your base class,\nyou can use the `cdq()`master method.  This is a \"magic\" method, like\n`rmq()` in [RubyMotionQuery](http://github.com/infinitered/rmq) or\n`$()` in jQuery, which will lift whatever you pass into it into the CDQ\nuniverse. The method is available inside all UIResponder classes (so, views and\ncontrollers) as well as in the console.  You can use it anywhere else by\nincluding the model `CDQ` into your classes.  To use an entity without a\nmodel class, just pass its name as a string into the master method, like so\n\n```ruby\n  cdq('Author').where(:name).eq('Shakespeare')\n  cdq('Author').where(:publish_count).gt(10)\n  cdq('Author').sort_by(:created_at).limit(1).offset(10)\n```\n\nAnything you can do with a model, you can also do with the master method, including\ndefining and using named scopes:\n\n```ruby\n  cdq('Author').scope :a_authors, cdq(:name).begins_with('A')\n  cdq('Author').scope :prolific, cdq(:publish_count).gt(99)\n```\n\u003e NOTE: strings and symbols are NOT interchangeable. `cdq('Entity')` gives you a\nquery generator for an entity, but `cdq(:attribute)` starts a predicate for an\nattribute.\n\n## Reserved model attributes\n\nCDQ does some smart automatic attribute setting. If you add attributes `:created_at` and/or `:updated_at` to a model in your schema file, whenever a record is created or updated, these properties will be updated accordingly. Therefore, you can not define your own `:created_at` or `:updated_at` model attributes. These attributes must be of type `datetime`. Note that these attributes aren't set until you call `cdq.save`\n\nExample:\n\n```ruby\nschema \"0001 initial\" do\n  entity \"Author\" do\n    string :name, optional: false\n\n    datetime :created_at\n    datetime :updated_at\n  end\nend\n```\n\n```ruby\na = Author.create(name: \"Le Guin\")\n# Notice that the properties aren't set yet\n#\n# \u003cAuthor: 0x1175f9540\u003e (entity: Author; id: 0x117504810\n# \u003cx-coredata:///Author/tA4E22210-72CF-4272-BF2C-0C5C63A55B072\u003e ; data: {\n#     name: \"Le Guin\";\n#     created_at: nil;\n#     updated_at: nil;\n# })\n\ncdq.save\n\nputs a # Original reference to created Author object\n# \u003cAuthor: 0x1175f9540\u003e (entity: Author; id: 0x117504810\n# \u003cx-coredata:///Author/tA4E22210-72CF-4272-BF2C-0C5C63A55B072\u003e ; data: {\n#     name: \"Le Guin\";\n#     created_at: 2015-08-19 20:44:40 +0000;\n#     updated_at: 2015-08-19 20:44:40 +0000;\n# })\n\na.name = \"Some Other Guy\"\nputs a\n# Note that nothing has changed except the name:\n#\n# \u003cAuthor: 0x1175f9540\u003e (entity: Author; id: 0x117504810\n# \u003cx-coredata:///Author/tA4E22210-72CF-4272-BF2C-0C5C63A55B072\u003e ; data: {\n#     name: \"Some Other Guy\";\n#     created_at: 2015-08-19 20:44:40 +0000;\n#     updated_at: 2015-08-19 20:44:40 +0000;\n# })\n\ncdq.save\nputs a\n# \u003cAuthor: 0x1175f9540\u003e (entity: Author; id: 0x117504810\n# \u003cx-coredata:///Author/tA4E22210-72CF-4272-BF2C-0C5C63A55B072\u003e ; data: {\n#     name: \"Some Other Guy\";\n#     created_at: 2015-08-19 20:44:40 +0000;\n#     updated_at: 2015-08-19 20:47:40 +0000;\n# })\n```\n\nAlso note that you should never use `object_id` as a model attribute as it will conflict with an internally generated property.\n\n## iCloud\n\n**Removed as of version 2.0.0.  If you still need this, pin cdq gem to before version 2.0.0**\n\nAs of version 0.1.10, there is some experimental support for iCloud, written by\n@katsuyoshi.  Please try it out and let us know how it's working for you.  To\nenable, initialize like this:\n\n```ruby\n  cdq.stores.new(iCloud: true, container: \"com.your.container.id\")\n```\n\nYou can also set up iCloud in your cdq.yml file.\n\n## Documentation\n\n* [API](http://rubydoc.info/github/infinitered/cdq)\n* [Cheat Sheet](https://github.com/infinitered/cdq/wiki/CDQ-Cheat-Sheet)\n* [Tutorial](https://github.com/infinitered/cdq/wiki/Greenfield-Quick-Start)\n\n## Things that are currently missing\n\n* There is no facility for custom migrations yet\n* There are no explicit validations (but you can define them on your data model)\n* Lifecycle Callbacks or Observers\n\n## Tips\n\nIf you need, you could watch SQL statements by setting the following launch argument through `args` environment variable:\n\n```\n$ rake args='-com.apple.CoreData.SQLDebug 3'\n```\n\n`com.apple.CoreData.SQLDebug` takes a value between 1 and 3; the higher the value, the more verbose the output.\n\n## Premium Support\n\n[CDQ](https://github.com/infinitered/cdq), as an open source project, is free to use and always will be. [Infinite Red](https://infinite.red/) offers premium CDQ support and general mobile app design/development services. Email us at [hello@infinite.red](mailto:hello@infinite.red) to get in touch with us for more details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finfinitered%2Fcdq","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finfinitered%2Fcdq","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finfinitered%2Fcdq/lists"}