cdq 0.1.9 → 0.1.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|