cdq 0.1.2 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZmUwYzY4Nzg0NGQxN2E1YWE1ZTMxM2I0M2Y0NzEyMDFkNGU3ZDYwNw==
4
+ NzEwODIzMjYzYjBlY2YzYzVjODMzOGFmYjgzZTdhNDA4ZjQzZjJlMw==
5
5
  data.tar.gz: !binary |-
6
- MzA2ZGFjODI3MzMwYTYwNDViOTQyNWFhZTljYTMyZTg5NmU4Zjc5Mg==
6
+ NzM1MDIxYmMzZDIxNzY4ZWFmOGQzYjZhOWQ2MjM0NjA3NDQ1Njg4MQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- YzgxZGYzMjIxZjk3MTYxYThkYTVjMTllNzYyOWJhZjhjMjAyNGU3ODNlZGZm
10
- ZmI4NTgzZjVjNjc5YjVmN2YxNGFjN2ZkZDllNTk0YjZiM2MwZjhiYmMzOGJi
11
- YzllZDQxN2QxM2Q5ZTM4OTIxNjA4NjA2ZDM3NzQ0YmE0MWMzZWM=
9
+ Y2JhZGRhYjRiYzRmNTlhZTJmZjA1ZmViNzdjOTU1YWI2OTgxNDY1MjNlNDg1
10
+ NTIzYWE3NGU2MTBmZmY1NWI2OTA2MDllNGZiNzQwMzlkMjE0MTcxYTRmZmUz
11
+ NTEwOGUxODdjMTM3NGFiZmU5YzcxNDRlYjVmMzJlYzUwZGZmYmY=
12
12
  data.tar.gz: !binary |-
13
- ZWVjZGUxNTBmYTdkNTMwMDg5NDZhZjRlMTQxYTM3NDU1YWM3YjhkZjcyOTA4
14
- ZjRjNjY1YTk2NGU0NTY0YTIzY2JmZmUzMjUzNDIzMjg5NGJkYjhhMjRiZDZh
15
- OWI0NDY5ZGQ4YjE4NTllODJiYzI0ZjVmMjg4NzZlZDVmY2Q5MjQ=
13
+ ZDI1M2Y3M2RmNzNiY2Q2NjQwYjY1OGU5OGJkMTUxZTI5MmE3YjRjMTIwOTU4
14
+ YzBiYzBkY2I2Zjc2MWJiNTdkYWI4MzY1ZjJlMmE4ODk2YjQ3ZDAxYzkyZmYy
15
+ MzY4ZGVkYmM5ZGQzZmE1NTE4OWI0YTFhZDg0OGI2ZTIzYWNjY2I=
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 InfiniteRed LLC (http://infinitered.com)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md CHANGED
@@ -4,8 +4,12 @@
4
4
  Core Data Query (CDQ) is a library to help you manage your Core Data stack
5
5
  while using RubyMotion. It uses a data model file, which you can generate in
6
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.
7
+
8
+ [![Dependency Status](https://gemnasium.com/infinitered/cdq.png)](https://gemnasium.com/infinitered/cdq)
9
+ [![Build Status](https://travis-ci.org/infinitered/cdq.png?branch=master)](https://travis-ci.org/infinitered/cdq)
10
+ [![Gem Version](https://badge.fury.io/rb/cdq.png)](http://badge.fury.io/rb/cdq)
11
+
12
+ ## Introduction
9
13
 
10
14
  CDQ began its life as a fork of
11
15
  [MotionData](https://github.com/alloy/MotionData), but it became obvious I
@@ -14,9 +18,10 @@ rewriting almost everything. If you pay attention, you can still find the
14
18
  genetic traces, so thanks to @alloy for sharing his work and letting me learn
15
19
  so much.
16
20
 
17
- [![Dependency Status](https://gemnasium.com/infinitered/cdq.png)](https://gemnasium.com/infinitered/cdq)
18
- [![Build Status](https://travis-ci.org/infinitered/cdq.png?branch=master)](https://travis-ci.org/infinitered/cdq)
19
- [![Gem Version](https://badge.fury.io/rb/cdq.png)](http://badge.fury.io/rb/cdq)
21
+ CDQ aims to streamline the process of getting you up and running Core Data, while
22
+ avoiding too much abstraction or method pollution on top of the SDK. While it
23
+ borrows many ideas from ActiveRecord (especially AREL), it is designed to
24
+ harmonize with Core Data's way of doing things first.
20
25
 
21
26
  ### Why use a static Data Model?
22
27
 
@@ -26,7 +31,16 @@ schema as it grows, if you can follow a few [simple rules](https://developer.app
26
31
 
27
32
  ## Installing
28
33
 
29
- ##### Quick Start:
34
+ ### Quick Start:
35
+
36
+ For brand-new apps, or just to try it out, check out the
37
+ [Greenfield Quick Start](https://github.com/infinitered/cdq/wiki/Greenfield-Quick-Start) tutorial.
38
+ There's also a
39
+ [Cheat Sheet](https://github.com/infinitered/cdq/wiki/CDQ-Cheat-Sheet)
40
+ and
41
+ [API docs](http://rubydoc.info/github/infinitered/cdq).
42
+
43
+ ### Even Quicker Start:
30
44
 
31
45
  ```bash
32
46
  $ gem install cdq
@@ -37,7 +51,7 @@ $ cdq init
37
51
 
38
52
  This way assumes you want to use ruby-xcdm. Run ```cdq -h``` for list of more generators.
39
53
 
40
- ##### Using Bundler:
54
+ ### Using Bundler:
41
55
 
42
56
  ```ruby
43
57
  gem 'cdq'
@@ -56,7 +70,7 @@ it to your resources file and make sure it's named the same as your RubyMotion
56
70
  project. If you're using `ruby-xcdm` (which I highly recommend) then it will
57
71
  create the datamodel file automatically and put it in the right place.
58
72
 
59
- Now include the setup code in your `app_delegate.rb` file:
73
+ Now include the setup code in your ```app_delegate.rb``` file:
60
74
 
61
75
  ```ruby
62
76
  class AppDelegate
@@ -73,19 +87,102 @@ That's it! You can create specific implementation classes for your entities if
73
87
  you want, but it's not required. You can start running queries on the console or
74
88
  in your code right away.
75
89
 
76
- ## Creating new objects
90
+ ## Schema
91
+
92
+ The best way to use CDQ is together with ruby-xcdm, which is installed as a
93
+ dependency. For the full docs, see its [github page](http://github.com/infinitered/ruby-xcdm),
94
+ but here's a taste. Schema files are found in the "schemas" directory within your
95
+ app root, and they are versioned for automatic migrations, and this is what they look like:
96
+
97
+ ```ruby
98
+ schema "0001 initial" do
99
+
100
+ entity "Article" do
101
+
102
+ string :body, optional: false
103
+ integer32 :length
104
+ boolean :published, default: false
105
+ datetime :publishedAt, default: false
106
+ string :title, optional: false
107
+
108
+ belongs_to :author
109
+ end
110
+
111
+ entity "Author" do
112
+ float :fee
113
+ string :name, optional: false
114
+ has_many :articles
115
+ end
116
+
117
+ end
118
+ ```
119
+
120
+ Ruby-xcdm translates these files straight into the XML format that Xcode uses for datamodels.
121
+
122
+ ## Context Management
123
+
124
+ Managing NSManagedObjectContext objects in Core Data can be tricky, especially
125
+ if you are trying to take advantage of nested contexts for better threading
126
+ behavior. One of the best parts of CDQ is that it handles contexts for you
127
+ relatively seamlessly. If you have a simple app, you may never need to worry
128
+ about contexts at all.
129
+
130
+ ### Nested Contexts
131
+
132
+ For a great discussion of why you might want to use nested contexts, see [here](http://www.cocoanetics.com/2012/07/multi-context-coredata/).
77
133
 
78
- CDQ maintains a stack of NSManagedObjectContexts for you, and any create
79
- operations will insert new, unsaved objects into the context currently on top
80
- of the stack. `cdq.save` will save the whole stack, including correctly using
81
- private threads if appropriate.
134
+ CDQ maintains a stack of contexts (one stack per thread), and by default, all
135
+ operations on objects use the topmost context. You just call ```cdq.save```
136
+ and it saves the whole stack. Or you can get a list of all the contexts in
137
+ order with ```cdq.contexts.all``` and do more precise work.
138
+
139
+ Settings things up the way you want is easy. Here's how you'd set it up for asynchronous
140
+ saves:
82
141
 
83
142
  ```ruby
84
- cdq('Author').create(name: "Le Guin", publish_count: 150, first_published: 1970)
85
- cdq('Author').create(name: "Shakespeare", publish_count: 400, first_published: 1550)
86
- cdq('Author').create(name: "Blake", publish_count: 100, first_published: 1778)
87
- cdq.save
143
+ cdq.contexts.new(NSPrivateQueueConcurrencyType)
144
+ cdq.contexts.new(NSMainQueueConcurrencyType)
145
+ ```
146
+
147
+ This pushes a private queue context onto the bottom of the stack, then a main queue context on top of it.
148
+ Since the main queue is on top, all your data operations will use that. ```cdq.save``` then saves the
149
+ main context, and schedules a save on the root context.
150
+
151
+ ### Temporary Contexts
152
+
153
+ From time to time, you may need to use a temporary context. For example, on
154
+ importing a large amount of data from the network, it's best to process and
155
+ load into a temporary context (possibly in a background thread) and then move
156
+ all the data over to your main context all at once. CDQ makes that easy too:
88
157
 
158
+ ```ruby
159
+ cdq.contexts.new(NSConfinementConcurrencyType) do
160
+ # Your work here
161
+ end
162
+ ```
163
+
164
+ ## Object Lifecycle
165
+
166
+ ### Creating
167
+ ```ruby
168
+ Author.create(name: "Le Guin", publish_count: 150, first_published: 1970)
169
+ Author.create(name: "Shakespeare", publish_count: 400, first_published: 1550)
170
+ Author.create(name: "Blake", publish_count: 100, first_published: 1778)
171
+ cdq.save
172
+ ```
173
+
174
+ ### Updating
175
+ ```ruby
176
+ author = Author.first
177
+ author.name = "Ursula K. Le Guin"
178
+ cdq.save
179
+ ```
180
+
181
+ ### Deleting
182
+ ```ruby
183
+ author = Author.first
184
+ author.destroy
185
+ cdq.save
89
186
  ```
90
187
 
91
188
  ## Queries
@@ -100,71 +197,86 @@ running queries that only return a single object, consider rearchitecting.
100
197
  That said, queries are sometimes the only solution, and it's very handy to be
101
198
  able to use them easily when debugging from the console, or in unit tests.
102
199
 
103
- With that out of the way, here are some samples:
104
-
105
- ```ruby
106
- # Simple Queries
107
- cdq('Author').where(:name).eq('Shakespeare')
108
- cdq('Author').where(:publish_count).gt(10)
200
+ All of these queries are infinitely daisy-chainable, and almost everything is
201
+ possible to do using only chained methods, no need to drop into NSPredicate format
202
+ strings unless you want to.
109
203
 
110
- # sort, limit, and offset
111
- cdq('Author').sort_by(:created_at).limit(1).offset(10)
204
+ Here are some examples. See the [cheat
205
+ sheet](https://github.com/infinitered/cdq/wiki/CDQ-Cheat-Sheet) for a complete
206
+ list.
112
207
 
113
- # Compound queries
114
- cdq('Author').where(:name).eq('Blake').and(:first_published).le(Time.local(1700))
208
+ ### Conditions
115
209
 
116
- # Multiple comparisons against the same attribute
117
- cdq('Author').where(:created_at).ge(yesterday).and.lt(today)
210
+ ```ruby
211
+ Author.where(:name).eq('Shakespeare')
212
+ Author.where(:publish_count).gt(10)
213
+ Author.where(name: 'Shakespeare', publish_count: 15)
214
+ Author.where("name LIKE '%@'", '%kesp%')
118
215
  ```
119
216
 
120
- ## Named Scopes
217
+ ### Sorts, Limits and Offsets
121
218
 
122
- You can save up partially-constructed queries for later use using named scopes, even
123
- combining them seamlessly with other queries or other named scopes:
219
+ ```ruby
220
+ Author.sort_by(:created_at).limit(1).offset(10)
221
+ Author.sort_by(:created_at, :descending)
222
+ ```
223
+
224
+ ### Conjunctions
124
225
 
125
226
  ```ruby
126
- cdq('Author').scope :a_authors, cdq(:name).begins_with('A')
127
- cdq('Author').scope :prolific, cdq(:publish_count).gt(99)
227
+ Author.where(:name).eq('Blake').and(:first_published).le(Time.local(1700))
128
228
 
129
- cdq('Author').prolific.a_authors.limit(5)
229
+ # Multiple comparisons against the same attribute
230
+ Author.where(:created_at).ge(yesterday).and.lt(today)
130
231
  ```
131
232
 
132
- > NOTE: strings and symbols are NOT interchangeable. `cdq('Entity')` gives you a
133
- query generator for an entity, but `cdq(:attribute)` starts a predicate for an
134
- attribute.
233
+ #### Nested Conjunctions
234
+
235
+ ```ruby
236
+ Author.where(:name).contains("Emily").and(cdq(:pub_count).gt(100).or.lt(10))
237
+ ```
238
+
239
+ ### Fetching
240
+
241
+ Like ActiveRecord, CDQ will not run a fetch until you actually request specific
242
+ objects. There are several methods for getting at the data:
243
+
244
+ * ```array```
245
+ * ```first```
246
+ * ```each```
247
+ * ```[]```
248
+ * ```map```
249
+ * Anything else in ```Enumerable```
135
250
 
136
251
  ## Dedicated Models
137
252
 
138
253
  If you're using CDQ in a brand new project, you'll probably want to use
139
- dedicated model classes for your entities. It will enable the usual
140
- class-level customization of your model objects, and also a somewhat more
254
+ dedicated model classes for your entities.
141
255
  familiar-looking and natural syntax for queries and scopes:
142
256
 
143
257
  ```ruby
144
- class Author < CDQManagedOjbect
145
- scope :a_authors, cdq(:name).begins_with('A')
146
- scope :prolific, cdq(:publish_count).gt(99)
147
- end
258
+ class Author < CDQManagedOjbect
259
+ end
148
260
  ```
149
261
 
150
- Now you can change the queries above to:
262
+ ## Named Scopes
263
+
264
+ You can save up partially-constructed queries for later use using named scopes, even
265
+ combining them seamlessly with other queries or other named scopes:
151
266
 
152
267
  ```ruby
153
- Author.where(:name).eq('Shakespeare')
154
- Author.where(:publish_count).gt(10)
155
- Author.where(:name).eq('Blake').and(:first_published).le(Time.local(1700))
156
- Author.where(:created_at).ge(yesterday).and.lt(today)
157
- Author.prolific.a_authors.limit(5)
158
- ```
268
+ class Author < CDQManagedOjbect
269
+ scope :a_authors, where(:name).begins_with('A')
270
+ scope :prolific, where(:publish_count).gt(99)
271
+ end
159
272
 
160
- Anything you can do with `cdq('Author')` you can now do with just `Author`. If you have a
161
- pre-existing implementation class that you can't turn into a CDQManagedObject, you can also
162
- just wrap the class: `cdq(Author)`.
273
+ Author.prolific.a_authors.limit(5)
274
+ ```
163
275
 
164
276
  ## Using CDQ with a pre-existing model
165
277
 
166
278
  If you have an existing app that already manages its own data model, you can
167
- use that, too, and override CDQ's stack at any layer:
279
+ still use CDQ, overriding its stack at any layer:
168
280
 
169
281
  ```ruby
170
282
  cdq.setup(context: App.delegate.mainContext) # don't set up model or store coordinator
@@ -173,12 +285,48 @@ cdq.setup(model: App.delegate.managedObjectModel) # Don't load model
173
285
  ```
174
286
 
175
287
  You cannot use CDQManagedObject as a base class when overriding this way,
176
- you'll need to use <tt>cdq('Entity')</tt>. If you have an existing model and
177
- want to use it with CDQManagedObject without changing its name, You'll need to
178
- use a <tt>cdq.yml</tt> config file. See [CDQConfig](motion/cdq/config.rb).
288
+ you'll need to use the master method, described below. If you have an
289
+ existing model and want to use it with CDQManagedObject without changing its
290
+ name, You'll need to use a <tt>cdq.yml</tt> config file. See
291
+ [CDQConfig](http://github.com/infinitered/cdq/tree/master/motion/cdq/config.rb).
292
+
293
+ ### Working without model classes using the master method
294
+
295
+ If you need or want to work without using CDQManagedObject as your base class,
296
+ you can use the ```cdq()``` master method. This is a "magic" method, like
297
+ ```rmq()``` in [RubyMotionQuery](http://github.com/infinitered/rmq) or
298
+ ```$()``` in jQuery, which will lift whatever you pass into it into the CDQ
299
+ universe. The method is available inside all UIResponder classes (so, views and
300
+ controllers) as well as in the console. You can use it anywhere else by
301
+ including the model ```CDQ``` into your classes. To use an entity without a
302
+ model class, just pass its name as a string into the master method, like so
303
+
304
+ ```ruby
305
+ cdq('Author').where(:name).eq('Shakespeare')
306
+ cdq('Author').where(:publish_count).gt(10)
307
+ cdq('Author').sort_by(:created_at).limit(1).offset(10)
308
+ ```
309
+
310
+ Anything you can do with a model, you can also do with the master method, including
311
+ defining and using named scopes:
312
+
313
+ ```ruby
314
+ cdq('Author').scope :a_authors, cdq(:name).begins_with('A')
315
+ cdq('Author').scope :prolific, cdq(:publish_count).gt(99)
316
+ ```
317
+ > NOTE: strings and symbols are NOT interchangeable. `cdq('Entity')` gives you a
318
+ query generator for an entity, but `cdq(:attribute)` starts a predicate for an
319
+ attribute.
320
+
321
+ ## Documentation
322
+
323
+ * [API](http://rubydoc.info/github/infinitered/cdq)
324
+ * [Cheat Sheet](https://github.com/infinitered/cdq/wiki/CDQ-Cheat-Sheet)
325
+ * [Tutorial](https://github.com/infinitered/cdq/wiki/Greenfield-Quick-Start)
179
326
 
180
327
  ## Things that are currently missing
181
328
 
182
329
  * There is no facility for custom migrations yet
183
330
  * There are no explicit validations (but you can define them on your data model)
331
+ * Lifecycle Callbacks or Observers
184
332
 
@@ -103,19 +103,7 @@ Options:
103
103
  end
104
104
  puts "Done."
105
105
 
106
- puts %{
107
- Now open app/app_delegate.rb, and add
108
-
109
- include CDQ
110
-
111
- at class level, and
112
-
113
- cdq.setup
114
-
115
- wherever you want the stack to get set up, probably right before you set
116
- your root controller. Edit your inital schema, create a few models, and
117
- you're off and running.
118
- }
106
+ puts %{\n Now edit schemas/0001_initial.rb to define your schema, and you're off and running. }
119
107
 
120
108
  end
121
109
  end
@@ -1,4 +1,4 @@
1
1
 
2
2
  module CDQ
3
- VERSION = '0.1.2'
3
+ VERSION = '0.1.5'
4
4
  end
@@ -8,6 +8,41 @@ module CDQ
8
8
 
9
9
  extend self
10
10
 
11
+ # The master method that will lift a variety of arguments into the CDQ
12
+ # ecosystem. What it returns depends on the type of the argument passed:
13
+ #
14
+ # Class: Finds an entity with the same name as the class and returns a
15
+ # targeted query. (This is also used internally for dedicated models.)
16
+ #
17
+ # cdq(Author).where(:attribute).eq(1).limit(3)
18
+ #
19
+ # String: Finds an entity with the name provided in the string and returns a
20
+ # targeted query.
21
+ #
22
+ # cdq('Author').where(:attribute).eq(1).limit(3)
23
+ #
24
+ # Symbol: Returns an untargeted partial predicate. This is useful for nested
25
+ # queries, and for defining scopes.
26
+ #
27
+ # Author.scope :singletons, cdq(:attribute).eq(1)
28
+ # Author.where( cdq(:attribute).eq(1).or.eq(3) ).and(:name).ne("Roger")
29
+ #
30
+ # CDQObject: returns the object itself (no-op).
31
+ #
32
+ # NSManagedObject: wraps the object in a CDQObjectProxy, which permits
33
+ # cdq-style queries on the object's relationships.
34
+ #
35
+ # emily_dickinson = Author.first
36
+ # cdq(emily_dickinson).articles.where(:page_count).lt(5).array
37
+ #
38
+ # Array: wraps the array in a CDQCollectionProxy, which lets you run queries
39
+ # relative to the members of the collection.
40
+ #
41
+ # emily_dickinson = Author.first
42
+ # edgar_allen_poe = Author.all[4]
43
+ # charles_dickens = Author.all[7]
44
+ # cdq([emily_dickinson, edgar_allen_poe, charles_dickens]).where(:avg_rating).eq(1)
45
+ #
11
46
  def cdq(obj = nil)
12
47
  obj ||= self
13
48
 
@@ -48,15 +83,15 @@ module CDQ
48
83
  end
49
84
  end
50
85
 
51
- def self.pollute(klass)
52
- end
53
-
54
86
  end
55
87
 
88
+
89
+ # @private
56
90
  class UIResponder
57
91
  include CDQ
58
92
  end
59
93
 
94
+ # @private
60
95
  class TopLevel
61
96
  include CDQ
62
97
  end