cdq 1.0.2 → 1.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9e4159aee06a7a17669675f80c482c0cef41c456
4
- data.tar.gz: 73c3f008cffa0e014230287e8e21bd751f9dca5d
3
+ metadata.gz: 4ca83058a084e81380741e9f398d491ded74fb38
4
+ data.tar.gz: 7a3da15d054b1de39f696cca1357d66b2586a47c
5
5
  SHA512:
6
- metadata.gz: 21ce21b6ac607663ad50317d5a1de945984bdc509c8c521e385c3fdaad119b6e3aea286d4150b46ca9dfcc44f52e09996ba28b24df1270116332123b929b7639
7
- data.tar.gz: 495a1bf8692bf6c45762e78698870065ab4bb8df01a32185b46ff6d2b1bf1a65fea8652543fbd1f585a273941571a00daa43cc08562d3c720cfdf24f16f8a647
6
+ metadata.gz: 25e10d54dede83dcc44ead98586d04260a8618cb41aff696e5c2290091adf0838a70c8d84fa6a9f802fd56c376205b5003c8137d8288dfb3c8caa65a52078fa0
7
+ data.tar.gz: 88dd18915e1222995a1b6334bbe72e51f182274ae327ec04d646038bd2d1b93a8c9a41da47c40d1866e0db971d36b818567a72065fb8fd620cb488a3e9808e98
data/README.md CHANGED
@@ -117,6 +117,25 @@ app root, and they are versioned for automatic migrations, and this is what they
117
117
 
118
118
  Ruby-xcdm translates these files straight into the XML format that Xcode uses for datamodels.
119
119
 
120
+ ### Boolean Values
121
+
122
+ Since CoreData stores boolean values as an `NSNumber`, cdq provides helper
123
+ methods to allow you to get the boolean value of the property. Take the `Article`
124
+ model from above with the `boolean`:`published`. If you call `published` directly
125
+ you'll get the `NSNumber` `0` or `1`. If you call `published?` you'll get a
126
+ boolean `true` or `false`
127
+
128
+ ```ruby
129
+ article_1 = Article.create(published: true)
130
+ article_2 = Article.create(published: false)
131
+
132
+ article_1.published # => 1
133
+ article_2.published # => 0
134
+
135
+ article_1.published? # => true
136
+ article_2.published? # => false
137
+ ```
138
+
120
139
  ## Context Management
121
140
 
122
141
  Managing NSManagedObjectContext objects in Core Data can be tricky, especially
@@ -191,6 +210,8 @@ all the data over to your main context all at once. CDQ makes that easy too:
191
210
  cdq.save
192
211
  ```
193
212
 
213
+ **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`.
214
+
194
215
  ### Deleting
195
216
  ```ruby
196
217
  author = Author.first
@@ -249,6 +270,16 @@ Here are some examples. **See the [cheat sheet](https://github.com/infinitered/
249
270
  Author.where(:name).contains("Emily").and(cdq(:pub_count).gt(100).or.lt(10))
250
271
  ```
251
272
 
273
+ ### Calculations
274
+
275
+ ```ruby
276
+ Author.sum(:fee)
277
+ Author.average(:fee)
278
+ Author.min(:fee)
279
+ Author.max(:fee)
280
+ Author.where(:name).eq("Emily").sum(:fee)
281
+ ```
282
+
252
283
  ### Fetching
253
284
 
254
285
  Like ActiveRecord, CDQ will not run a fetch until you actually request specific
@@ -256,6 +287,7 @@ objects. There are several methods for getting at the data:
256
287
 
257
288
  * `array`
258
289
  * `first`
290
+ * `last`
259
291
  * `each`
260
292
  * `[]`
261
293
  * `map`
@@ -355,3 +387,12 @@ You can also set up iCloud in your cdq.yml file.
355
387
  * There are no explicit validations (but you can define them on your data model)
356
388
  * Lifecycle Callbacks or Observers
357
389
 
390
+ ## Tips
391
+
392
+ If you need, you could watch SQL statements by setting the following launch argument through `args` environment variable:
393
+
394
+ ```
395
+ $ rake args='-com.apple.CoreData.SQLDebug 3'
396
+ ```
397
+
398
+ `com.apple.CoreData.SQLDebug` takes a value between 1 and 3; the higher the value, the more verbose the output.
@@ -1,4 +1,4 @@
1
1
 
2
2
  module CDQ
3
- VERSION = '1.0.2'
3
+ VERSION = '1.0.3'
4
4
  end
@@ -22,8 +22,12 @@ module CDQ
22
22
  @objects
23
23
  end
24
24
 
25
- def first
26
- @objects.first
25
+ def first(n = 1)
26
+ n == 1 ? @objects.first : @objects.first(n)
27
+ end
28
+
29
+ def last(n = 1)
30
+ n == 1 ? @objects.last : @objects.last(n)
27
31
  end
28
32
 
29
33
  end
@@ -8,12 +8,14 @@ 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] 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.
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
+ # [app_group_id] The app group id set in iTunes member center (group.com.mycompany.myapp)
16
+ # [app_group_container_uuid] WORKAROUND: The app group's UUID for iOS Simulator 8.1 which doesn't return an app group container path from the id
17
+ # [icloud] If it's true, CDQ works with iCloud.
18
+ # [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.
17
19
  #
18
20
  # Using the config file is not necessary. If you do not include it, the bundle display name
19
21
  # will be used. For most people with a new app, this is what you want to do, especially if
@@ -24,7 +26,7 @@ module CDQ
24
26
  #
25
27
  class CDQConfig
26
28
 
27
- attr_reader :config_file, :database_name, :database_dir, :model_name, :name, :icloud, :icloud_container
29
+ attr_reader :config_file, :database_name, :database_dir, :model_name, :name, :icloud, :icloud_container, :app_group_id, :app_group_container_uuid
28
30
 
29
31
  def initialize(config_file)
30
32
  h = nil
@@ -46,6 +48,8 @@ module CDQ
46
48
  @database_dir = search_directory_for h['database_dir'] || h[:database_dir]
47
49
  @database_name = h['database_name'] || h[:database_name] || name
48
50
  @model_name = h['model_name'] || h[:model_name] || name
51
+ @app_group_id = h['app_group_id'] || h[:app_group_id]
52
+ @app_group_container_uuid = h['app_group_container_uuid'] || h[:app_group_container_uuid]
49
53
  @icloud = begin
50
54
  case h['icloud'] || h[:icloud]
51
55
  when true, 1
@@ -56,9 +60,14 @@ module CDQ
56
60
  end
57
61
  @icloud_container = h['icloud_container'] || h[:icloud_container]
58
62
  end
59
-
63
+
60
64
  def database_url
61
- dir = NSSearchPathForDirectoriesInDomains(database_dir, NSUserDomainMask, true).last
65
+ if app_group_id.nil?
66
+ dir = NSSearchPathForDirectoriesInDomains(database_dir, NSUserDomainMask, true).last
67
+ else
68
+ dir = app_group_container
69
+ end
70
+
62
71
  path = File.join(dir, database_name + '.sqlite')
63
72
  NSURL.fileURLWithPath(path)
64
73
  end
@@ -67,6 +76,17 @@ module CDQ
67
76
  NSBundle.mainBundle.URLForResource(model_name, withExtension: "momd");
68
77
  end
69
78
 
79
+ def app_group_container
80
+ if (UIDevice.currentDevice.model =~ /simulator/i).nil? # device
81
+ dir = NSFileManager.defaultManager.containerURLForSecurityApplicationGroupIdentifier(app_group_id).path
82
+ elsif ! app_group_container_uuid.nil? # simulator with app group uuid workaround
83
+ dev_container = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true).last.stringByDeletingLastPathComponent.stringByDeletingLastPathComponent.stringByDeletingLastPathComponent.stringByDeletingLastPathComponent
84
+ dir = dev_container.stringByAppendingPathComponent("Shared").stringByAppendingPathComponent("AppGroup").stringByAppendingPathComponent(app_group_container_uuid)
85
+ else # simulator no workaround, fallback to default dir
86
+ dir = NSSearchPathForDirectoriesInDomains(database_dir, NSUserDomainMask, true).last
87
+ end
88
+ end
89
+
70
90
  def self.default
71
91
  @default ||=
72
92
  begin
@@ -4,9 +4,9 @@ module CDQ
4
4
  class CDQRelationshipQuery < CDQTargetedQuery
5
5
 
6
6
  def initialize(owner, name, set = nil, opts = {})
7
- @owner = owner
7
+ @owner = owner ? WeakRef.new(owner) : nil
8
8
  @relationship_name = name
9
- @set = set
9
+ @set = set ? WeakRef.new(set) : nil
10
10
  relationship = owner.entity.relationshipsByName[name]
11
11
  if relationship.isToMany
12
12
  if @owner.ordered_set?(name)
@@ -20,12 +20,16 @@ module CDQ
20
20
  target_class = constantize(entity_description.managedObjectClassName)
21
21
  super(entity_description, target_class, opts)
22
22
  if @inverse_rel.isToMany
23
- @predicate = self.where(@inverse_rel.name.to_sym).contains(@owner).predicate
23
+ @predicate = self.where(@inverse_rel.name.to_sym).contains(owner).predicate
24
24
  else
25
25
  @predicate = self.where(@inverse_rel.name.to_sym => @owner).predicate
26
26
  end
27
27
  end
28
28
 
29
+ def dealloc
30
+ super
31
+ end
32
+
29
33
  # Creates a new managed object within the target relationship
30
34
  #
31
35
  def new(opts = {})
@@ -47,6 +51,10 @@ module CDQ
47
51
  @set.removeObject obj
48
52
  end
49
53
 
54
+ def remove_all
55
+ @set.removeAllObjects
56
+ end
57
+
50
58
  def self.extend_set(set, owner, name)
51
59
  set.extend SetExt
52
60
  set.extend Enumerable
@@ -72,8 +80,12 @@ module CDQ
72
80
  self.allObjects
73
81
  end
74
82
 
75
- def first
76
- array.first
83
+ def first(n = 1)
84
+ n == 1 ? array.first : array.first(n)
85
+ end
86
+
87
+ def last(n = 1)
88
+ n == 1 ? array.last : array.last(n)
77
89
  end
78
90
 
79
91
  # duplicating a lot of common methods because it's way faster than using method_missing
@@ -62,8 +62,18 @@ module CDQ #:nodoc:
62
62
  #
63
63
  # Causes execution.
64
64
  #
65
- def first
66
- limit(1).array.first
65
+ def first(n = 1)
66
+ result = limit(n).array
67
+ n == 1 ? result.first : result
68
+ end
69
+
70
+ # Return the last entity matching the query.
71
+ #
72
+ # Causes execution.
73
+ #
74
+ def last(n = 1)
75
+ result = offset(count - n).limit(n).array
76
+ n == 1 ? result.first : result
67
77
  end
68
78
 
69
79
  # Fetch a single entity from the query by index. If the optional
@@ -89,6 +99,57 @@ module CDQ #:nodoc:
89
99
  array.each(*args, &block)
90
100
  end
91
101
 
102
+ # Calculation method based on core data aggregate functions.
103
+ def calculate(operation, column_name)
104
+ raise("No context has been set. Probably need to run cdq.setup") unless context
105
+ raise("Cannot find attribute #{column_name} while calculating #{operation}") unless @entity_description.attributesByName[column_name.to_s]
106
+ desc_name = operation.to_s + '_of_' + column_name.to_s
107
+ fr = fetch_request.tap do |req|
108
+ req.propertiesToFetch = [
109
+ NSExpressionDescription.alloc.init.tap do |desc|
110
+ desc.name = desc_name
111
+ desc.expression = NSExpression.expressionForFunction(operation.to_s + ':', arguments: [ NSExpression.expressionForKeyPath(column_name.to_s) ])
112
+ desc.expressionResultType = @entity_description.attributesByName[column_name.to_s].attributeType
113
+ end
114
+ ]
115
+ req.resultType = NSDictionaryResultType
116
+ end
117
+ with_error_object([]) do |error|
118
+ r = context.executeFetchRequest(fr, error:error)
119
+ r.first[desc_name]
120
+ end
121
+ end
122
+
123
+ # Calculates the sum of values on a given column.
124
+ #
125
+ # Author.sum(:fee) # => 6.0
126
+ def sum(column_name)
127
+ calculate(:sum, column_name)
128
+ end
129
+
130
+ # Calculates the average of values on a given column.
131
+ #
132
+ # Author.average(:fee) # => 2.0
133
+ def average(column_name)
134
+ calculate(:average, column_name)
135
+ end
136
+
137
+ # Calculates the minimum of values on a given column.
138
+ #
139
+ # Author.min(:fee) # => 1.0
140
+ def min(column_name)
141
+ calculate(:min, column_name)
142
+ end
143
+ alias :minimum :min
144
+
145
+ # Calculates the maximum of values on a given column.
146
+ #
147
+ # Author.max(:fee) # => 3.0
148
+ def max(column_name)
149
+ calculate(:max, column_name)
150
+ end
151
+ alias :maximum :max
152
+
92
153
  # Returns the fully-contstructed fetch request, which can be executed outside of CDQ.
93
154
  #
94
155
  def fetch_request
@@ -42,11 +42,11 @@ class CDQManagedObject < CoreDataQueryManagedObjectBase
42
42
  def scope(name, query = nil, &block)
43
43
  cdq.scope(name, query, &block)
44
44
  if query
45
- self.class.send(:define_method, name) do
45
+ define_method(name) do
46
46
  where(query)
47
47
  end
48
48
  else
49
- self.class.send(:define_method, name) do |*args|
49
+ define_method(name) do |*args|
50
50
  where(block.call(*args))
51
51
  end
52
52
  end
@@ -174,6 +174,15 @@ class CDQManagedObject < CoreDataQueryManagedObjectBase
174
174
  objectID.URIRepresentation.absoluteString.inspect
175
175
  end
176
176
 
177
+ def method_missing(name, *args, &block)
178
+ if name[-1] == "?"
179
+ property_name = name[0...-1]
180
+ if entity.propertiesByName[property_name] && entity.propertiesByName[property_name].attributeType == NSBooleanAttributeType
181
+ send(property_name) == 1 ? true : false
182
+ end
183
+ end
184
+ end
185
+
177
186
  protected
178
187
 
179
188
  # Called from method that's dynamically added from
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: 1.0.2
4
+ version: 1.0.3
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: 2015-03-18 00:00:00.000000000 Z
12
+ date: 2015-06-20 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.8
23
+ version: 0.0.9
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.8
33
+ version: 0.0.9
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: motion-yaml
36
36
  requirement: !ruby/object:Gem::Requirement