cdq 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +11 -0
  3. data/Gemfile +7 -0
  4. data/Gemfile.lock +32 -0
  5. data/README.md +173 -0
  6. data/Rakefile +24 -0
  7. data/app/app_delegate.rb +13 -0
  8. data/app/test_models.rb +15 -0
  9. data/cdq.gemspec +18 -0
  10. data/lib/cdq.rb +12 -0
  11. data/lib/cdq/version.rb +4 -0
  12. data/motion/cdq.rb +58 -0
  13. data/motion/cdq/collection_proxy.rb +29 -0
  14. data/motion/cdq/config.rb +67 -0
  15. data/motion/cdq/context.rb +190 -0
  16. data/motion/cdq/model.rb +32 -0
  17. data/motion/cdq/object.rb +83 -0
  18. data/motion/cdq/object_proxy.rb +30 -0
  19. data/motion/cdq/partial_predicate.rb +53 -0
  20. data/motion/cdq/query.rb +128 -0
  21. data/motion/cdq/relationship_query.rb +122 -0
  22. data/motion/cdq/store.rb +52 -0
  23. data/motion/cdq/targeted_query.rb +170 -0
  24. data/motion/managed_object.rb +98 -0
  25. data/resources/CDQ.xcdatamodeld/.xccurrentversion +8 -0
  26. data/resources/CDQ.xcdatamodeld/0.0.1.xcdatamodel/contents +34 -0
  27. data/resources/Default-568h@2x.png +0 -0
  28. data/resources/KEEPME +0 -0
  29. data/schemas/001_baseline.rb +44 -0
  30. data/spec/cdq/collection_proxy_spec.rb +51 -0
  31. data/spec/cdq/config_spec.rb +74 -0
  32. data/spec/cdq/context_spec.rb +92 -0
  33. data/spec/cdq/managed_object_spec.rb +81 -0
  34. data/spec/cdq/model_spec.rb +14 -0
  35. data/spec/cdq/module_spec.rb +44 -0
  36. data/spec/cdq/object_proxy_spec.rb +37 -0
  37. data/spec/cdq/object_spec.rb +58 -0
  38. data/spec/cdq/partial_predicate_spec.rb +52 -0
  39. data/spec/cdq/query_spec.rb +127 -0
  40. data/spec/cdq/relationship_query_spec.rb +75 -0
  41. data/spec/cdq/store_spec.rb +39 -0
  42. data/spec/cdq/targeted_query_spec.rb +120 -0
  43. data/spec/helpers/thread_helper.rb +16 -0
  44. data/spec/integration_spec.rb +38 -0
  45. data/vendor/cdq/ext/CoreDataQueryManagedObjectBase.h +8 -0
  46. data/vendor/cdq/ext/CoreDataQueryManagedObjectBase.m +22 -0
  47. metadata +138 -0
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MjBhMWJiZjc1MTc0OGQyMzM4NTEwN2ZkMGIyOWQ3MTJmODdiNWZhYg==
5
+ data.tar.gz: !binary |-
6
+ NmE3MTQ2MjIyMmYxYTJiYzg4OTJmMDYxMjk5ZjE4YWU2YjgzMzY1Nw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ YTBlZTY0NDE4NzIxMDY2ODcxMmUzNGMxM2M3OWFhYTExM2RjYjc1ZWI2NjRh
10
+ MTFiZjUyNzIwY2VlMDRmOTk3MTFmMGE2MTg0YTQ3YTljMTJlZGVkNWJkYjJm
11
+ NzZlZjYzYWRiODQ2OGVhNzNjNGUwOTliMDhkNzZiZDQ3YmU1YTE=
12
+ data.tar.gz: !binary |-
13
+ YmM1Njk2Nzg3ODBkODYxMjAxOWNlNmVkZWU3ZTg0YzNhMDBjOTQ5OTljNjcx
14
+ OWJiODM1MGYxOGNlODg1NGM4MzY3MzEzZjUyM2I5OTE5OGEwZGE0YmNlMjkx
15
+ NzkxNjlkNzIzYzQyZGYxODQxZmIyNjg2NGQwOTk1ZWJiODczYzI=
@@ -0,0 +1,11 @@
1
+ .repl_history
2
+ .*.sw?
3
+ build
4
+ vendor/cdq/ext/build-*
5
+ vendor/cdq/ext/ext.bridgesupport
6
+ resources/*.nib
7
+ resources/*.momd
8
+ resources/*.storyboardc
9
+ examples/**/*.nib
10
+ tags
11
+ .rbenv-version
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem 'rake'
6
+ gem 'motion-stump', :group => :spec
7
+ gem 'cdq', :path => '.'
@@ -0,0 +1,32 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ cdq (0.1.1)
5
+ motion-yaml
6
+ ruby-xcdm (~> 0.0, >= 0.0.5)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (3.2.16)
12
+ i18n (~> 0.6, >= 0.6.4)
13
+ multi_json (~> 1.0)
14
+ builder (3.2.2)
15
+ i18n (0.6.9)
16
+ motion-stump (0.3.0)
17
+ motion-yaml (1.2)
18
+ multi_json (1.8.2)
19
+ plist (3.1.0)
20
+ rake (10.1.0)
21
+ ruby-xcdm (0.0.5)
22
+ activesupport (~> 3.2)
23
+ builder (~> 3.2)
24
+ plist (~> 3.1)
25
+
26
+ PLATFORMS
27
+ ruby
28
+
29
+ DEPENDENCIES
30
+ cdq!
31
+ motion-stump
32
+ rake
@@ -0,0 +1,173 @@
1
+
2
+ # Streamlined Core Data for RubyMotion
3
+
4
+ Core Data Query (CDQ) is a library to help you manage your Core Data stack
5
+ while using RubyMotion. It uses a data model file, which you can generate in
6
+ XCode, or you can use [ruby-xcdm](https://github.com/infinitered/ruby-xcdm).
7
+ CDQ aims to streamline the process of getting you up and running Core Data, while
8
+ avoiding too much abstraction or method pollution on top of the SDK.
9
+
10
+ CDQ began its life as a fork of
11
+ [MotionData](https://github.com/alloy/MotionData), but it became obvious I
12
+ wanted to take things in a different direction, so I cut loose and ended up
13
+ rewriting almost everything. If you pay attention, you can still find the
14
+ genetic traces, so thanks to @alloy for sharing his work and letting me learn
15
+ so much.
16
+
17
+ ### Why use a static Data Model?
18
+
19
+ By using a real data model file that gets compiled and included in your bundle,
20
+ you can take advantage of automatic migration, which simplifies managing your
21
+ schema as it grows, if you can follow a few simple rules.
22
+
23
+ ## Installing
24
+
25
+ Using Bundler:
26
+
27
+ ```ruby
28
+ gem 'cdq'
29
+ ```
30
+
31
+ If you want to see bleeding-edge changes, point Bundler at the git repo:
32
+
33
+ ```ruby
34
+ gem 'cdq', git: 'git://github.com/infinitered/cdq.git'
35
+ ```
36
+
37
+ ## Setting up your stack
38
+
39
+ You will need a data model file. If you've created one in XCode, move or copy
40
+ it to your resources file and make sure it's named the same as your RubyMotion
41
+ project. If you're using `ruby-xcdm` (which I highly recommend) then it will
42
+ create the datamodel file automatically and put it in the right place.
43
+
44
+ Now include the setup code in your `app_delegate.rb` file:
45
+
46
+ ```ruby
47
+ class AppDelegate
48
+ include CDQ
49
+
50
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
51
+ cdq.setup
52
+ true
53
+ end
54
+ end
55
+
56
+ class TopLevel
57
+ include CDQ
58
+ end
59
+ ```
60
+
61
+ That's it! You can create specific implementation classes for your entities if
62
+ you want, but it's not required. You can start running queries on the console or
63
+ in your code right away.
64
+
65
+ ## Creating new objects
66
+
67
+ CDQ maintains a stack of NSManagedObjectContexts for you, and any create
68
+ operations will insert new, unsaved objects into the context currently on top
69
+ of the stack. `cdq.save` will save the whole stack, including correctly using
70
+ private threads if appropriate.
71
+
72
+ ```ruby
73
+ cdq('Author').create(name: "Le Guin", publish_count: 150, first_published: 1970)
74
+ cdq('Author').create(name: "Shakespeare", publish_count: 400, first_published: 1550)
75
+ cdq('Author').create(name: "Blake", publish_count: 100, first_published: 1778)
76
+ cdq.save
77
+
78
+ ```
79
+
80
+ ## Queries
81
+
82
+ A quick aside about queries in Core Data. You should avoid them whenever
83
+ possible in your production code. Core Data is designed to work efficiently
84
+ when you hang on to references to specific objects and use them as you would
85
+ any in-memory object, letting Core Data handle your memory usage for you. If
86
+ you're coming from a server-side rails background, this can be pretty hard to
87
+ get used to, but this is a very different environment. So if you find yourself
88
+ running queries that only return a single object, consider rearchitecting.
89
+ That said, queries are sometimes the only solution, and it's very handy to be
90
+ able to use them easily when debugging from the console, or in unit tests.
91
+
92
+ With that out of the way, here are some samples:
93
+
94
+ ```ruby
95
+ # Simple Queries
96
+ cdq('Author').where(:name).eq('Shakespeare')
97
+ cdq('Author').where(:publish_count).gt(10)
98
+
99
+ # sort, limit, and offset
100
+ cdq('Author').sort_by(:created_at).limit(1).offset(10)
101
+
102
+ # Compound queries
103
+ cdq('Author').where(:name).eq('Blake').and(:first_published).le(Time.local(1700))
104
+
105
+ # Multiple comparisons against the same attribute
106
+ cdq('Author').where(:created_at).ge(yesterday).and.lt(today)
107
+ ```
108
+
109
+ ## Named Scopes
110
+
111
+ You can save up partially-constructed queries for later use using named scopes, even
112
+ combining them seamlessly with other queries or other named scopes:
113
+
114
+ ```ruby
115
+ cdq('Author').scope :a_authors, cdq(:name).begins_with('A')
116
+ cdq('Author').scope :prolific, cdq(:publish_count).gt(99)
117
+
118
+ cdq('Author').prolific.a_authors.limit(5)
119
+ ```
120
+
121
+ > NOTE: strings and symbols are NOT interchangeable. `cdq('Entity')` gives you a
122
+ query generator for an entity, but `cdq(:attribute)` starts a predicate for an
123
+ attribute.
124
+
125
+ ## Dedicated Models
126
+
127
+ If you're using CDQ in a brand new project, you'll probably want to use
128
+ dedicated model classes for your entities. It will enable the usual
129
+ class-level customization of your model objects, and also a somewhat more
130
+ familiar-looking and natural syntax for queries and scopes:
131
+
132
+ ```ruby
133
+ class Author < CDQManagedOjbect
134
+ scope :a_authors, cdq(:name).begins_with('A')
135
+ scope :prolific, cdq(:publish_count).gt(99)
136
+ end
137
+ ```
138
+
139
+ Now you can change the queries above to:
140
+
141
+ ```ruby
142
+ Author.where(:name).eq('Shakespeare')
143
+ Author.where(:publish_count).gt(10)
144
+ Author.where(:name).eq('Blake').and(:first_published).le(Time.local(1700))
145
+ Author.where(:created_at).ge(yesterday).and.lt(today)
146
+ Author.prolific.a_authors.limit(5)
147
+ ```
148
+
149
+ Anything you can do with `cdq('Author')` you can now do with just `Author`. If you have a
150
+ pre-existing implementation class that you can't turn into a CDQManagedObject, you can also
151
+ just wrap the class: `cdq(Author)`.
152
+
153
+ ## Using CDQ with a pre-existing model
154
+
155
+ If you have an existing app that already manages its own data model, you can
156
+ use that, too, and override CDQ's stack at any layer:
157
+
158
+ ```ruby
159
+ cdq.setup(context: App.delegate.mainContext) # don't set up model or store coordinator
160
+ cdq.setup(store: App.delegate.persistentStoreCoordinator) # Don't set up model
161
+ cdq.setup(model: App.delegate.managedObjectModel) # Don't load model
162
+ ```
163
+
164
+ You cannot use CDQManagedObject as a base class when overriding this way,
165
+ you'll need to use <tt>cdq('Entity')</tt>. If you have an existing model and
166
+ want to use it with CDQManagedObject without changing its name, You'll need to
167
+ use a <tt>cdq.yml</tt> config file. See [CDQConfig](motion/cdq/config.rb).
168
+
169
+ ## Things that are currently missing
170
+
171
+ * There is no facility for custom migrations yet
172
+ * There are no explicit validations (but you can define them on your data model)
173
+
@@ -0,0 +1,24 @@
1
+ $:.unshift("/Library/RubyMotion/lib")
2
+ require 'motion/project/template/ios'
3
+
4
+ require 'bundler'
5
+ require 'bundler/gem_tasks'
6
+
7
+ Motion::Project::App.setup do |app|
8
+ # Use `rake config' to see complete project settings.
9
+ app.name = 'CDQ'
10
+ app.vendor_project('vendor/cdq/ext', :static)
11
+ end
12
+
13
+ if ARGV.join(' ') =~ /spec/
14
+ Bundler.require :default, :spec
15
+ else
16
+ Bundler.require
17
+ end
18
+
19
+ require 'cdq'
20
+ require 'motion-stump'
21
+ require 'ruby-xcdm'
22
+ require 'motion-yaml'
23
+
24
+ task :"build:simulator" => :"schema:build"
@@ -0,0 +1,13 @@
1
+ class AppDelegate
2
+ include CDQ
3
+
4
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
5
+ cdq.setup
6
+ true
7
+ end
8
+ end
9
+
10
+ class TopLevel
11
+ include CDQ
12
+ end
13
+
@@ -0,0 +1,15 @@
1
+ class Author < CDQManagedObject
2
+ end
3
+
4
+ class Article < CDQManagedObject
5
+ scope :all_published, where(:published).eq(true)
6
+ scope :with_title, where(:title).ne(nil).sort_by(:title, :descending)
7
+ scope :published_since { |date| where(:publishedAt).ge(date) }
8
+ end
9
+
10
+ class Citation < CDQManagedObject
11
+ end
12
+
13
+ class Writer < CDQManagedObject
14
+ end
15
+
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/cdq/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["infinitered", "kemiller"]
6
+ gem.email = ["ken@infinitered.com"]
7
+ gem.description = "Core Data Query for RubyMotion"
8
+ gem.summary = "Core Data Query for RubyMotion"
9
+ gem.homepage = "http://github.com/infinitered/cdq"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
13
+ gem.name = "cdq"
14
+ gem.require_paths = ["lib"]
15
+ gem.add_runtime_dependency 'ruby-xcdm', '~> 0.0', '>= 0.0.5'
16
+ gem.add_runtime_dependency 'motion-yaml'
17
+ gem.version = CDQ::VERSION
18
+ end
@@ -0,0 +1,12 @@
1
+
2
+ unless defined?(Motion::Project::App)
3
+ raise "This must be required from within a RubyMotion Rakefile"
4
+ end
5
+
6
+ Motion::Project::App.setup do |app|
7
+ parent = File.join(File.dirname(__FILE__), '..')
8
+ app.files.unshift(Dir.glob(File.join(parent, "motion/cdq/**/*.rb")))
9
+ app.files.unshift(Dir.glob(File.join(parent, "motion/*.rb")))
10
+ app.frameworks += %w{ CoreData }
11
+ app.vendor_project(File.join(parent, 'vendor/cdq/ext'), :static)
12
+ end
@@ -0,0 +1,4 @@
1
+
2
+ module CDQ
3
+ VERSION = '0.1.1'
4
+ end
@@ -0,0 +1,58 @@
1
+
2
+ module CDQ
3
+
4
+ class CDQObject; end
5
+ class CDQQuery < CDQObject; end
6
+ class CDQPartialPredicate < CDQObject; end
7
+ class CDQTargetedQuery < CDQQuery; end
8
+
9
+ extend self
10
+
11
+ def cdq(obj = nil)
12
+ obj ||= self
13
+
14
+ @@base_object ||= CDQObject.new
15
+
16
+ case obj
17
+ when Class
18
+ if obj.isSubclassOfClass(NSManagedObject)
19
+ entity_description = @@base_object.models.current.entitiesByName[obj.name]
20
+ if entity_description.nil?
21
+ raise "Cannot find an entity named #{obj.name}"
22
+ end
23
+ CDQTargetedQuery.new(entity_description, obj)
24
+ else
25
+ @@base_object
26
+ end
27
+ when String
28
+ entity_description = @@base_object.models.current.entitiesByName[obj]
29
+ target_class = NSClassFromString(entity_description.managedObjectClassName)
30
+ if entity_description.nil?
31
+ raise "Cannot find an entity named #{obj}"
32
+ end
33
+ CDQTargetedQuery.new(entity_description, target_class)
34
+ when Symbol
35
+ CDQPartialPredicate.new(obj, CDQQuery.new)
36
+ when CDQObject
37
+ obj
38
+ when NSManagedObject
39
+ CDQObjectProxy.new(obj)
40
+ when Array
41
+ if obj.first.class.isSubclassOfClass(NSManagedObject)
42
+ CDQCollectionProxy.new(obj, obj.first.entity)
43
+ else
44
+ @@base_object
45
+ end
46
+ else
47
+ @@base_object
48
+ end
49
+ end
50
+
51
+ def self.pollute(klass)
52
+ end
53
+
54
+ end
55
+
56
+ class UIResponder
57
+ include CDQ
58
+ end
@@ -0,0 +1,29 @@
1
+
2
+ module CDQ
3
+ class CDQCollectionProxy < CDQTargetedQuery
4
+
5
+ def initialize(objects, entity_description)
6
+ @objects = objects
7
+ super(entity_description, constantize(entity_description.managedObjectClassName))
8
+ @predicate = self.where("%@ CONTAINS SELF", @objects).predicate
9
+ end
10
+
11
+ def count
12
+ @objects.size
13
+ end
14
+
15
+ def get
16
+ @objects
17
+ end
18
+
19
+ def array
20
+ @objects
21
+ end
22
+
23
+ def first
24
+ @objects.first
25
+ end
26
+
27
+ end
28
+ end
29
+
@@ -0,0 +1,67 @@
1
+ module CDQ
2
+
3
+
4
+ # = Configure the CDQ Stack
5
+ #
6
+ # This class wraps the YAML configuration file that will allow you to
7
+ # override the names used when setting up the database file and finding the
8
+ # model file. This file is named <tt>cdq.yml</tt> and must be found at the
9
+ # root of your resources directory. It supports the following top-level keys:
10
+ #
11
+ # [name] The root name for both database and model
12
+ # [database_name] The root name for the database file (relative to the documents directory)
13
+ # [model_name] The root name for the model file (relative to the bundle directory)
14
+ #
15
+ # Using the config file is not necessary. If you do not include it, the bundle display name
16
+ # will be used. For most people with a new app, this is what you want to do, especially if
17
+ # you are using ruby-xcdm schemas. The only case where using the config file is required
18
+ # is when you want to use CDQManagedObject-based models with a custom model or database, because
19
+ # class loading order of operations makes it impossible to configure from within your
20
+ # AppDelegate.
21
+ #
22
+ class CDQConfig
23
+
24
+ attr_reader :config_file, :database_name, :model_name, :name
25
+
26
+ def initialize(config_file)
27
+ case config_file
28
+ when String
29
+ @config_file = config_file
30
+ if File.file?(config_file)
31
+ h = File.open(config_file) { |f| YAML.load(f.read) }
32
+ else
33
+ h = {}
34
+ end
35
+ when Hash
36
+ h = config_file
37
+ else
38
+ h = {}
39
+ end
40
+
41
+ @name = h['name'] || h[:name] || NSBundle.mainBundle.objectForInfoDictionaryKey("CFBundleDisplayName")
42
+ @database_name = h['database_name'] || h[:database_name] || name
43
+ @model_name = h['model_name'] || h[:model_name] || name
44
+ end
45
+
46
+ def database_url
47
+ dir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true).last
48
+ path = File.join(dir, database_name + '.sqlite')
49
+ NSURL.fileURLWithPath(path)
50
+ end
51
+
52
+ def model_url
53
+ NSBundle.mainBundle.URLForResource(model_name, withExtension: "momd");
54
+ end
55
+
56
+ def self.default
57
+ @default ||=
58
+ begin
59
+ cf_file = NSBundle.mainBundle.pathForResource("cdq", ofType: "yml");
60
+ new(cf_file)
61
+ end
62
+ end
63
+ end
64
+
65
+ end
66
+
67
+