SkillAgentSearch skills...

Cdq

Core Data Query for RubyMotion

Install / Use

/learn @kemiller/Cdq
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

[!IMPORTANT] CDQ has been archived is no longer maintained. Thanks everyone for their support over the years!

Streamlined Core Data for RubyMotion

Core Data Query (CDQ) is a library to help you manage your Core Data stack while using RubyMotion. It uses a data model file, which you can generate in XCode, or you can use ruby-xcdm.

Build Status Gem Version

CDQ was created by Ken Miller at Infinite Red, a React Native expert consulting company based the US.

Get Started

  1. Introducing CDQ
  2. Greenfield Quick Start Tutorial
  3. Cheat Sheet
  4. API docs

Introducing CDQ

CDQ began its life as a fork of MotionData, but it became obvious I wanted to take things in a different direction, so I cut loose and ended up rewriting almost everything. If you pay attention, you can still find the genetic traces, so thanks to @alloy for sharing his work and letting me learn so much.

CDQ aims to streamline the process of getting you up and running Core Data, while avoiding too much abstraction or method pollution on top of the SDK. While it borrows many ideas from ActiveRecord (especially AREL), it is designed to harmonize with Core Data's way of doing things first.

Why use a static Data Model?

By using a real data model file that gets compiled and included in your bundle, you can take advantage of automatic migration, which simplifies managing your schema as it grows, if you can follow a few simple rules.

Installing

$ gem install cdq
$ motion create my_app # if needed
$ cd my_app
$ cdq init

This way assumes you want to use ruby-xcdm. Run cdq -h for list of more generators.

Using Bundler:

gem 'cdq'

If you want to see bleeding-edge changes, point Bundler at the git repo:

gem 'cdq', git: 'git://github.com/infinitered/cdq.git'

Setting up your stack

You will need a data model file. If you've created one in XCode, move or copy it to your resources file and make sure it's named the same as your RubyMotion project. If you're using ruby-xcdm (which I highly recommend) then it will create the datamodel file automatically and put it in the right place.

Now include the setup code in your app_delegate.rb file:

class AppDelegate
  include CDQ

  def application(application, didFinishLaunchingWithOptions:launchOptions)
    cdq.setup
    true
  end
end

That's it! You can create specific implementation classes for your entities if you want, but it's not required. You can start running queries on the console or in your code right away.

Schema

The best way to use CDQ is together with ruby-xcdm, which is installed as a dependency. For the full docs, see its github page, but here's a taste. Schema files are found in the "schemas" directory within your app root, and they are versioned for automatic migrations, and this is what they look like:

  schema "0001 initial" do

    entity "Article" do
      string    :body,        optional: false
      integer32 :length
      boolean   :published,   default: false
      datetime  :publishedAt, default: false
      string    :title,       optional: false

      belongs_to :author
    end

    entity "Author" do
      float :fee
      string :name, optional: false

      # Deleting an author will delete all associated articles
      has_many :articles, deletionRule: "Cascade"
    end

  end

Ruby-xcdm translates these files straight into the XML format that Xcode uses for datamodels.

Boolean Values

Since CoreData stores boolean values as an NSNumber, cdq provides helper methods to allow you to get the boolean value of the property. Take the Article model from above with the boolean:published. If you call published directly you'll get the NSNumber 0 or 1. If you call published? you'll get a boolean true or false

article_1 = Article.create(published: true)
article_2 = Article.create(published: false)

article_1.published # => 1
article_2.published # => 0

article_1.published? # => true
article_2.published? # => false

Context Management

Managing NSManagedObjectContext objects in Core Data can be tricky, especially if you are trying to take advantage of nested contexts for better threading behavior. One of the best parts of CDQ is that it handles contexts for you relatively seamlessly. If you have a simple app, you may never need to worry about contexts at all.

Nested Contexts

For a great discussion of why you might want to use nested contexts, see here.

CDQ maintains a stack of contexts (one stack per thread), and by default, all operations on objects use the topmost context. You just call cdq.save and it saves the whole stack. Or you can get a list of all the contexts in order with cdq.contexts.all and do more precise work.

To access the cdq object from a class method inside a class that is not a CDQManagedObject subclass, make sure to include the CDQ module in your class like this:

class MyClass
  class << self
    include CDQ

    def my_class_method
      # Do something
      cdq.save
    end
  end
end

# Elsewhere
MyClass.my_class_method

Settings things up the way you want is easy. Here's how you'd set it up for asynchronous saves:

  cdq.contexts.push(:root)
  cdq.contexts.push(:main)

This pushes a private queue context onto the bottom of the stack, then a main queue context on top of it. Since the main queue is on top, all your data operations will use that. cdq.save then saves the main context, and schedules a save on the root context.

In addition, since these two contexts are globally important, it makes them available at cdq.contexts.main and cdq.contexts.root.

Temporary Contexts

From time to time, you may need to use a temporary context. For example, on importing a large amount of data from the network, it's best to process and load into a temporary context (possibly in a background thread) and then move all the data over to your main context all at once. CDQ makes that easy too:

  cdq.background do

    # Your work here

    cdq.save
  end

Object Lifecycle

Creating

  Author.create(name: "Le Guin", publish_count: 150, first_published: 1970)
  Author.create(name: "Shakespeare", publish_count: 400, first_published: 1550)
  Author.create(name: "Blake", publish_count: 100, first_published: 1778)
  cdq.save

CDQ 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.

Reading

  author = Author.create(name: "Le Guin", publish_count: 150, first_published: 1970)
  author.name # => "Le Guin"
  author.publish_count # => 150
  author.attributes # => { "name" => "Le Guin", "publish_count" => 150, "first_published" => 1970 }

Updating

  author = Author.first
  author.name = "Ursula K. Le Guin"
  cdq.save

You can also update multiple attributes of a single object:

  author = Author.first
  author.update(name: "Mark Twain", publish_count: 30, first_published: 1865)
  cdq.save

The 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:

  new_author_data = {
    name: "Mark Twain",
    publish_count: 30,
    first_published: 1865,
    some_attribute_that_doesnt_exist_on_author: "balderdash!"
  }  
  sanitized = new_author_data.keep_if{|k,_| Author.attribute_names.include?(k) }

  author = Author.first
  author.update(sanitized)
  cdq.save

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.

CDQ 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.

Deleting

  author = Author.first
  author.destroy
  cdq.save

Queries

A quick aside about queries in Core Data. You should avoid them whenever possible in your production code. Core Data is designed to work efficiently when you hang on to references to specific objects and use them as you would any in-memory object, letting Core Data handle your memory usage for you. If you're coming from a server-side rails background, this can be pretty hard to get used to, but this is a very different environment. So if you find yourself running queries that only return a single object, consider rearchitecting. That said, queries are sometimes the only solution, and it's very handy to be able to use them easily when debugging from the console, or in unit tests.

All of these queries are infinitely daisy-chainable, and almost everything is possible to do using only chained methods, no need to drop into NSPredicate format strings unless you want to.

Here are some examples. See the cheat sheet for a complete list.

Conditions

  Author.where(:name).eq('Sha
View on GitHub
GitHub Stars171
CategoryData
Updated8mo ago
Forks34

Languages

Ruby

Security Score

92/100

Audited on Jul 29, 2025

No findings