GithubHelp home page GithubHelp logo

cdq's Introduction

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 is maintained by Infinite Red, a web and mobile development company based in Portland, OR and San Francisco, CA.

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.

I am actively developing and improving CDQ (updated February 2015) so if you have trouble or find a bug, please open a ticket!

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('Shakespeare')
  Author.where(:publish_count).gt(10)
  Author.where(name: 'Shakespeare', publish_count: 15)
  Author.where("name LIKE %@", '*kesp*')
  Author.where("name LIKE %@", 'Shakespear?')

Sorts, Limits and Offsets

  Author.sort_by(:created_at).limit(1).offset(10)
  Author.sort_by(:created_at, order: :descending)
  Author.sort_by(:created_at, case_insensitive: true)

Conjunctions

  Author.where(:name).eq('Blake').and(:first_published).le(Time.local(1700))

  # Multiple comparisons against the same attribute
  Author.where(:created_at).ge(yesterday).and.lt(today)

Nested Conjunctions

  Author.where(:name).contains("Emily").and(cdq(:pub_count).gt(100).or.lt(10))

Calculations

  Author.sum(:fee)
  Author.average(:fee)
  Author.min(:fee)
  Author.max(:fee)
  Author.where(:name).eq("Emily").sum(:fee)

Fetching

Like ActiveRecord, CDQ will not run a fetch until you actually request specific objects. There are several methods for getting at the data:

  • array
  • first
  • last
  • each
  • []
  • map
  • Anything else in Enumerable

Dedicated Models

If you're using CDQ in a brand new project, you'll probably want to use dedicated model classes for your entities. familiar-looking and natural syntax for queries and scopes:

  class Author < CDQManagedObject
  end

Named Scopes

You can save up partially-constructed queries for later use using named scopes, even combining them seamlessly with other queries or other named scopes:

  class Author < CDQManagedObject
    scope :a_authors, where(:name).begins_with('A')
    scope :prolific, where(:publish_count).gt(99)
  end

  Author.prolific.a_authors.limit(5)

Using CDQ with a pre-existing model

If you have an existing app that already manages its own data model, you can still use CDQ, overriding its stack at any layer:

cdq.setup(context: App.delegate.mainContext) # don't set up model or store coordinator
cdq.setup(store: App.delegate.persistentStoreCoordinator) # Don't set up model
cdq.setup(model: App.delegate.managedObjectModel) # Don't load model

You cannot use CDQManagedObject as a base class when overriding this way, you'll need to use the master method, described below. If you have an existing model and want to use it with CDQManagedObject without changing its name, You'll need to use a cdq.yml config file. See CDQConfig.

Working without model classes using the master method

If you need or want to work without using CDQManagedObject as your base class, you can use the cdq()master method. This is a "magic" method, like rmq() in RubyMotionQuery or $() in jQuery, which will lift whatever you pass into it into the CDQ universe. The method is available inside all UIResponder classes (so, views and controllers) as well as in the console. You can use it anywhere else by including the model CDQ into your classes. To use an entity without a model class, just pass its name as a string into the master method, like so

  cdq('Author').where(:name).eq('Shakespeare')
  cdq('Author').where(:publish_count).gt(10)
  cdq('Author').sort_by(:created_at).limit(1).offset(10)

Anything you can do with a model, you can also do with the master method, including defining and using named scopes:

  cdq('Author').scope :a_authors, cdq(:name).begins_with('A')
  cdq('Author').scope :prolific, cdq(:publish_count).gt(99)

NOTE: strings and symbols are NOT interchangeable. cdq('Entity') gives you a query generator for an entity, but cdq(:attribute) starts a predicate for an attribute.

Reserved model attributes

CDQ 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

Example:

schema "0001 initial" do
  entity "Author" do
    string :name, optional: false

    datetime :created_at
    datetime :updated_at
  end
end
a = Author.create(name: "Le Guin")
# Notice that the properties aren't set yet
#
# <Author: 0x1175f9540> (entity: Author; id: 0x117504810
# <x-coredata:///Author/tA4E22210-72CF-4272-BF2C-0C5C63A55B072> ; data: {
#     name: "Le Guin";
#     created_at: nil;
#     updated_at: nil;
# })

cdq.save

puts a # Original reference to created Author object
# <Author: 0x1175f9540> (entity: Author; id: 0x117504810
# <x-coredata:///Author/tA4E22210-72CF-4272-BF2C-0C5C63A55B072> ; data: {
#     name: "Le Guin";
#     created_at: 2015-08-19 20:44:40 +0000;
#     updated_at: 2015-08-19 20:44:40 +0000;
# })

a.name = "Some Other Guy"
puts a
# Note that nothing has changed except the name:
#
# <Author: 0x1175f9540> (entity: Author; id: 0x117504810
# <x-coredata:///Author/tA4E22210-72CF-4272-BF2C-0C5C63A55B072> ; data: {
#     name: "Some Other Guy";
#     created_at: 2015-08-19 20:44:40 +0000;
#     updated_at: 2015-08-19 20:44:40 +0000;
# })

cdq.save
puts a
# <Author: 0x1175f9540> (entity: Author; id: 0x117504810
# <x-coredata:///Author/tA4E22210-72CF-4272-BF2C-0C5C63A55B072> ; data: {
#     name: "Some Other Guy";
#     created_at: 2015-08-19 20:44:40 +0000;
#     updated_at: 2015-08-19 20:47:40 +0000;
# })

Also note that you should never use object_id as a model attribute as it will conflict with an internally generated property.

iCloud

Removed as of version 2.0.0. If you still need this, pin cdq gem to before version 2.0.0

As of version 0.1.10, there is some experimental support for iCloud, written by @katsuyoshi. Please try it out and let us know how it's working for you. To enable, initialize like this:

  cdq.stores.new(iCloud: true, container: "com.your.container.id")

You can also set up iCloud in your cdq.yml file.

Documentation

Things that are currently missing

  • There is no facility for custom migrations yet
  • There are no explicit validations (but you can define them on your data model)
  • Lifecycle Callbacks or Observers

Tips

If you need, you could watch SQL statements by setting the following launch argument through args environment variable:

$ rake args='-com.apple.CoreData.SQLDebug 3'

com.apple.CoreData.SQLDebug takes a value between 1 and 3; the higher the value, the more verbose the output.

Premium Support

CDQ, as an open source project, is free to use and always will be. Infinite Red offers premium CDQ support and general mobile app design/development services. Email us at [email protected] to get in touch with us for more details.

cdq's People

Contributors

andrewhavens avatar cmckni3 avatar digitalfx avatar digitalmoksha avatar gantman avatar iurimatias avatar jamonholmgren avatar jonmorehouse avatar katsuyoshi avatar kemiller avatar kevinvangelder avatar markrickert avatar nanamiwang avatar no-itsbackpack avatar paulca avatar scottmills avatar skandragon avatar toshiwo avatar twerth avatar watson1978 avatar willrax avatar wndxlori avatar y8 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cdq's Issues

case-insensitive sorting

I don't see a way to do a case insensitive query. For instance, I would love to replace this:

fetch_request = NSFetchRequest.alloc.init
entity = NSEntityDescription.entityForName("Tag", inManagedObjectContext: cdq.contexts.current)
fetch_request.setEntity(entity)

name_desc = NSSortDescriptor.alloc.initWithKey("name", ascending: true, selector: :caseInsensitiveCompare)
fetch_request.setSortDescriptors([ name_desc ])

with something like this:

query = Tag.sort_by(:name, case_insensitive: true)
fetch_request = query.fetch_request

The syntax doesn't matter much, but I know this is tricky because adding options to the search terms will make things... messy. However, I do believe in Ruby 1.9 and higher, keys are stored in the order added, so one could perhaps do:

query = Tag.sort_by(name: { case_insensitive: true }, city: {})

Silent crash saving models with validations

I am not yet sure if this is a problem with cdq, or RubyMotion.

I have a model with a string field called "name", and am attempting to write a validation for it. However, when cdq.save is called, the application silently exits (according to the console)

class Tag < CDQManagedObject
  def validateName(valuePointer, error: errorPtr)
    puts valuePointer[0].inspect
    true
  end
end

It works so long as I don't actually touch valuePointer in any way. it will crash with any of these:

puts valuePointer.inspect
puts valuePointer[0].inspect
NSLog(@"%@", valuePointer)
NSLog(@"%@", valuePointer[0])

Documentation: sort_by doesn't work with order: parameter

In using CDQ (which is great by the way), I noticed that the readme states you can change the sort order like this:

Author.sort_by(:created_at, order: :descending)

However, for me it always sorts in ascending order. I have to do this to make it work:

Author.sort_by(:created_at, :descending)

NSFetchedResultsController error on iPhone-6 and 6-plus.

I am currently facing an error on the iphone6 and iphone 6-plus on my app. I am using the cdq gem in conjunction with Restkit to fetch data from my backend and store in core-data. Everything works fine and dandy on the iPhone5/5s/5c but run into an nsfetchedresults controller error on the newer devices.

StackTrace

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0xc000000000010016
Triggered by Thread:  0

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   sight                           0x0000000100478cc0 __unnamed_146 + 88
1   CoreData                        0x00000001879d05ec __77-[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:]_block_invoke + 3564
2   CoreData                        0x000000018795526c developerSubmittedBlockToNSManagedObjectContextPerform + 196
3   CoreData                        0x0000000187955470 -[NSManagedObjectContext performBlockAndWait:] + 228
4   CoreData                        0x00000001878e3e34 -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] + 120
5   CoreFoundation                  0x0000000187ba8ae0 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 16
6   CoreFoundation                  0x0000000187ae721c _CFXNotificationPost + 2056
7   Foundation                      0x00000001889e6cbc -[NSNotificationCenter postNotificationName:object:userInfo:] + 68
8   CoreData                        0x00000001878e3d90 -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] + 84
9   CoreData                        0x0000000187954df8 -[NSManagedObjectContext _mergeChangesFromDidSaveDictionary:usingObjectIDs:] + 3388
10  CoreData                        0x00000001879550b4 -[NSManagedObjectContext mergeChangesFromContextDidSaveNotification:] + 484
11  sight                           0x00000001001e075c __unnamed_162 + 84
12  sight                           0x0000000100938a7c rb_vm_dispatch + 5636
13  sight                           0x00000001001c3c60 vm_dispatch + 992
14  sight                           0x00000001004798fc rb_scope__context_did_save:__block__ + 532
15  sight                           0x000000010093a1f4 vm_block_eval(RoxorVM*, rb_vm_block*, objc_selector*, unsigned long, int, unsigned long const*) + 1240
16  sight                           0x0000000100920f3c rb_gcd_block_dispatcher + 44
17  libdispatch.dylib               0x000000019899d368 _dispatch_client_callout + 12
18  libdispatch.dylib               0x00000001989a197c _dispatch_main_queue_callback_4CF + 928
19  CoreFoundation                  0x0000000187bb9fa0 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 8
20  CoreFoundation                  0x0000000187bb8048 __CFRunLoopRun + 1488
21  CoreFoundation                  0x0000000187ae50a0 CFRunLoopRunSpecific + 392
22  GraphicsServices                0x0000000190c875a0 GSEventRunModal + 164
23  UIKit                           0x000000018c41a3bc UIApplicationMain + 1484
24  sight                           0x00000001001cb820 main (main.mm:15)
25  libdyld.dylib                   0x00000001989c6a04 start + 0

Thread 1 name:  Dispatch queue: com.apple.libdispatch-manager
Thread 1:
0   libsystem_kernel.dylib          0x0000000198ac4c94 kevent64 + 8
1   libdispatch.dylib               0x00000001989ac97c _dispatch_mgr_invoke + 272
2   libdispatch.dylib               0x000000019899f3b0 _dispatch_mgr_thread + 48

Thread 2:
0   libsystem_kernel.dylib          0x0000000198adfc78 __workq_kernreturn + 8
1   libsystem_pthread.dylib         0x0000000198b79390 _pthread_wqthread + 988
2   libsystem_pthread.dylib         0x0000000198b78fa4 start_wqthread + 0

Thread 3 name:  AFNetworking
Thread 3:
0   libsystem_kernel.dylib          0x0000000198ac4e7c mach_msg_trap + 8
1   libsystem_kernel.dylib          0x0000000198ac4cf4 mach_msg + 68
2   CoreFoundation                  0x0000000187bb9ecc __CFRunLoopServiceMachPort + 196
3   CoreFoundation                  0x0000000187bb7e20 __CFRunLoopRun + 936
4   CoreFoundation                  0x0000000187ae50a0 CFRunLoopRunSpecific + 392
5   Foundation                      0x00000001889ed800 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 312
6   Foundation                      0x0000000188a47ef4 -[NSRunLoop(NSRunLoop) run] + 92
7   sight                           0x00000001000c00a4 +[AFURLConnectionOperation networkRequestThreadEntryPoint:] (AFURLConnectionOperation.m:189)
8   Foundation                      0x0000000188ad5c08 __NSThread__main__ + 1068
9   libsystem_pthread.dylib         0x0000000198b7be7c _pthread_body + 160
10  libsystem_pthread.dylib         0x0000000198b7bdd8 _pthread_start + 156
11  libsystem_pthread.dylib         0x0000000198b78fac thread_start + 0

Thread 4 name:  com.apple.NSURLConnectionLoader
Thread 4:
0   libsystem_kernel.dylib          0x0000000198ac4e7c mach_msg_trap + 8
1   libsystem_kernel.dylib          0x0000000198ac4cf4 mach_msg + 68
2   CoreFoundation                  0x0000000187bb9ecc __CFRunLoopServiceMachPort + 196
3   CoreFoundation                  0x0000000187bb7e20 __CFRunLoopRun + 936
4   CoreFoundation                  0x0000000187ae50a0 CFRunLoopRunSpecific + 392
5   CFNetwork                       0x00000001875ea4e4 +[NSURLConnection(Loader) _resourceLoadLoop:] + 436
6   Foundation                      0x0000000188ad5c08 __NSThread__main__ + 1068
7   libsystem_pthread.dylib         0x0000000198b7be7c _pthread_body + 160
8   libsystem_pthread.dylib         0x0000000198b7bdd8 _pthread_start + 156
9   libsystem_pthread.dylib         0x0000000198b78fac thread_start + 0

Thread 5 name:  com.apple.CFSocket.private
Thread 5:
0   libsystem_kernel.dylib          0x0000000198adf498 __select + 8
1   CoreFoundation                  0x0000000187bbf8c4 __CFSocketManager + 656
2   libsystem_pthread.dylib         0x0000000198b7be7c _pthread_body + 160
3   libsystem_pthread.dylib         0x0000000198b7bdd8 _pthread_start + 156
4   libsystem_pthread.dylib         0x0000000198b78fac thread_start + 0

Thread 6:
0   libsystem_kernel.dylib          0x0000000198adfc78 __workq_kernreturn + 8
1   libsystem_pthread.dylib         0x0000000198b79390 _pthread_wqthread + 988
2   libsystem_pthread.dylib         0x0000000198b78fa4 start_wqthread + 0

Thread 7:
0   libsystem_kernel.dylib          0x0000000198adfc78 __workq_kernreturn + 8
1   libsystem_pthread.dylib         0x0000000198b79390 _pthread_wqthread + 988
2   libsystem_pthread.dylib         0x0000000198b78fa4 start_wqthread + 0

Thread 8:
0   libsystem_kernel.dylib          0x0000000198adfc78 __workq_kernreturn + 8
1   libsystem_pthread.dylib         0x0000000198b79390 _pthread_wqthread + 988
2   libsystem_pthread.dylib         0x0000000198b78fa4 start_wqthread + 0

Rakefile

 app.pods do
    pod 'SDWebImage',     '3.7.1'
    pod 'RestKit',        '0.24.0'
    pod 'RestKit/Testing'
    pod 'DACircularProgress'
    pod 'NYSegmentedControl'
    pod 'Facebook-iOS-SDK'
    pod 'SSKeychain'
end

Controller Code

...
  def viewDidload
    ...
    fetch_controller.delegate = self
    unless fetch_controller.performFetch(error_ptr)
      raise "Error performing fetch: #{error_ptr[2].description}"
    end
    ...
  end

  def posts
    @posts ||= feed.posts.sort_by(:created_at, order: :descending)
  end

  def fetch_controller
    @fetch_controller ||= NSFetchedResultsController.alloc.initWithFetchRequest(
      posts.fetch_request,
      managedObjectContext: posts.context,
      sectionNameKeyPath: nil,
      cacheName: nil
    )
  end

 def controllerWillChangeContent(controller)
    NSLog("BEGIN UPDATE")
    feed_table_view.beginUpdates
    NSLog("BEGIN UPDATED")
  end

  def controller(controller, didChangeObject: post, atIndexPath: index_path, forChangeType: change_type, newIndexPath: new_index_path)
    case change_type
      when NSFetchedResultsChangeInsert
        NSLog("Fetch Result Insert !!!")
        feed_table_view.insertRowsAtIndexPaths([new_index_path], withRowAnimation: UITableViewRowAnimationBottom)
        NSLog("Fetch Result Inserted !!!")
      when NSFetchedResultsChangeDelete
        feed_table_view.deleteRowsAtIndexPaths([index_path], withRowAnimation: UITableViewRowAnimationMiddle)
      when NSFetchedResultsChangeUpdate
        NSLog("Fetch Result Update !!!")
        cell = feed_table_view.cellForRowAtIndexPath(index_path)
        NSLog("Fetch Result Update 1 !!!")
        return unless cell
        cell.set_cell_information(feed_table_view, post, index_path)
        NSLog("Fetch Result Updated 2 !!!")
    end
  end

  def controllerDidChangeContent(controller)
    NSLog("DID UPDATE")
    feed_table_view.endUpdates
    NSLog("END UPDATED")
  end

  def context_did_save(notification)
    Dispatch::Queue.main.async do 
          fetch_controller.managedObjectContext.mergeChangesFromContextDidSaveNotification(notification)
    end
 end
...

On older devices I get back all the log statements but on an iPhone6/6plus it only makes it as far as NSLog("BEGIN UPDATE").
Any sort of help is welcomed.
I also posted the same question on the rubymotion google group and was told to post it here also.
https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!msg/rubymotion/uitBOXCoCs8/baWbscPh4SUJ

Issues with contexts/threading

I have a "delete" action that removes a photoset, which in turn deletes all associated photos (this is dumbed down a bit):

class Photoset < CDQManagedObject
  def destroy
    photos.each(&:destroy)
    super
  end
end

class Photo < CDQManagedObject
  def destroy
    # Delete some other nested associations and do some other cleanup
    super
  end
end

If I call photoset.destroy everything works fine, but it blocks the UI thread. I first threw my code in a Dispatch::Queue.concurrent.async block so my progress spinner can keep spinning, and it kind of works... sometimes. One attempt will work fine, but other times it will crash with an EXC_BAD_ACCESS (SIGSEGV) exception relating to Core Data, always while inside the destroy method of one of the Photo objects:

Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
Exception Codes:       EXC_I386_GPFLT

Application Specific Information:
objc_msgSend() selector name: mutableCopyWithZone:
CoreSimulator 110.4 - Device: iPhone 6 - Runtime: iOS 8.1 (12B411) - DeviceType: iPhone 6

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libobjc.A.dylib                 0x0000000100d7b00b objc_msgSend + 11
1   CoreData                        0x0000000103506135 -[NSManagedObject(_NSInternalMethods) _newPropertiesForRetainedTypes:andCopiedTypes:preserveFaults:] + 549
2   CoreData                        0x0000000103505efa -[NSManagedObject(_NSInternalMethods) _newAllPropertiesWithRelationshipFaultsIntact__] + 90
3   CoreData                        0x0000000103505de8 -[NSManagedObjectContext(_NSInternalChangeProcessing) _establishEventSnapshotsForObject:] + 72
4   CoreData                        0x0000000103505ce5 _PFFastMOCObjectWillChange + 229
5   CoreData                        0x000000010351cebf _PF_ManagedObject_WillChangeValueForKeywithSetMutation + 159
6   CoreData                        0x0000000103523227 -[NSManagedObject(_NSInternalMethods) _excludeObject:fromPropertyWithKey:andIndex:] + 647
7   CoreData                        0x000000010351c49b -[NSManagedObject(_NSInternalMethods) _maintainInverseRelationship:forProperty:oldDestination:newDestination:] + 251
8   CoreData                        0x00000001035209a6 -[NSManagedObject(_NSInternalMethods) _propagateDelete:] + 902
9   CoreData                        0x000000010352042f -[NSManagedObjectContext(_NSInternalChangeProcessing) _propagateDeletesUsingTable:] + 479
10  CoreData                        0x00000001035201e4 -[NSManagedObjectContext(_NSInternalChangeProcessing) _processDeletedObjects:] + 1044
11  CoreData                        0x000000010350710d -[NSManagedObjectContext(_NSInternalChangeProcessing) _propagatePendingDeletesAtEndOfEvent:] + 109
12  CoreData                        0x0000000103502efb -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] + 1659
13  CoreData                        0x00000001034dbd26 _performRunLoopAction + 310

So I took a stab at contexts instead, using info I found in the issues here and the recent wiki page about threading:

cdq.contexts.new(NSPrivateQueueConcurrencyType) do
  context = cdq.contexts.current
  context.performBlock(-> {
    cdq.contexts.push(context) do
      photoset.destroy
      cdq.save(always_wait: true)
      Dispatch::Queue.main.async do
        cdq.save
        navigationController.popViewControllerAnimated(true)
      end
    end
  })
end

Same as using Dispatch::Queue -- works ok sometimes, crashes others, never in the same spot.

Did I come across a bug in either CDQ or CoreData, or am I just doing something stupid (probably the latter!)

Default Values

It seems that default values aren't respected with the CDQManagedObject. When I declare them in the schema and create a new object, everything is nil

schema "0001 initial" do
  entity "Connection" do
    string :id, default: "id"
  end
end

class Connection < CDQManagedEntity do
end

obj = Connection.create
obj.id 
#=> nil

Digging into the generated xcdmodeldata objects, created by ruby-xcdm, it looks like the defaults are created, just not respected within the object

Memory occupation

I have a project something like this:
https://github.com/rokugou/cdq-test

When i created object after all operations.
without association
screen shot 2014-04-30 at 10 14 03 am

with association
screen shot 2014-04-30 at 10 20 54 am

Memory occupation like explosion.

I have the following questions:
1.
If there are any way to solve the memory occupation?

If i destroy all the man data, and insert again with association.
I need to update the apple reference, but how?

I tried to add man_id to apple and iterate all the apple and update reference.
Are there any better way to do this?

When i created object with association in the loop, and save after the loop.
The association reference broken.

I am new to Rubymotion and Objective-C, if there are any stupid questions, sorry about that. Thank you.

Problem with NSPredicate.predicateWithFormat after upgrade to RM 2.30

Hi, I had some code that worked without incident in RM 2.29. It looks like this:

      def with_predicate(fetch_request)
        fetch_request.predicate = NSPredicate.predicateWithFormat(
          "(shopper_id != -1) and (ANY shopper_visit.time_exited > %@)",
          NSDate.dateWithTimeIntervalSinceNow(0)
        )
        fetch_request
      end

Running under 2.29, the above code works, but under RM 2.30 I get the following error:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid predicate: nil RHS'

I've isolated the failure to the above code. Replacing the predicate with:

      def with_predicate(fetch_request)
        fetch_request.predicate = NSPredicate.predicateWithFormat(
          "(shopper_id != -1)"
        )
        fetch_request
      end

will work, but it's obviously useless because it doesn't do what I want.

Any ideas?

Output for #inspect is strange

This is all somewhat strange output, since "created_at" is put in quotes, CostHere, which is a string, is not, but "5.1", which is a float, is in quotes.

This is of course low priority, but might be something useful to fix, as it makes CostHere look like a class to the Ruby mind.

=> <Item: 0xe554d10> (entity: Item; id: 0xe1a7700 <x-coredata://C33D4E86-0EEF-4BB0-8638-1D4B8F104610/Item/p1> ; data: {
    cost = CostHere;
    "created_at" = nil;
    descr = nil;
    images = "<relationship fault: 0xe05ed80 'images'>";
    name = Asd;
    quantity = "5.1";
    sectionIdentifier = A;
    tags = "<relationship fault: 0xe063dd0 'tags'>";
    thumbnailImage = nil;
    "updated_at" = nil;
})

quick start doesn't work on osx

I followed quick start, it works well in ios, but not osx, when running rake, it returns following error

2014-05-14 21:26:31.355 cdqsample-osx[36822:303] model.rb:50:in `load_model': No model file.  Cannot create an NSManagedObjectModel without one. (RuntimeError)
    from model.rb:13:in `current'
    from cdq.rb:58:in `cdq:'
    from managed_object.rb:32:in `inherited:'
2014-05-14 21:26:31.357 cdqsample-osx[36822:303] *** Terminating app due to uncaught exception 'RuntimeError', reason: 'model.rb:50:in `load_model': No model file.  Cannot create an NSManagedObjectModel without one. (RuntimeError)
    from model.rb:13:in `current'
    from cdq.rb:58:in `cdq:'
    from managed_object.rb:32:in `inherited:'

any idea?

NSDate comparison not equal

I have an API response and a Core Data object:

api_response['created_at'] # => 2014-08-25 22:52:21 -0700
my_model.created_at # => 2014-08-25 22:52:21 -0700

I can't query by this date:

MyModel.where(created_at: api_response['created_at']).first # => nil

The two objects aren't equal, despite being apparently equal:

# both are NSDate objects
my_model.created_at == api_response['created_at'] # => false
# yet they are equal if converted to strings
my_model.created_at.to_s == api_response['created_at'].to_s # => true

Any ideas on how I could query on this field effectively?

NoMethodError when deleting entity

I'm trying to delete a record with the following code

place = Place.where(:title).eq(title)
place.delete
cdq.save

title is not nil. Here is the error from the terminal:

*** Terminating app due to uncaught exception 'NoMethodError', reason: 'targeted_query.rb:152:in `method_missing:': undefined method `delete' for #<CDQ::CDQTargetedQuery:0x10bb2520 ...> (NoMethodError)

When I write the following, I get the same error as above.

place = Place.where(:title).eq('Car') #car is a existing title
place.delete
cdq.save

I as well can't access place.title.

But when I write the following, it works though:

place = Place.first
place.delete
cdq.save

CDQManagedObject#save doesn't update and NSFetchedResultsController

I have a UITableViewController with a NSFetchedResultsController datasource. If I update data using a CDQManagedObject:

event = Event.where(foo:bar).first
event.title = "A new title"
event.save

the fetchedObjects of my NSFetchedResultsController instance are not updated.

Using

event = cdq('Event').where(foo:bar).first
event.title = "A better title"

immediately updates the fetchedObjects of my NSFetchedResultsController instance.

Thus when using the cdqmanagedobject syntax I have to manually re fetch data into my NSFetchedResultsController instance, like this:

    NSFetchedResultsController.deleteCacheWithName "MyCacheName"
    error_ptr = Pointer.new(:object)
    @fetched_results_controller.performFetch(error_ptr)
    @table.reloadData

It shouldn't do this, right?

Notes:

  • I use controller:didChangeObject:atIndexPath:forChangeType:newIndexPath to listen for changes to the event objects.
  • in viewDidLoad I define @table as a UITableView
  • in viewDidLoad I define @fetched_results_controller = NSFetchedResultsController.alloc.initWithFetchRequest(fetch_request,managedObjectContext:cdq.contexts.all.first,sectionNameKeyPath:nil,cacheName:"EventsDataViewCache")

Subclass of CDQManagedObject reported as "not subclass of NSManagedObject"

I'm seeing very strange behavior from a single class in a project with ca 10 classes backed with cdq.

With the following class-definition:

class Player < CDQManagedObject
# Random stuff here, but no subclass implementations of #new, #description or other stuff. 
end

and the following schema:

  entity "Player" do 
    string    :id
    string    :name

    has_one :character
    has_one :party, inverse: "Party.players"
    has_one :question, inverse: "Question.players"

    # Add meetings here!
  end

and these conditions:

Player.ancestors => [Player, CDQManagedObject, CDQ, CoreDataQueryManagedObjectBase, NSManagedObject, NSObject, Kernel]

and

Player.ancestors.include?(NSManagedObject) => true
Player.ancestors.include?(CDQManagedObject) => true

trying to instantiate a player with:

p = Player.new(:id => "monkey", :name => "random") => #<NSException:0xe156cd0>

as well as

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '"Player" is not a subclass of NSManagedObject.'

I have a bunch of other classes behaving according to docs, except this one?

Using cdq 0.1.8 and RubyMotion 2.26

Immutable NSDictionary

The transformable type should really return immutable data types, such as NSDictionary rather than Hash (which is ~= NSMutableDictionary). This is because mutating a transformable type doesn't tell the context that anything has changed.

# Mutating does not work
m = MyModel.first
m.my_dictionary # => {}
m.my_dictionary[:wat] = "Wat" # => { :wat => "Wat" }
cdq.save
MyModel.first.my_dictionary # => {}

# Assignment does work
m = MyModel.first
m.my_dictionary = { wat: "Wat" }
cdq.save
MyModel.first.my_dictionary # => { :wat => "Wat" }

I haven't dug into the internals of CDQ yet to see how this would be possible, but feel free to tell me if this seems easy or not. To turn a hash into an immutable NSDictionary is easy: {}.copy is now immutable.

Invalid behavior from entitiesByName on OSX

So, this might not be the correct place to report this, but I know too little about this space to know where the correct place is.

Given a schema like:

schema "0001 initial" do
  entity "Track" do
    string :title
    string :artist
    string :album
  end
  entity "PlayWeight" do
    string :type
    integer32 :value
  end
end

You can generate the models using cdq create and have rake build the schema and it works fine on iOS.

If you try to do the same on OSX, you get

cdq.rb:58:in `cdq:': Cannot find an entity named Track (RuntimeError)
 from managed_object.rb:32:in `inherited:'

at compile time.

I tried to do some digging to figure out why. You can get past the error and into the REPL by removing the model files.

What I eventually got to was:

(main)>  ss = CDQ.class_variable_get(:@@base_object)
=> #<CDQ::CDQObject:0x7faedd82e5f0>
(main)> ss.models.current.entitiesByName
=> {"PlayWeight"=>#<NSEntityDescription:0x7faedd909910>, "Track"=>#<NSEntityDescription:0x7faedd9099c0>}
(main)> ss.models.current.entitiesByName["PlayWeight"]
=> #<NSEntityDescription:0x7faedd909910>
(main)> ss.models.current.entitiesByName["Track"]
=> nil

It looks like the Entities are getting loaded, but for some unknown reason the hash returned by entitiesByName doesn't find some of its own keys.

I think this is unexpected, and I think this is the cause of my error, but I don't know enough about CoreData and friends to know if this is being caused by me missing something fundamental.

Other things of interest:

(main)> ss.models.current.entitiesByName.keys.map {|key| k.class}
=> [String, String]
(main)> ss.models.current.entitiesByName.class
=> Hash
(main)> ss.models.current.entitiesByName.keys.last == "Track"
=> true
(main)> ss.models.current.entitiesByName.clone["Track"]  #<=== Note .clone
=> #<NSEntityDescription:0x7faedd9099c0>

Any ideas? I just discovered that calling clone on the hash suddenly makes it work, and I'm even more confused.

Example of using cdq as a datasource for a table view?

I'm searching for a full featured yet tableview data source compatible core data wrapper. So far, the ones I like seem to not support tableview, and the ones I dislike do. :)

Is there an example of how to use cdq, complete with table view sectioning and so forth, without loading all records?

Thanks!

Floating point accuracy "fix"

For your linking, should anyone need this, or you want something similar integrated into cdq at some point:

This is a quickly thrown together string-to-integer mapper that allows one to specify the precision (number of decimal places) to return. It works well enough for my uses, where I am ultimately using strings anyway for views.

https://github.com/skandragon/stringify_float

The underlying core data field should be an integer32 or integer64 to act as backing store for the strings. integer64 is likely large enough for just about anyone, but integer32 will hold most common values.

Can't remove a relationship

Sorry if I missed it in the docs, specs and code, but I can't figure out how to remove a relationship between objects. I don't want to remove either object just the relationship between them. In my case, it is a many-to-many relationship.

How is the best way to accomplish this?

Thank you.

Query Result caching best practices

I am calling .all on my CDQManagedObject class and then I iterate over the result set a couple of different times using .each. When I have SQLite debugging enabled via:

rake args="-com.apple.CoreData.SQLDebug 1"

So I can actually see the queries being generated I can see that each iteration of .each causes a new SELECT query to be executed. Wouldn't CoreData / cdq just do that once and then use the cached result on subsequent iterations?

Reproducible from the REPL:

a = Account.all
a.each { |c| puts c.name } # would need to adjust for your object model
# at this point you can see it run the query
a.each { |c| puts c.name } # would need to adjust for your object model
# it now runs another query

Is there something I am missing?

Thank you.

Casting of boolean values

Boolean values are stored as 0/1, and that's what CDQ returns when you query an object. Since ruby treats 0 as a truthy value, you can't do something like if person.happy, but instead have to do if person.happy == 1. I'm new to CDQ, so I'm not sure if this is something that should be cast to true/false automatically?

Sample schema:

schema "0001 initial" do
  entity "Person" do
    boolean :happy
  end
end

Sample REPL code:

(main)> p = Person.create(happy: false)
=> <Person: 0xb622490> (entity: Person; id: 0xb621a60 <x-coredata:///Person/tE660D344-00B6-4A86-A0BE-84A6EE30E7324> ; data: {
    happy = 0;
})
(main)> p.happy
=> 0
(main)> p.happy = true
=> true
(main)> p.happy
=> 1

External Storage

Hi, is there a way to give binary data entities the "external storage" option, as it is possible in Xcode? Or is there a more recommended way to save images in RubyMotion? Thanks!

YAML Error

When providing a custom cdq.yml file in the resources directory, running the application results in this crash:

(main)> 2014-06-19 12:02:55.575 Closer[35150:70b] config.rb:32:in `block in initialize:': uninitialized constant CDQ::CDQConfig::YAML (NameError)
    from config.rb:32:in `initialize:'
    from config.rb:62:in `default'
    from model.rb:9:in `initialize:'
    from object.rb:17:in `models'
    from cdq.rb:55:in `cdq:'
    from managed_object.rb:32:in `inherited:'
2014-06-19 12:02:55.591 Closer[35150:70b] *** Terminating app due to uncaught exception 'NameError', reason: 'config.rb:32:in `block in initialize:': uninitialized constant CDQ::CDQConfig::YAML (NameError)
    from config.rb:32:in `initialize:'
    from config.rb:62:in `default'
    from model.rb:9:in `initialize:'
    from object.rb:17:in `models'
    from cdq.rb:55:in `cdq:'
    from managed_object.rb:32:in `inherited:'
'
*** First throw call stack:
(
    0   CoreFoundation                      0x034081e4 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x013618e5 objc_exception_throw + 44
    2   Closer                              0x00c4681a _ZL10__vm_raisev + 346
    3   Closer                              0x00c468f8 rb_vm_raise + 216
    4   Closer                              0x00b49e71 rb_exc_raise + 17
    5   Closer                              0x00b436b7 rb_name_error + 135
    6   Closer                              0x00bff295 rb_mod_const_missing + 165
    7   Closer                              0x00c3b69d _ZL20dispatch_rimp_callerPFP11objc_objectS0_P13objc_selectorzEmS1_iPKm + 46445
    8   Closer                              0x00c220ba rb_vm_dispatch + 6554
    9   Closer                              0x00c00106 rb_const_get_0 + 1046
    10  Closer                              0x00c00200 rb_const_get + 32
    11  Closer                              0x00c3e7d2 rb_vm_const_lookup_level + 338
    12  Closer                              0x003dabc3 vm_get_const + 227
    13  Closer                              0x003e39b4 rb_scope__initialize:__block__ + 100
    14  Closer                              0x00c2ff8b _ZL20dispatch_bimp_callerPFP11objc_objectS0_P13objc_selectorzEmS1_mP11rb_vm_blockiPKm + 46507
    15  Closer                              0x00c233b1 _ZL13vm_block_evalP7RoxorVMP11rb_vm_blockP13objc_selectormiPKm + 1137
    16  Closer                              0x00c2373d rb_vm_yield_args + 77
    17  Closer                              0x00c17553 rb_yield + 67
    18  Closer                              0x00c46a28 rb_ensure + 24
    19  Closer                              0x00b63cb2 rb_io_s_open + 82
    20  Closer                              0x00c21a27 rb_vm_dispatch + 4871
    21  Closer                              0x003db0bc vm_dispatch + 1100
    22  Closer                              0x003e31bb rb_scope__initialize:__ + 795
    23  Closer                              0x00c3b69d _ZL20dispatch_rimp_callerPFP11objc_objectS0_P13objc_selectorzEmS1_iPKm + 46445
    24  Closer                              0x00c220ba rb_vm_dispatch + 6554
    25  Closer                              0x00b7f147 rb_class_new_instance0 + 823
    26  Closer                              0x00b7ee07 rb_class_new_instance_imp + 23
    27  Closer                              0x00c21a27 rb_vm_dispatch + 4871
    28  Closer                              0x003db0bc vm_dispatch + 1100
    29  Closer                              0x003e42aa rb_scope__default__ + 458
    30  Closer                              0x00c3b69d _ZL20dispatch_rimp_callerPFP11objc_objectS0_P13objc_selectorzEmS1_iPKm + 46445
    31  Closer                              0x00c220ba rb_vm_dispatch + 6554
    32  Closer                              0x003e96cc vm_dispatch + 1100
    33  Closer                              0x003f1559 rb_scope__initialize:__ + 233
    34  Closer                              0x00c3b69d _ZL20dispatch_rimp_callerPFP11objc_objectS0_P13objc_selectorzEmS1_iPKm + 46445
    35  Closer                              0x00c220ba rb_vm_dispatch + 6554
    36  Closer                              0x00b7f147 rb_class_new_instance0 + 823
    37  Closer                              0x00b7ee07 rb_class_new_instance_imp + 23
    38  Closer                              0x00c21a27 rb_vm_dispatch + 4871
    39  Closer                              0x0040ac7c vm_dispatch + 1100
    40  Closer                              0x004131ae rb_scope__models__ + 206
    41  Closer                              0x00c3b69d _ZL20dispatch_rimp_callerPFP11objc_objectS0_P13objc_selectorzEmS1_iPKm + 46445
    42  Closer                              0x00c220ba rb_vm_dispatch + 6554
    43  Closer                              0x004737cc vm_dispatch + 1100
    44  Closer                              0x0047bc4f rb_scope__cdq:__ + 959
    45  Closer                              0x00c3b69d _ZL20dispatch_rimp_callerPFP11objc_objectS0_P13objc_selectorzEmS1_iPKm + 46445
    46  Closer                              0x00c220ba rb_vm_dispatch + 6554
    47  Closer                              0x0048294c vm_dispatch + 1100
    48  Closer                              0x0048ac39 rb_scope__inherited:__ + 153
    49  Closer                              0x00c3b69d _ZL20dispatch_rimp_callerPFP11objc_objectS0_P13objc_selectorzEmS1_iPKm + 46445
    50  Closer                              0x00c220ba rb_vm_dispatch + 6554
    51  Closer                              0x00b2cc3f rb_class_inherited + 271
    52  Closer                              0x00c3ef2c rb_vm_define_class + 988
    53  Closer                              0x009ac485 rb_scope1 + 101
    54  Closer                              0x009b4a9d MREP_B4B81E55B15B422BAC3B7131D3B2EE63 + 9389
    55  Closer                              0x00083341 RubyMotionInit + 3761
    56  Closer                              0x0008369d main + 109
    57  libdyld.dylib                       0x05922701 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NameError

I tried manually running gem install motion-yaml but it results in the same error.

Models inside of a module

I usually keep all of my models inside of a "Model" module, and am trying to link up a schema to use on of my models. Is there anyway to do this using cdq?

I've tried the following:

class Model::Connection < CDQManagedObject
schema "0001 initial" do
    entity "Model::Connection" do
      ...
    end
  end
end

Question: CDQ and RestKit

I'm doing some work with CDQ and RestKit and I'm trying to come up with a way to quickly and easily build all of the appropriate request and response mappers that RestKit needs. I'm working on a gem with the working title of Restikle that I hope can make this job a bit easier, especially for people who want to connect a RubyMotion app to a RESTful Rails back-end, using CoreData and CDQ on the device as a local model repository.

My goal is to be able to provide my Restikle::Instrumentor class the output of rake routes from the Rails project, and then either db/schema.rb (from the Rails project) or resources/<cdq_schema.rb> (from the RubyMotion+CDQ project) and then have Instrumentor automatically configure RestKit with all of the correct request and response mappers.

This would avoid the vast amount of boilerplate that RestKit seems to need to do even the simplest thing.

I've made some good progress, but the bit that I am looking for some clues on is how to inflect the attributes for a particular CDQ model entity so that I can build the appropriate RestKit RKEntityMappings for each attribute of the entity. I want Restikle to be able to create these mappings automatically from the data that I have available, after parsing the routes and schema files.

Given a CDQ entity, is there a way to get the attributes for that entity? And what about relationships between CDQ entities (like one-to-many, etc).

I have had a look around and I can't find anything that makes the RestKit configuration any easier. It just seems like everyone who uses RestKit is ok with all of the boilerplate. Apologies in advance if I have missed something obvious that makes this kind of thing easier, but I just can't find it.

The REST back-end that I am talking is a very rich e-commerce platform with many resources and many more routes. So my motivation here is to avoid what is quite literally, thousands of lines of boilerplate code to configure RestKit.

I'll be more than happy to publish the Restikle gem if I can get this working, because I just can't be the only person labouring with this kind of Ruby-Motion / RestKit / RESTful Rails challenge.

Any help much appreciated.

Is it possible to use Ruby module names for CDQ entity names?

I'm curious, is it possible to use module names for entity names?

For example, with a class like:

module MyModule
  class MyEntity < CDQManagedObject
  ...
  end
end 

And then define an entity like:

schema "0001 initial" do
  entity "MyModule::MyEntity" do
    string     :field,  default:  ''
    ...
  end

The above isn't working for me, and so I'm wondering if I am doing something wrong, or if this is possible?

Thanks,
M@

WAL files hanging around and forever growing

I know there is a cdq.setup, but I don't see a corresponding cdq.shutdown. I am wondering if this is why my core data WAL files are growing, seemingly without bound.

Or, am I doing something else badly? :)

Best practice when creating or deleteing thousands of records

I'm trying to create around five thousand records simultaneously when syncing with a web service. I'm having a hard time with read/write performance and was wondering if there are some tips for accomplishing this task.

Here's some things I've found:

  • Performance suffers most when deleting existing records.
  • Using built-in associations slows read/write

Some ideas:

  • Chunk incoming data and create records in multiple background tasks
  • Implement a deleted_at column, a scope to find current records, and a background task to wipe 'deleted' items

How have other users of CDQ (or CoreData in general) found success with large datasets?

Thank you in advance!

-Spencer

question regarding schema and object types

I have a general question about creating a schema that has arrays and hashes in the model. If I understand some of this correctly I would need a has_many relationship for tags because this entity can have many tags. However, for my metadata how do I specify this as it will be a hash of key/value information. The remote Api will return JSON code with arrays and hashes so its unclear to me how to consume the data when the schema only supports primitive types.

Example:

entity "Item" do
    string :name, optional: false
    string :title, optional: false
    string :uuid, optional: false
    string :type, optional: false
    string :tags, optional: false (How do I make this an array)
    string :metadata optional: false (How do I make this a map/hash)

  end

build error with x86_64 and rubymotion 3

using rubymotion 3 (with or without specifying app.archs["iphoneos"] = ["armv7"] or app.archs["iphonesimulator"] = ["armv7"] in the rakefile) i get this error:


Loading schemas/0001_initial.rb
   Writing resources/T-.xcdatamodeld/0001 initial.xcdatamodel/contents
     Build ./build/iPhoneSimulator-8.1-Development
     Build /Users/foo/.rvm/gems/ruby-2.1.2/gems/motion-yaml-1.3/lib/YAMLKit
     Build /Users/foo/.rvm/gems/ruby-2.1.2/gems/cdq-0.1.11/lib/../vendor/cdq/ext
      Link ./build/iPhoneSimulator-8.1-Development/myapp.app/myapp
ld: warning: ignoring file .rvm/gems/ruby-2.1.2/gems/cdq-0.1.11/vendor/cdq/ext/build-iPhoneSimulator/libext.a, file was built for archive which is not the architecture being linked (x86_64): .rvm/gems/ruby-2.1.2/gems/cdq-0.1.11/vendor/cdq/ext/build-iPhoneSimulator/libext.a
ld: warning: ignoring file ./build/iPhoneSimulator-8.1-Development/objs/main.o, file was built for i386 which is not the architecture being linked (x86_64): ./build/iPhoneSimulator-8.1-Development/objs/main.o
ld: warning: ignoring file ./build/iPhoneSimulator-8.1-Development/objs/Users/foo/myapp/app/app_delegate.rb.o, missing required architecture x86_64 in file ./build/iPhoneSimulator-8.1-Development/objs/Users/foo/bar/my-app/app/app_delegate.rb.o (1 slices)
ld: warning: ignoring file ./build/iPhoneSimulator-8.1-Development/objs/Users/foo/myapp/app/event_view_controller.rb.o, missing required architecture x86_64 in file ./build/iPhoneSimulator-8.1-Development/objs/Users/foo/bar/my-app/app/event_view_controller.rb.o (1 slices)
ld: warning: ignoring file ./build/iPhoneSimulator-8.1-Development/objs/Users/foo/myapp/app/extensions.rb.o, missing required architecture x86_64 in file ./build/iPhoneSimulator-8.1-Development/objs/Users/foo/bar/my-app/app/extensions.rb.o (1 slices)
ld: warning: ignoring file ./build/iPhoneSimulator-8.1-Development/objs/Users/foo/myapp/app/helper.rb.o, missing required architecture x86_64 in file ./build/iPhoneSimulator-8.1-Development/objs/Users/foo/bar/my-app/app/helper.rb.o (1 slices)
Undefined symbols for architecture x86_64:
  "_MREP_1C098F9D1EC347888F5E832CF4269BE3", referenced from:
      _RubyMotionInit in init.o
  "_MREP_48D9E60809A74B23A8FA0D02C6BDED7D", referenced from:
      _RubyMotionInit in init.o
  "_MREP_E3B7D585F9D14896A958EF29B19A8221", referenced from:
      _RubyMotionInit in init.o
  "_MREP_FF476E80BF6048538D4A2FEFA5D98C06", referenced from:
      _RubyMotionInit in init.o
  "_main", referenced from:
     implicit entry/start for main executable
     (maybe you meant: _rb_vm_main_thread)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
rake aborted!

cdq kills permissions

I did the following:

  1. added the following to the rake file:
    gem 'cdq'
  2. ran bundle install
  3. ran cdq init

Now if i run rake i get :
Errno::EACCES: Permission denied - build-iPhone Simulator

I can get it to work by doing sudo rake but this should not be like this...

Error when running rake spec

Hello.

I just write a very simple app with cdq and get the following error when i run rake spec:

Category
  - should be a Category entity [ERROR: NameError - undefined local variable or method `cdq' for #<Bacon::Context:0x8fee620 ...>]

NoMethodError: undefined method `include' for #<Bacon::Context:0x8fee620 ...>
   spec.rb:307:in `block in run_before_filters': Category - should be a Category entity
   spec.rb:438:in `execute_block'
   spec.rb:307:in `run_before_filters'
   spec.rb:327:in `run'

NameError: undefined local variable or method `cdq' for #<Bacon::Context:0x8fee620 ...>
   spec.rb:321:in `block in run_after_filters': Category - should be a Category entity
   spec.rb:438:in `execute_block'
   spec.rb:321:in `run_after_filters'
   spec.rb:420:in `finish_spec'
   spec.rb:316:in `run_spec_block'
   spec.rb:329:in `run'

2 specifications (1 requirements), 0 failures, 2 errors

Any ideas?

cannot use 2 models or more in OSX

using cdq in an OSX app, it crashes when using 2 or models (works fine with 1)

sample app at https://github.com/iurimatias/cdqbug

Run ./build/MacOSX-10.9-Development/CdqBug.app/Contents/MacOS/CdqBug
2014-06-26 23:25:43.956 CdqBug[52575:303] cdq.rb:58:in cdq:': Cannot find an entity named Author (RuntimeError) from managed_object.rb:32:ininherited:'
2014-06-26 23:25:43.957 CdqBug[52575:303] *** Terminating app due to uncaught exception 'RuntimeError', reason: 'cdq.rb:58:in `cdq:': Cannot find an entity named Author (RuntimeError)

DECIMAL data type

I have a decimal type defined like:

  entity "Item" do
    decimal :cost
    decimal :quantity
  end

The hope is that this would allow me to store money and partial product quantities accurately, but when they go into or out of the database, they come back very badly:

(main)> i = Item.first
=> <Item: 0x12a22100> (entity: Item; id: 0xd3d60f0 <x-coredata://C33D4E86-0EEF-4BB0-8638-1D4B8F104610/Item/p1> ; data: {
    cost = "5.1";
    quantity = "5.1";
})
(main)> i.cost
=> 5.09999847412109
(main)> i.cost = 5.1
=> 5.09999847412109

I suspect this is a problem with how floats are represented inside RubyMotion:

(main)> 5.1
=> 5.09999847412109
(main)> 5.10
=> 5.09999847412109
(main)> 5.11
=> 5.10999870300293
(main)> 5.111
=> 5.11100006103516

I'm not yet sure how to work around this issue...

Using cdq within a framework

Is this possible? I have just tried adding cdq to the Gemfile on a framework and the result is a crash upon load:

  Simulate ./build/iPhoneSimulator-8.1-Development/DataTest.app
(main)> 2014-12-18 19:15:10.949 DataTest[22812:310052] uninitialized constant YAMLKit (NameError)
2014-12-18 19:15:10.953 DataTest[22812:310052] *** Terminating app due to uncaught exception 'NameError', reason: 'uninitialized constant YAMLKit (NameError)

Threading context example?

IN the readme we have this

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.contexts.new(NSConfinementConcurrencyType) do
    # Your work here
  end

could someone clarify what "# Your work here" would look like

I have an app where I setup cdq in the main app delegate, then in a background thread i want to save some data, and update the main context's thread - which sounds like this exactly what I want to do.

If i use the NSConfinementConcurrencyType I get errors like

014-09-18 15:19:35.269 Hello 200[95245:e03] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Can only use -performBlockAndWait: on an NSManagedObjectContext that was created with a queue.'

if I use another thread type, it works and saves in the context of the thread, but back on the main context it is not carrying over.

Any sort of sample code instead of 'your work here' might help me at least investigate what is going on here.

Crash when building for iOS 7 with Xcode 6.01 (iOS8 SDK)

I've managed to replicate this on two different machines and am at a loss as to why this is occurring. This might be a Core Data specific problem because things work fine when building for iOS7 w/Xcode 5 ... and when building for iOS8 w/Xcode 6. I thought I would post this here in the off chance this is something someone else has seen, OR someone sees in the near future.

Details for the crash are as follows:

  1. Install Xcode 6.01
  2. Install the iOS 7 emulator in Xcode 6.01. (Xcode -> Preferences -> Downloads)
  3. Pull down this code: git clone [email protected]:jayroh/sample-cdq.git && cd sample-cdq && bundle
  4. Run the app using a 7.x emulator: rake target=7.1
  5. The app will start up in the iPhone 5S / iOS 7.1 emulator and give you the repl.
  6. To trigger the crash, run the following in the repl:
p = Post.new and p.active = 1

More can be found at this sample app, including the crash-log

Cannot get gem to work

Hello,

First off, thanks for creating such a cool gem!

I try to use it but I get the below error:

reason: 'uninitialized constant CDQManagedOjbect (NameError)

  1. I have added CDQ to gem file and ran bundle.
  2. I have ran cdq init.
  3. I have added include CDQ and cdq.setup to Appdelegate.

I am using the Promotion gem. Could that be a problem?

Is it possible to use CDQ to perform a query that uses a block?

I have a situation where I have a list of Stores and each Store has a StoreLocation which is just a lat/lon pair. For simplicity, just consider that each Store has a lat and a lon element.

I want to filter the set of Stores to just those that a "nearby", where nearby means that the result of the Great Circle calculation is less than some value. Note: I know how to do all of the geo stuff, so this bit is irrelevant to my CDQ question, but it's useful background.

I already have a table view controller that is displaying all stores using CDQ and a fetched results controller, but now I want to add in the ability to filter the list down based on proximity.

Here's the method that the TVC uses to get a fetched results controller:

  def fetch_controller
    @entities ||= Store.all.sort_by(:name)
    @fetch_controller ||= NSFetchedResultsController.alloc.initWithFetchRequest(
      @entities.fetch_request,
      managedObjectContext: @entities.context,
      sectionNameKeyPath: nil,
      cacheName: nil
    )
  end

What I want to know is, how can use a CDQ query to make the @entities ||= ... line filter based on application of the Great Circle algorithm? This is what I tried:

    def fetch_controller
      return super if !@nearby
      @entities ||= begin
        cur_loc = App.delegate.session.shopper_location.current_location
        Store.all_nearby_to(cur_loc.coordinate.latitude, cur_loc.coordinate.longitude)
      end
      @fetch_controller ||= NSFetchedResultsController.alloc.initWithFetchRequest(
        @entities.fetch_request,
        managedObjectContext: @entities.context,
        sectionNameKeyPath: nil,
        cacheName: nil
      )
    end

And here's the impl of the all_nearby_to method in the Store CDO:

  def self.all_nearby_to(lat, lon)
    my_loc  = MyApp::GreatCircleLocationImpl.new(lat: lat, lon: lon)
    Store.all.sort_by(:store_location).find_all do |store|
      if store.store_location && store.store_location.lat && store.store_location.lon
        other_loc = MyApp::GreatCircleLocationImpl.new(lat: store.store_location.lat, lon: store.store_location.lat)
        my_loc.nearby?(other_loc)
      end
    end
  end

This filters the CDOs perfectly. However, the problem is that it returns an array, and not whatever it is that CDO returns from Store.all or any version of a CDQ query applied to a CDO entity.

What I think I want to be able to do is the inject a block into a query so that I can apply my algorithm over the set of entities returned from Store.all. But I could have this totally wrong.

Any ideas would be much appreciated. Thanks,
M@

Query Best Practices

Thanks for an awesome gem!

In the README you say:

So if you find yourself running queries that only return a single object, consider rearchitecting

and I am coming from a Rails / server-side background, so what IS the best approach?

Given a basic model like User{id,name,email} and you get back a result set from the server and just create CDQ objects with it. Then later you want to retrieve a user by a known ID, so my first thought is just:

user = User.where(:id => 99).first

But this seemingly flies against the instructions above.

Thus, what is the best approach and what is the recommended re-architecting?

Thanks again.

Crazy behaviour: CDQ entity will not accept assignment for (only) 2 attributes

I have some crazy behaviour that I simply cannot understand. I have a CDQ entity called "Fund" which is defined like this:

  entity "Fund" do
    datetime   :created_at
    datetime   :updated_at
    datetime   :deleted_at
    integer64  :fund_id,               default: -1
    string     :nickname,              default: 'FUND'
    string     :source,                default: 'visa'
    string     :name_on_card,          default: '######## ########'
    string     :ppc_hash,              default: ''
    string     :address_line1,         default: ''
    string     :address_line2,         default: ''
    string     :address_city,          default: ''
    string     :address_postcode,      default: ''
    string     :address_state,         default: ''
    string     :address_country,       default: ''
    string     :number,                default: '####-####-####-####'
    string     :security_code,         default: '###'
    string     :expiry_month,          default: '##'
    string     :expiry_year,           default: '####'
    integer64  :shopper_ref,           default: -1
    belongs_to :shopper,               optional: true
  end

I thought that everything was going swimmingly well, until I came across a weird bug where I couldn't assign to an element. I jumped into the REPL and did the following:

(main)> f = Fund.new
=> <Fund: 0xf3d9ce0> (entity: Fund; id: 0x182729a0 <x-coredata:///Fund/t6AC0587E-C09D-4E12-977D-3F34FAD8AD218> ; data: {
    "address_city" = "";
    "address_country" = "";
    "address_line1" = "";
    "address_line2" = "";
    "address_postcode" = "";
    "address_state" = "";
    "created_at" = nil;
    "deleted_at" = nil;
    "expiry_month" = "##";
    "expiry_year" = "####";
    "fund_id" = "-1";
    "name_on_card" = "######## ########";
    nickname = FUND;
    number = "####-####-####-####";
    "ppc_hash" = "";
    "security_code" = "###";
    shopper = nil;
    "shopper_ref" = "-1";
    source = visa;
    "updated_at" = nil;
})
(main)> f.entity.attributesByName.keys.each {|e| puts "#{e}: #{f.respond_to?(e.to_sym)}" }
address_city: true
address_country: true
address_line1: true
address_line2: true
address_postcode: true
address_state: true
created_at: true
deleted_at: true
expiry_month: true
expiry_year: true
fund_id: true
name_on_card: true
nickname: true
number: true
ppc_hash: true
security_code: true
shopper_ref: true
source: true
updated_at: true
=> ["address_city", "address_country", "address_line1", "address_line2", "address_postcode", "address_state", "created_at", "deleted_at", "expiry_month", "expiry_year", "fund_id", "name_on_card", "nickname", "number", "ppc_hash", "security_code", "shopper_ref", "source", "updated_at"]
(main)> f.entity.attributesByName.keys.each {|e| assigner="#{e}=".to_sym; puts "#{e}: #{f.respond_to?(e.to_sym)} #{f.respond_to?(assigner)}" }
address_city: true true
address_country: true true
address_line1: true false
address_line2: true false
address_postcode: true true
address_state: true true
created_at: true true
deleted_at: true true
expiry_month: true true
expiry_year: true true
fund_id: true true
name_on_card: true true
nickname: true true
number: true true
ppc_hash: true true
security_code: true true
shopper_ref: true true
source: true true
updated_at: true true
=> ["address_city", "address_country", "address_line1", "address_line2", "address_postcode", "address_state", "created_at", "deleted_at", "expiry_month", "expiry_year", "fund_id", "name_on_card", "nickname", "number", "ppc_hash", "security_code", "shopper_ref", "source", "updated_at"]

Why does the f instance (of Fund) NOT think that f.respond_to?("address_line1=".to_sym) is ok? And same for f.respond_to?("address_line2=".to_sym) but all other attributes are ok?

What the hell is wrong with address_line and address_line2 as attributes? What crazy thing am I doing wrong here?

Context not being stored on thread

Hello,

I'm not sure if this is expected, but I just ran into this issue since the ios8 upgrade.

Anyways, I'm creating a queue, setting up a context stack on the queue and am expecting to be able to use that queue again without having to re-configure the context. In ios8 this is failing and keeps saying the context is not configured and to run cdq.setup

class Connection < CDQManagedObject; end

class Test

  include CDQ

  class << self

    include CDQ

    def get
      @q.async do
        # NOTE fails here
        puts cdq.contexts.current
        puts Connection.count
      end
    end

    def setup
      parent = cdq.contexts.current
      @q = Dispatch::Queue.new(:test.to_s)

      @q.sync do
        cdq.contexts.push(parent)
        @context = cdq.contexts.new(NSPrivateQueueConcurrencyType)
        Connection.create(first_name: :jon)
      end
    end
  end

end

And the error:

2014-09-21 17:14:19.253 cdq[25470:337244] *** Terminating app due to uncaught exception 'RuntimeError', reason: 'targeted_query.rb:34:in `count': No context has been set.  Probably need to run cdq.setup (RuntimeError)
        from managed_object.rb:62:in `method_missing:'
        from connection.rb:15:in `block in get'
...

Am I correct in thinking that once I call setup, the queue should be configured? I shouldn't have to reconfigure when I call get correct?

cdq context tutorial

Is there a tutorial on how to use contexts with cdq? I really don't need the weight of rest kit, but I do need background syncing. I am likely to ditch RestKit for a multi device sync system, and would likely use cdq there. But, I am not sure how to actually do things like have a background thread use one cdq context that, when saved, rolls up into the main context that the UI sees. Or, a way to directly modify objects in a detail view, and then either cancel or save that context and have it roll up. That sort of thing SHOULD simplify code.

I have found some for core data, but mapping them into cdq's world view is currently beyond me. Give me a OS kernel, and I'm all set -- but core data is tricky! :)

Ability to Rollback Changes

Right now there's no direct way to roll back a model to its persisted state, as far as I can tell (short of not calling cdq.save, but it looks like the changed data is still cached until relaunching the app). The workaround I'm using now is calling cdq.contexts.current.rollback, though that rolls back the entire context instead of just one model.

Not sure if this is possible to implement, or if I'm missing something obvious?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.