RedPotion uses Core Data as its local data store. Specifically it uses the CDQ gem.
app.data
The cdq
command is aliased to app.data
to give you a more semantic way to access local data.
Schema
Now go edit schemas/0001_initial.rb. There's some commented-out example code in it. Go ahead and uncomment the example entities and save the file. It should look like this:
schema "0001 initial" do
# Examples:
#
entity "Person" do
string :name, optional: false
has_many :posts
end
entity "Post" do
string :title, optional: false
string :body
datetime :created_at
datetime :updated_at
has_many :replies, inverse: "Post.parent"
belongs_to :parent, inverse: "Post.replies"
belongs_to :person
end
end
Models
Now to generate models for the entities, Person and Post, that you've just set up in the schema. CDQ includes a handy generator to save a little time:
$ cdq create model person
$ cdq create model post
Creating Data
You're now ready to run rake and start playing with your data. The simulator should come up cleanly.
Once you have a console prompt, start creating some data:
marie = Person.create(name: "Marie Skłodowska")
pierre = Person.create(name: "Pierre Curie")
post = marie.posts.create(title: "First Post", body: "This stuff seems to be glowing.", created_at: Time.now)
post.replies.create(title: "Marry Me!", body: "That is so freaking amazing!", person: pierre, created_at: Time.now)
cdq.save
Note that unlike ActiveRecord, created_at will not (currently) get set automatically.
Now quit the app and restart so you can verify that things are saving to disk correctly.
An aside about data faults
When you're looking around, you may notice something strange. Sometimes when you run a query, especially for the first time, you get a result like this:
(main)> Person.all.array
=> [<Person: 0x9288d30> (entity: Person; id: 0x9285150 <x-coredata://FD49F7ED-9459-4675-A00F-4CF8B6C1419E/Person/p1> ; data: <fault>), <Person: 0x9288f80> (entity: Person; id: 0x9285160 <x-coredata://FD49F7ED-9459-4675-A00F-4CF8B6C1419E/Person/p2> ; data: <fault>)]
Very compact. But other times, it might look like this:
(main)> Person.all.map(&:name)
=> ["Marie Skłodowska", "Pierre Curie"]
(main)> Person.all.array
=> [<Person: 0x9288d30> (entity: Person; id: 0x9285150 <x-coredata://FD49F7ED-9459-4675-A00F-4CF8B6C1419E/Person/p1> ; data: {
name = "Marie Sk\U0142odowska";
posts = "<relationship fault: 0x9280260 'posts'>";
}), <Person: 0x9288f80> (entity: Person; id: 0x9285160 <x-coredata://FD49F7ED-9459-4675-A00F-4CF8B6C1419E/Person/p2> ; data: {
name = "Pierre Curie";
posts = "<relationship fault: 0x9290540 'posts'>";
})]
Not so compact, but more useful. What's going on? Core Data is very smart
about how much data to load, and won't go fetch the details of an object until
they're used. So in the first example, I'd just loaded the app and hadn't used
anything yet, so you see the text data: <fault>
in there indicating that
it hasn't fetched the attributes from the SQLite database that actually holds
all your data. In the second example, I deliberately loop through each object
and ask it for data, so now when I print out the collection, it shouls you all the
attributes. But you'll note that for posts, it still says "relationship fault",
because we haven't asked it about posts yet. Turtles all the way down.
Queries
Now try some queries:
pierre = Person.where(:name).contains("Pierre").first
marie = Person.where(:name).ne("Pierre Curie").first
Post.where(:person).eq(pierre).array
Post.sort_by(:title).first
Post.sort_by(:created_at)[1]
Post.count
Create some more data and then run the queries again, or try some new combinations.
Cheatsheet
cdq.setup # Load the whole system
cdq.contexts.current # The currently-active NSManagedObjectContext
cdq.contexts.all # See all contexts on the stack
cdq.contexts.new(type) # Create a new context and push it onto the stack
cdq.save # Save all contexts on the stack
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
Updating
author = Author.first
author.name = "Ursula K. Le Guin"
cdq.save
Deleting
author = Author.first
author.destroy
cdq.save
Queries
Author.where(:name).eq("Emily")
Author.where(:name).not_equal("Emily")
Author.limit(1)
Author.offset(10)
Author.where(:name).contains("A").offset(10).first
# Conjuctions
Author.where(:name).contains("Emily").and.contains("Dickinson")
Author.where(:name).begins_with("E").or(:pub_count).eq(1)
Post.where(:date).ge(start_date).and.le(end_date) # gt, ge (greater or equal), lt, le (less or equal)
# Nested Conjuctions
Author.where(:name).contains("Emily").and(cdq(:pub_count).gt(100).or.lt(10))
# Relationships
Author.first.publications.offset(2).limit(1)
cdq(emily_dickinson).publications.where(:type).eq('poetry')
# Sorting
Author.sort_by(:name)
Author.sort_by(:name, order: :descending, case_insensitive: true)
Scopes
class Author < CDQManagedObject
scope :prolific, where(:pub_count).gt(50)
end
Operators
Many short-form operators also have verbose equivalents.
eq (equal)
ne (not_equal)
lt (less_than)
le (less_than_or_equal)
gt (greater_than)
ge (greater_than_or_equal)
contains
matches
in
begins_with
ends_with
between
Accessors
These methods pull you out of CDQ-land and return actual objects or values. They go at the end of a query or scope and will force execution.
array (to_a)
first
map
each
[]