cdq 0.1.9 → 0.1.10
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.
- checksums.yaml +8 -8
- data/README.md +12 -0
- data/lib/cdq.rb +1 -0
- data/lib/cdq/version.rb +1 -1
- data/motion/cdq.rb +7 -3
- data/motion/cdq/config.rb +22 -10
- data/motion/cdq/context.rb +36 -3
- data/motion/cdq/object.rb +2 -2
- data/motion/cdq/store.rb +97 -17
- data/motion/managed_object.rb +17 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YWY0OGVmY2Y5Y2UxMmNjZjI0YjcyODhmNmY0OWNhYWFkZmMwYjMzMQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MTczM2QyOWI4NDczZDY1ODBlNDFjNDI0NGI1MjhjODFlNTU5Njc2Ng==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MmU3ZDJjMDZkOWE3YjczYzAxZDQ2MDM5NTBiNTc0YjY2OGQ5MDQzNWY5YmM1
|
10
|
+
MjZlMWY1MTBlYjRjNDVjZGI2NWFlYjQ0YzliYWYwYWM2NWE3ODc0Mjc5ZmQ5
|
11
|
+
OWY4MDExZmEwNzYzNGJiZGUzY2EwNmE0ZTQwZTliODQ4MWU4YjM=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YWY3YjliOGJiMDM1Y2JhZGZmMzQ2OGUxMDg3ODNjYzNkOWM0ODUxMGM4YzQ1
|
14
|
+
ZTA2N2FkNzhhODYxZTlmOGNjY2E4NzIxYjE0N2QwMjJjYmRlMzc4MWVmZWMz
|
15
|
+
NDBlZDhhYmMyZTI3ZTZiODllMGQ5MmJhZWIzNmJjYmRkYjgyYmU=
|
data/README.md
CHANGED
@@ -322,6 +322,18 @@ defining and using named scopes:
|
|
322
322
|
query generator for an entity, but `cdq(:attribute)` starts a predicate for an
|
323
323
|
attribute.
|
324
324
|
|
325
|
+
## iCloud
|
326
|
+
|
327
|
+
As of version 0.1.10, there is some experimental support for iCloud, written by
|
328
|
+
@katsuyoshi. Please try it out and let us know how it's working for you. To
|
329
|
+
enable, initialize like this:
|
330
|
+
|
331
|
+
```ruby
|
332
|
+
cdq.stores.new(iCloud: true, container: "com.your.container.id")
|
333
|
+
```
|
334
|
+
|
335
|
+
You can also set up iCloud in your cdq.yml file.
|
336
|
+
|
325
337
|
## Documentation
|
326
338
|
|
327
339
|
* [API](http://rubydoc.info/github/infinitered/cdq)
|
data/lib/cdq.rb
CHANGED
data/lib/cdq/version.rb
CHANGED
data/motion/cdq.rb
CHANGED
@@ -51,9 +51,11 @@ module CDQ
|
|
51
51
|
case obj
|
52
52
|
when Class
|
53
53
|
if obj.isSubclassOfClass(NSManagedObject)
|
54
|
+
entities = NSDictionary.dictionaryWithDictionary(
|
55
|
+
@@base_object.models.current.entitiesByName)
|
54
56
|
entity_description =
|
55
|
-
|
56
|
-
|
57
|
+
entities[obj.name] ||
|
58
|
+
entities[obj.ancestors[1].name]
|
57
59
|
if entity_description.nil?
|
58
60
|
raise "Cannot find an entity named #{obj.name}"
|
59
61
|
end
|
@@ -62,7 +64,9 @@ module CDQ
|
|
62
64
|
@@base_object
|
63
65
|
end
|
64
66
|
when String
|
65
|
-
|
67
|
+
entities = NSDictionary.dictionaryWithDictionary(
|
68
|
+
@@base_object.models.current.entitiesByName)
|
69
|
+
entity_description = entities[obj]
|
66
70
|
target_class = NSClassFromString(entity_description.managedObjectClassName)
|
67
71
|
if entity_description.nil?
|
68
72
|
raise "Cannot find an entity named #{obj}"
|
data/motion/cdq/config.rb
CHANGED
@@ -8,10 +8,12 @@ module CDQ
|
|
8
8
|
# model file. This file is named <tt>cdq.yml</tt> and must be found at the
|
9
9
|
# root of your resources directory. It supports the following top-level keys:
|
10
10
|
#
|
11
|
-
# [name]
|
12
|
-
# [database_dir]
|
13
|
-
# [database_name]
|
14
|
-
# [model_name]
|
11
|
+
# [name] The root name for both database and model
|
12
|
+
# [database_dir] The root name for the database directory (NSDocumentDirectory or NSApplicationSupportDirectory)
|
13
|
+
# [database_name] The root name for the database file (relative to the database_dir)
|
14
|
+
# [model_name] The root name for the model file (relative to the bundle directory)
|
15
|
+
# [icloud] If it's true, CDQ works with iCloud.
|
16
|
+
# [icloud_container] Set id of iCloud container if you use iCloud. If it's nil, use first container listed in the com.apple.developer.ubiquity-container-identifiers entitlement array.
|
15
17
|
#
|
16
18
|
# Using the config file is not necessary. If you do not include it, the bundle display name
|
17
19
|
# will be used. For most people with a new app, this is what you want to do, especially if
|
@@ -22,27 +24,37 @@ module CDQ
|
|
22
24
|
#
|
23
25
|
class CDQConfig
|
24
26
|
|
25
|
-
attr_reader :config_file, :database_name, :database_dir, :model_name, :name
|
27
|
+
attr_reader :config_file, :database_name, :database_dir, :model_name, :name, :icloud, :icloud_container
|
26
28
|
|
27
29
|
def initialize(config_file)
|
30
|
+
h = nil
|
28
31
|
case config_file
|
29
32
|
when String
|
30
33
|
@config_file = config_file
|
34
|
+
h = nil
|
31
35
|
if File.file?(config_file)
|
32
36
|
h = File.open(config_file) { |f| YAML.load(f.read) }
|
33
|
-
|
34
|
-
h =
|
37
|
+
# If a file was consisted comments only, it may parse as an Array.
|
38
|
+
h = nil unless h.is_a? Hash
|
35
39
|
end
|
36
40
|
when Hash
|
37
41
|
h = config_file
|
38
|
-
else
|
39
|
-
h = {}
|
40
42
|
end
|
43
|
+
h ||= {}
|
41
44
|
|
42
|
-
@name = h['name'] || h[:name] || NSBundle.mainBundle.objectForInfoDictionaryKey("
|
45
|
+
@name = h['name'] || h[:name] || NSBundle.mainBundle.objectForInfoDictionaryKey("CFBundleExecutable")
|
43
46
|
@database_dir = search_directory_for h['database_dir'] || h[:database_dir]
|
44
47
|
@database_name = h['database_name'] || h[:database_name] || name
|
45
48
|
@model_name = h['model_name'] || h[:model_name] || name
|
49
|
+
@icloud = begin
|
50
|
+
case h['icloud'] || h[:icloud]
|
51
|
+
when true, 1
|
52
|
+
true
|
53
|
+
else
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
@icloud_container = h['icloud_container'] || h[:icloud_container]
|
46
58
|
end
|
47
59
|
|
48
60
|
def database_url
|
data/motion/cdq/context.rb
CHANGED
@@ -1,13 +1,18 @@
|
|
1
|
-
|
2
1
|
module CDQ
|
3
2
|
|
4
3
|
class CDQContextManager
|
5
4
|
|
6
5
|
BACKGROUND_SAVE_NOTIFICATION = 'com.infinitered.cdq.context.background_save_completed'
|
6
|
+
DID_FINISH_IMPORT_NOTIFICATION = 'com.infinitered.cdq.context.did_finish_import'
|
7
7
|
|
8
8
|
def initialize(opts = {})
|
9
9
|
@store_manager = opts[:store_manager]
|
10
10
|
end
|
11
|
+
|
12
|
+
def dealloc
|
13
|
+
NSNotificationCenter.defaultCenter.removeObserver(self) if @observed_context
|
14
|
+
super
|
15
|
+
end
|
11
16
|
|
12
17
|
# Push a new context onto the stack for the current thread, making that context the
|
13
18
|
# default. If a block is supplied, push for the duration of the block and then
|
@@ -66,6 +71,7 @@ module CDQ
|
|
66
71
|
#
|
67
72
|
def new(concurrency_type, &block)
|
68
73
|
@has_been_set_up = true
|
74
|
+
|
69
75
|
context = NSManagedObjectContext.alloc.initWithConcurrencyType(concurrency_type)
|
70
76
|
if current
|
71
77
|
context.parentContext = current
|
@@ -73,7 +79,15 @@ module CDQ
|
|
73
79
|
if @store_manager.invalid?
|
74
80
|
raise "store coordinator not found. Cannot create the first context without one."
|
75
81
|
else
|
76
|
-
context.
|
82
|
+
context.mergePolicy = NSMergePolicy.alloc.initWithMergeType(NSMergeByPropertyObjectTrumpMergePolicyType)
|
83
|
+
context.performBlockAndWait ->{
|
84
|
+
coordinator = @store_manager.current
|
85
|
+
context.persistentStoreCoordinator = coordinator
|
86
|
+
#Dispatch::Queue.main.async {
|
87
|
+
NSNotificationCenter.defaultCenter.addObserver(self, selector:"did_finish_import:", name:NSPersistentStoreDidImportUbiquitousContentChangesNotification, object:nil)
|
88
|
+
@observed_context = context
|
89
|
+
#}
|
90
|
+
}
|
77
91
|
end
|
78
92
|
end
|
79
93
|
push(context, &block)
|
@@ -82,6 +96,7 @@ module CDQ
|
|
82
96
|
# Save all contexts in the stack, starting with the current and working down.
|
83
97
|
#
|
84
98
|
def save(options = {})
|
99
|
+
set_timestamps
|
85
100
|
always_wait = options[:always_wait]
|
86
101
|
stack.reverse.each do |context|
|
87
102
|
if always_wait || context.concurrencyType == NSMainQueueConcurrencyType
|
@@ -125,6 +140,15 @@ module CDQ
|
|
125
140
|
end
|
126
141
|
true
|
127
142
|
end
|
143
|
+
|
144
|
+
|
145
|
+
def did_finish_import(notification)
|
146
|
+
@observed_context.performBlockAndWait ->{
|
147
|
+
@observed_context.mergeChangesFromContextDidSaveNotification(notification)
|
148
|
+
NSNotificationCenter.defaultCenter.postNotificationName(DID_FINISH_IMPORT_NOTIFICATION, object:self, userInfo:{context: @observed_context})
|
149
|
+
}
|
150
|
+
end
|
151
|
+
|
128
152
|
|
129
153
|
private
|
130
154
|
|
@@ -189,7 +213,16 @@ module CDQ
|
|
189
213
|
end
|
190
214
|
end
|
191
215
|
end
|
216
|
+
|
217
|
+
def set_timestamps
|
218
|
+
now = Time.now
|
219
|
+
eos = current.insertedObjects.allObjects + current.updatedObjects.allObjects
|
220
|
+
eos.each do |e|
|
221
|
+
e.created_at ||= now if e.respond_to? :created_at=
|
222
|
+
e.updated_at = now if e.respond_to? :updated_at=
|
223
|
+
end
|
224
|
+
end
|
192
225
|
|
193
|
-
end
|
226
|
+
end
|
194
227
|
|
195
228
|
end
|
data/motion/cdq/object.rb
CHANGED
@@ -18,9 +18,9 @@ module CDQ
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def reset!(opts = {})
|
21
|
-
@@context_manager.reset!
|
21
|
+
@@context_manager.reset! if @@context_manager
|
22
22
|
@@context_manager = nil
|
23
|
-
@@store_manager.reset!
|
23
|
+
@@store_manager.reset! if @@store_manager
|
24
24
|
@@store_manager = nil
|
25
25
|
end
|
26
26
|
|
data/motion/cdq/store.rb
CHANGED
@@ -3,6 +3,8 @@ module CDQ
|
|
3
3
|
|
4
4
|
class CDQStoreManager
|
5
5
|
|
6
|
+
STORE_DID_INITIALIZE_NOTIFICATION = 'com.infinitered.cdq.store.did_initialize'
|
7
|
+
|
6
8
|
attr_writer :current
|
7
9
|
|
8
10
|
def initialize(opts = {})
|
@@ -10,6 +12,13 @@ module CDQ
|
|
10
12
|
@model_manager = opts[:model_manager]
|
11
13
|
end
|
12
14
|
|
15
|
+
def new(opts = {})
|
16
|
+
@config = opts[:config] || CDQConfig.default
|
17
|
+
@model_manager = opts[:model_manager] || CDQ.cdq.models
|
18
|
+
@icloud = opts[:icloud] || opts[:iCloud] || @config.icloud
|
19
|
+
@icloud_container = @config.icloud_container
|
20
|
+
end
|
21
|
+
|
13
22
|
def current
|
14
23
|
@current ||= create_store
|
15
24
|
end
|
@@ -28,27 +37,98 @@ module CDQ
|
|
28
37
|
if invalid?
|
29
38
|
raise "No model found. Can't create a persistent store coordinator without it."
|
30
39
|
else
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
40
|
+
if @icloud
|
41
|
+
create_icloud_store
|
42
|
+
else
|
43
|
+
create_local_store
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_icloud_store
|
49
|
+
coordinator = NSPersistentStoreCoordinator.alloc.initWithManagedObjectModel(@model_manager.current)
|
50
|
+
|
51
|
+
Dispatch::Queue.concurrent.async {
|
52
|
+
# get icloud first container
|
35
53
|
url = @config.database_url
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
54
|
+
icloud_url = NSFileManager.defaultManager.URLForUbiquityContainerIdentifier(@icloud_container)
|
55
|
+
if icloud_url
|
56
|
+
error = Pointer.new(:object)
|
57
|
+
icloud_url = icloud_url.URLByAppendingPathComponent("data")
|
58
|
+
error = Pointer.new(:object)
|
59
|
+
options = { NSMigratePersistentStoresAutomaticallyOption => true,
|
60
|
+
NSInferMappingModelAutomaticallyOption => true,
|
61
|
+
NSPersistentStoreUbiquitousContentNameKey => url.path.lastPathComponent.gsub(".", "_"),
|
62
|
+
NSPersistentStoreUbiquitousContentURLKey => icloud_url,
|
63
|
+
}
|
64
|
+
coordinator.lock
|
65
|
+
store = coordinator.addPersistentStoreWithType(NSSQLiteStoreType,
|
66
|
+
configuration:nil,
|
67
|
+
URL:url,
|
68
|
+
options:options,
|
69
|
+
error:error)
|
70
|
+
coordinator.unlock
|
71
|
+
|
72
|
+
if store.nil?
|
73
|
+
error[0].userInfo['metadata'] && error[0].userInfo['metadata'].each do |key, value|
|
74
|
+
NSLog "#{key}: #{value}"
|
75
|
+
end
|
76
|
+
raise error[0].userInfo['reason']
|
77
|
+
end
|
78
|
+
|
79
|
+
else
|
80
|
+
error = Pointer.new(:object)
|
81
|
+
options = { NSMigratePersistentStoresAutomaticallyOption => true,
|
82
|
+
NSInferMappingModelAutomaticallyOption => true }
|
83
|
+
url = @config.database_url
|
84
|
+
mkdir_p File.dirname(url.path)
|
85
|
+
store = coordinator.addPersistentStoreWithType(NSSQLiteStoreType,
|
86
|
+
configuration:nil,
|
87
|
+
URL:url,
|
88
|
+
options:options,
|
89
|
+
error:error)
|
90
|
+
if store.nil?
|
91
|
+
error[0].userInfo['metadata'] && error[0].userInfo['metadata'].each do |key, value|
|
92
|
+
NSLog "#{key}: #{value}"
|
93
|
+
end
|
94
|
+
raise error[0].userInfo['reason']
|
45
95
|
end
|
46
|
-
raise error[0].userInfo['reason']
|
47
96
|
end
|
48
|
-
|
97
|
+
Dispatch::Queue.main.after(0) {
|
98
|
+
# This block is executed in a next run loop.
|
99
|
+
# So the managed object context has a store coordinator in this point.
|
100
|
+
NSNotificationCenter.defaultCenter.postNotificationName(STORE_DID_INITIALIZE_NOTIFICATION, object:coordinator)
|
101
|
+
}
|
102
|
+
}
|
103
|
+
coordinator
|
104
|
+
end
|
105
|
+
|
106
|
+
def create_local_store
|
107
|
+
coordinator = NSPersistentStoreCoordinator.alloc.initWithManagedObjectModel(@model_manager.current)
|
108
|
+
error = Pointer.new(:object)
|
109
|
+
options = { NSMigratePersistentStoresAutomaticallyOption => true,
|
110
|
+
NSInferMappingModelAutomaticallyOption => true }
|
111
|
+
url = @config.database_url
|
112
|
+
mkdir_p File.dirname(url.path)
|
113
|
+
store = coordinator.addPersistentStoreWithType(NSSQLiteStoreType,
|
114
|
+
configuration:nil,
|
115
|
+
URL:url,
|
116
|
+
options:options,
|
117
|
+
error:error)
|
118
|
+
if store.nil?
|
119
|
+
error[0].userInfo['metadata'] && error[0].userInfo['metadata'].each do |key, value|
|
120
|
+
NSLog "#{key}: #{value}"
|
121
|
+
end
|
122
|
+
raise error[0].userInfo['reason']
|
49
123
|
end
|
124
|
+
Dispatch::Queue.main.after(0) {
|
125
|
+
# This block is executed in a next run loop.
|
126
|
+
# So the managed object context has a store coordinator in this point.
|
127
|
+
NSNotificationCenter.defaultCenter.postNotificationName(STORE_DID_INITIALIZE_NOTIFICATION, object:coordinator)
|
128
|
+
}
|
129
|
+
coordinator
|
50
130
|
end
|
51
|
-
|
131
|
+
|
52
132
|
def mkdir_p dir
|
53
133
|
error = Pointer.new(:object)
|
54
134
|
m = NSFileManager.defaultManager
|
@@ -58,7 +138,7 @@ module CDQ
|
|
58
138
|
raise error[0].localizedDescription
|
59
139
|
end
|
60
140
|
end
|
61
|
-
|
141
|
+
|
62
142
|
end
|
63
143
|
|
64
144
|
end
|
data/motion/managed_object.rb
CHANGED
@@ -63,7 +63,11 @@ class CDQManagedObject < CoreDataQueryManagedObjectBase
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def respond_to?(name)
|
66
|
-
|
66
|
+
if cdq_initialized?
|
67
|
+
super(name) || cdq.respond_to?(name)
|
68
|
+
else
|
69
|
+
super(name)
|
70
|
+
end
|
67
71
|
end
|
68
72
|
|
69
73
|
def destroy_all
|
@@ -77,6 +81,18 @@ class CDQManagedObject < CoreDataQueryManagedObjectBase
|
|
77
81
|
cdq.save
|
78
82
|
end
|
79
83
|
|
84
|
+
def cdq(obj = nil)
|
85
|
+
if obj
|
86
|
+
super(obj)
|
87
|
+
else
|
88
|
+
@cdq_object ||= super(nil)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def cdq_initialized?
|
93
|
+
!@cdq_object.nil?
|
94
|
+
end
|
95
|
+
|
80
96
|
end
|
81
97
|
|
82
98
|
# Register this object for destruction with the current context. Will not
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cdq
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- infinitered
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-08-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ruby-xcdm
|
@@ -20,7 +20,7 @@ dependencies:
|
|
20
20
|
version: '0.0'
|
21
21
|
- - ! '>='
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: 0.0.
|
23
|
+
version: 0.0.7
|
24
24
|
type: :runtime
|
25
25
|
prerelease: false
|
26
26
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -30,7 +30,7 @@ dependencies:
|
|
30
30
|
version: '0.0'
|
31
31
|
- - ! '>='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.0.
|
33
|
+
version: 0.0.7
|
34
34
|
- !ruby/object:Gem::Dependency
|
35
35
|
name: motion-yaml
|
36
36
|
requirement: !ruby/object:Gem::Requirement
|