motion_migrate 0.1.0

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 (43) hide show
  1. data/.gitignore +17 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +152 -0
  6. data/Rakefile +3 -0
  7. data/lib/motion_migrate/generate.rb +51 -0
  8. data/lib/motion_migrate/model.rb +10 -0
  9. data/lib/motion_migrate/motion_generate/entity.rb +15 -0
  10. data/lib/motion_migrate/motion_generate/io.rb +79 -0
  11. data/lib/motion_migrate/motion_generate/parser.rb +27 -0
  12. data/lib/motion_migrate/motion_generate/property.rb +171 -0
  13. data/lib/motion_migrate/motion_generate/relationship.rb +153 -0
  14. data/lib/motion_migrate/motion_model/property.rb +14 -0
  15. data/lib/motion_migrate/motion_model/relationship.rb +20 -0
  16. data/lib/motion_migrate.rb +20 -0
  17. data/lib/tasks/migrate.rake +53 -0
  18. data/lib/version.rb +3 -0
  19. data/motion_migrate.gemspec +21 -0
  20. data/spec/property_spec.rb +113 -0
  21. data/spec/relationship_spec.rb +54 -0
  22. data/spec_project/.gitignore +18 -0
  23. data/spec_project/Gemfile +6 -0
  24. data/spec_project/Rakefile +14 -0
  25. data/spec_project/app/app_delegate.rb +7 -0
  26. data/spec_project/app/models/pilot.rb +7 -0
  27. data/spec_project/app/models/plane.rb +11 -0
  28. data/spec_project/db/schema.xcdatamodeld/.xccurrentversion +8 -0
  29. data/spec_project/db/schema.xcdatamodeld/schema.1.xcdatamodel/contents +6 -0
  30. data/spec_project/db/schema.xcdatamodeld/schema.10.xcdatamodel/contents +18 -0
  31. data/spec_project/db/schema.xcdatamodeld/schema.2.xcdatamodel/contents +7 -0
  32. data/spec_project/db/schema.xcdatamodeld/schema.3.xcdatamodel/contents +7 -0
  33. data/spec_project/db/schema.xcdatamodeld/schema.4.xcdatamodel/contents +8 -0
  34. data/spec_project/db/schema.xcdatamodeld/schema.5.xcdatamodel/contents +9 -0
  35. data/spec_project/db/schema.xcdatamodeld/schema.6.xcdatamodel/contents +13 -0
  36. data/spec_project/db/schema.xcdatamodeld/schema.7.xcdatamodel/contents +14 -0
  37. data/spec_project/db/schema.xcdatamodeld/schema.8.xcdatamodel/contents +16 -0
  38. data/spec_project/db/schema.xcdatamodeld/schema.9.xcdatamodel/contents +16 -0
  39. data/spec_project/spec/helper.rb +8 -0
  40. data/spec_project/spec/property_spec.rb +57 -0
  41. data/spec_project/spec/relationship_spec.rb +105 -0
  42. data/spec_project/vendor/Podfile.lock +11 -0
  43. metadata +113 -0
@@ -0,0 +1,153 @@
1
+ module MotionMigrate
2
+ module MotionGenerate
3
+ module Relationship
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ def belongs_to(name, options={})
10
+ relationship(name, :belongs_to, options)
11
+ end
12
+
13
+ def has_many(name, options={})
14
+ relationship(name, :has_many, options)
15
+ end
16
+
17
+ def relationship(name, type, options={})
18
+ options.each { |key, value| raise_if_relationship_option_not_allowed(type, key) }
19
+
20
+ raise_if_relationship_options_missing(options)
21
+ raise_if_deletion_rule_not_allowed(options)
22
+
23
+ attributes = {
24
+ name: name,
25
+ optional: core_data_boolean(true),
26
+ deletionRule: core_data_string(:no_action),
27
+ syncable: core_data_boolean(true)
28
+ }
29
+ attributes.merge!({ minCount: 1, maxCount: 1 }) if type == :belongs_to
30
+ attributes.merge!(core_data_relationship_attributes(type, options))
31
+ relationships[self.entity_name] = {} if relationships[self.entity_name].nil?
32
+ relationships[self.entity_name][name] = attributes
33
+ attributes
34
+ end
35
+
36
+ def relationships
37
+ @@relationships ||= {}
38
+ end
39
+
40
+ def raise_if_relationship_option_not_allowed(type, option)
41
+ unless relationship_option_allowed?(type, option)
42
+ raise <<-ERROR
43
+ ! The option must be one of the following:
44
+ !
45
+ ! For type :belongs_to:
46
+ ! :required
47
+ ! :deletion_rule
48
+ ! :class_name
49
+ ! :inverse_of
50
+ ! :spotlight
51
+ ! :truth_file
52
+ ! :transient
53
+ !
54
+ ! For type :has_many:
55
+ ! :required
56
+ ! :min
57
+ ! :max
58
+ ! :deletion_rule
59
+ ! :class_name
60
+ ! :inverse_of
61
+ ! :ordered
62
+ ! :spotlight
63
+ ! :truth_file
64
+ ! :transient
65
+ ERROR
66
+ end
67
+ end
68
+
69
+ def raise_if_relationship_options_missing(options)
70
+ unless relationship_options_complete?(options)
71
+ raise <<-ERROR
72
+ ! One of these options are required:
73
+ ! :class_name
74
+ ! :inverse_of
75
+ ERROR
76
+ end
77
+ end
78
+
79
+ def deletion_rule_allowed?(options)
80
+ allowed_deletion_rules = [
81
+ :nullify,
82
+ :cascade,
83
+ :deny
84
+ ]
85
+ !options[:deletion_rule] || allowed_deletion_rules.include?(options[:deletion_rule])
86
+ end
87
+
88
+ def raise_if_deletion_rule_not_allowed(options)
89
+ unless deletion_rule_allowed?(options)
90
+ raise <<-ERROR
91
+ ! One of these deletion rules are allowed:
92
+ ! :nullify
93
+ ! :cascade
94
+ ! :deny
95
+ ERROR
96
+ end
97
+ end
98
+
99
+ def relationship_options_complete?(options)
100
+ required_options = [:class_name, :inverse_of]
101
+ (required_options.uniq - options.keys.uniq).empty?
102
+ end
103
+
104
+ def relationship_option_allowed?(type, option)
105
+ allowed_options = {
106
+ has_many: [:ordered, :min, :max],
107
+ }[type] || []
108
+
109
+ allowed_options += [
110
+ :required,
111
+ :spotlight,
112
+ :truth_file,
113
+ :transient,
114
+ :inverse_of,
115
+ :class_name,
116
+ :deletion_rule
117
+ ]
118
+ allowed_options.include?(option)
119
+ end
120
+
121
+ def core_data_relationship_attributes(type, options)
122
+ attributes = {}
123
+
124
+ options.each do |key, value|
125
+ case key
126
+ when :required
127
+ attributes[:optional] = core_data_boolean(value != true)
128
+ when :inverse_of
129
+ attributes[:inverseName] = value
130
+ when :class_name
131
+ attributes[:inverseEntity] = value
132
+ attributes[:destinationEntity] = value
133
+ when :deletion_rule
134
+ attributes[:deletionRule] = core_data_string(value)
135
+ when :transient
136
+ attributes[:transient] = core_data_boolean(value)
137
+ when :spotlight
138
+ attributes[:spotlightIndexingEnabled] = core_data_boolean(value)
139
+ when :truth_file
140
+ attributes[:storedInTruthFile] = core_data_boolean(value)
141
+ when :min
142
+ attributes[:minCount] = value
143
+ when :max
144
+ attributes[:maxCount] = value
145
+ end
146
+ end
147
+ attributes[:toMany] = core_data_boolean(type == :has_many)
148
+ attributes
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,14 @@
1
+ module MotionMigrate
2
+ module MotionModel
3
+ module Property
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ def property(name, type, options={})
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ module MotionMigrate
2
+ module MotionModel
3
+ module Relationship
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ def belongs_to(name, options={})
10
+ end
11
+
12
+ def has_many(name, options={})
13
+ end
14
+
15
+ def belongs_to(name, options={})
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ unless defined?(Motion::Project::Config)
2
+ raise "This file must be required within a RubyMotion project Rakefile."
3
+ end
4
+
5
+ Motion::Project::App.setup do |app|
6
+ Dir.glob(File.join(File.dirname(__FILE__), "motion_migrate/motion_model/**/*.rb")).each do |file|
7
+ app.files.unshift(file)
8
+ end
9
+ app.files.unshift(File.join(File.dirname(__FILE__), 'motion_migrate/model.rb'))
10
+
11
+ app.frameworks += ['CoreData'] unless app.frameworks.include?("CoreData")
12
+ end
13
+
14
+ module MotionMigrate
15
+ end
16
+
17
+ # Include rake files
18
+ Dir.glob(File.join(File.dirname(__FILE__), "tasks/**/*.rake")) do |task|
19
+ import task
20
+ end
@@ -0,0 +1,53 @@
1
+ require "nokogiri"
2
+ require 'fileutils'
3
+
4
+ require "motion_migrate/motion_generate/entity"
5
+ require "motion_migrate/motion_generate/parser"
6
+ require "motion_migrate/motion_generate/property"
7
+ require "motion_migrate/motion_generate/relationship"
8
+ require "motion_migrate/generate"
9
+ require "motion_migrate/motion_generate/io"
10
+
11
+ namespace :db do
12
+ desc "Generate a version of the current database model as described in the models."
13
+ task :migrate do
14
+ schema_xml = MotionMigrate::Generate.build
15
+ MotionMigrate::IO.write(schema_xml)
16
+ end
17
+
18
+ desc "Go back to the previous version of the database model."
19
+ task :rollback do
20
+ if version = MotionMigrate::IO.current_schema_version
21
+ if version == 1
22
+ puts "! Can't rollback schema when version is 1."
23
+ else
24
+ schema = MotionMigrate::IO.current_schema
25
+ MotionMigrate::IO.write_current_schema(version - 1)
26
+ FileUtils.rm_rf(schema)
27
+ puts "# Data model rolled back to version #{version - 1}."
28
+ end
29
+ else
30
+ puts "! No schema found in this project."
31
+ end
32
+ end
33
+
34
+ namespace :schema do
35
+ desc "Dump the current version of the database model scheme."
36
+ task :dump do
37
+ if current_schema = MotionMigrate::IO.current_schema
38
+ puts File.open(File.join(current_schema, "contents")).read
39
+ else
40
+ puts "! No schema found in this project."
41
+ end
42
+ end
43
+
44
+ desc "Show the current version of the database model scheme."
45
+ task :version do
46
+ if version = MotionMigrate::IO.current_schema_version
47
+ puts "# Data model is currently at version #{version}."
48
+ else
49
+ puts "! No schema found in this project."
50
+ end
51
+ end
52
+ end
53
+ end
data/lib/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module MotionMigrate
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "motion_migrate"
8
+ gem.version = MotionMigrate::VERSION
9
+ gem.authors = ["Jelle Vandebeeck"]
10
+ gem.email = ["jelle@fousa.be"]
11
+ gem.description = %q{Generate the Core Data model from your RubyMotion code.}
12
+ gem.summary = %q{Generate the Core Data model from your RubyMotion code. Never open XCode again!}
13
+ gem.homepage = "http://fousa.github.com/motion_migrate/"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
+ gem.require_paths = ["lib"]
18
+
19
+ gem.add_development_dependency 'rake'
20
+ gem.add_dependency 'nokogiri'
21
+ end
@@ -0,0 +1,113 @@
1
+ require 'date'
2
+
3
+ require "motion_migrate/motion_generate/entity"
4
+ require "motion_migrate/motion_generate/parser"
5
+ require "motion_migrate/motion_generate/property"
6
+ require "motion_migrate/motion_generate/relationship"
7
+ require "motion_migrate/generate"
8
+
9
+ module MotionMigrate
10
+ describe "property definitions in the model" do
11
+ before do
12
+ @allowed_options = {
13
+ required: true,
14
+ transient: true,
15
+ indexed: true,
16
+ spotlight: true,
17
+ truth_file: true
18
+ }
19
+ @filled_allowed_attributes = {
20
+ optional: "NO",
21
+ transient: "YES",
22
+ indexed: "YES",
23
+ spotlightIndexingEnabled: "YES",
24
+ storedInTruthFile: "YES",
25
+ syncable: "YES"
26
+ }
27
+ end
28
+
29
+ %w(string integer_16 integer_32 integer_64 decimal double float date binary_data boolean).each do |type|
30
+ it "should be able to define #{type}" do
31
+ type_string = type.split("_").each{|word| word.capitalize! }.join(" ")
32
+ type_string = "Binary" if type == "binary_data"
33
+ MotionMigrate::Model.property(:field, type).should == { :name => :field,
34
+ :attributeType => type_string,
35
+ :optional=>"YES",
36
+ :syncable => "YES" }
37
+ end
38
+ end
39
+
40
+ it "should error on undiffened types" do
41
+ lambda { MotionMigrate::Model.property(:field, :unknown) }.should raise_error
42
+ end
43
+
44
+ %w(string integer_16 integer_32 integer_64 decimal double float date binary_data boolean).each do |type|
45
+ it "should error on undiffened options for #{type}" do
46
+ lambda { MotionMigrate::Model.property(:field, type, :unknown => true) }.should raise_error
47
+ end
48
+ end
49
+
50
+ it "should return the correct options for string" do
51
+ options = @allowed_options.merge({
52
+ min: "1",
53
+ max: "10",
54
+ default: "motion",
55
+ regex: "/we/"
56
+ })
57
+ MotionMigrate::Model.property(:field, :string, options).should == @filled_allowed_attributes.merge({ :name => :field,
58
+ :attributeType => "String",
59
+ :minValueString => "1",
60
+ :maxValueString => "10",
61
+ :defaultValueString => "motion",
62
+ :regularExpressionString => "/we/" })
63
+ end
64
+
65
+ %w(integer_16 integer_32 integer_64 decimal double float).each do |type|
66
+ it "should return the correct options for #{type}" do
67
+ options = @allowed_options.merge({
68
+ min: "1",
69
+ max: "10",
70
+ default: "5"
71
+ })
72
+ MotionMigrate::Model.property(:field, type, options).should == @filled_allowed_attributes.merge({ :name => :field,
73
+ :attributeType => type.split("_").each{|word| word.capitalize! }.join(" "),
74
+ :minValueString => "1",
75
+ :maxValueString => "10",
76
+ :defaultValueString => "5" })
77
+ end
78
+ end
79
+
80
+ it "should return the correct options for boolean" do
81
+ options = @allowed_options.merge({
82
+ default: true
83
+ })
84
+ MotionMigrate::Model.property(:field, :boolean, options).should == @filled_allowed_attributes.merge({ :name => :field,
85
+ :attributeType => "Boolean",
86
+ :defaultValueString => "YES" })
87
+ end
88
+
89
+ it "should return the correct options for binary_data" do
90
+ options = @allowed_options.merge({
91
+ external_storage: true
92
+ })
93
+ MotionMigrate::Model.property(:field, :binary_data, options).should == @filled_allowed_attributes.merge({ :name => :field,
94
+ :attributeType => "Binary",
95
+ :allowsExternalBinaryDataStorage => "YES" })
96
+ end
97
+
98
+ it "should return the correct options for date" do
99
+ options = @allowed_options.merge({
100
+ min: DateTime.new(2013, 1, 1, 12, 0, 0),
101
+ max: DateTime.new(2013, 1, 31, 12, 0, 0),
102
+ default: DateTime.new(2013, 1, 5, 12, 0, 0)
103
+ })
104
+ MotionMigrate::Model.property(:field, :date, options).should == @filled_allowed_attributes.merge({ :name => :field,
105
+ :attributeType => "Date",
106
+ :minDateTimeInterval => "378738000",
107
+ :maxDateTimeInterval => "381330000",
108
+ :defaultDateTimeInterval => "379083600" })
109
+ end
110
+ end
111
+ end
112
+
113
+ # Test all types + properties
@@ -0,0 +1,54 @@
1
+ require "motion_migrate/motion_generate/entity"
2
+ require "motion_migrate/motion_generate/parser"
3
+ require "motion_migrate/motion_generate/property"
4
+ require "motion_migrate/motion_generate/relationship"
5
+ require "motion_migrate/generate"
6
+
7
+ module MotionMigrate
8
+ describe "relationship definitions in the model" do
9
+ before do
10
+ @allowed_options = {
11
+ required: true,
12
+ transient: true,
13
+ class_name: "Test",
14
+ inverse_of: "test",
15
+ deletion_rule: :cascade,
16
+ spotlight: true,
17
+ truth_file: true
18
+ }
19
+ @filled_allowed_attributes = {
20
+ optional: "NO",
21
+ transient: "YES",
22
+ inverseEntity: "Test",
23
+ destinationEntity: "Test",
24
+ inverseName: "test",
25
+ deletionRule: "Cascade",
26
+ spotlightIndexingEnabled: "YES",
27
+ storedInTruthFile: "YES",
28
+ syncable: "YES"
29
+ }
30
+ end
31
+
32
+ %w(belongs_to has_many).each do |type|
33
+ it "should error on incomplete options for #{type}" do
34
+ lambda { MotionMigrate::Model.send(type, :field) }.should raise_error
35
+ end
36
+
37
+ it "should error on undiffened options for #{type}" do
38
+ lambda { MotionMigrate::Model.send(type, :field, :class_name => "Test", :inverse_of => "test", :unknown => true) }.should raise_error
39
+ end
40
+
41
+ it "should error on undiffened deletion rules for #{type}" do
42
+ lambda { MotionMigrate::Model.send(type, :field, :class_name => "Test", :inverse_of => "test", :deletion_rule => :unknown) }.should raise_error
43
+ end
44
+ end
45
+
46
+ it "should return the correct options for belongs_to" do
47
+ MotionMigrate::Model.belongs_to(:field, @allowed_options).should == @filled_allowed_attributes.merge({ :toMany => "NO", :name => :field, :minCount => 1, :maxCount => 1 })
48
+ end
49
+
50
+ it "should return the correct options for has_many" do
51
+ MotionMigrate::Model.has_many(:field, @allowed_options).should == @filled_allowed_attributes.merge({ :toMany => "YES", :name => :field })
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,18 @@
1
+ .repl_history
2
+ build
3
+ tags
4
+ app/pixate_code.rb
5
+ resources/*.nib
6
+ resources/*.momd
7
+ resources/*.storyboardc
8
+ .DS_Store
9
+ nbproject
10
+ .redcar
11
+ #*#
12
+ *~
13
+ *.sw[po]
14
+ .eprj
15
+ .sass-cache
16
+ .idea
17
+
18
+ /vendor/Pods/
@@ -0,0 +1,6 @@
1
+ source :rubygems
2
+
3
+ gem "rake", "0.9.4"
4
+ gem "motion_migrate", :path => "../../."
5
+ gem 'cocoapods'
6
+ gem "motion-cocoapods", "1.2.1"
@@ -0,0 +1,14 @@
1
+ # -*- coding: utf-8 -*-
2
+ $:.unshift("/Library/RubyMotion/lib")
3
+ require 'motion/project'
4
+
5
+ require 'bundler'
6
+ Bundler.require
7
+
8
+ Motion::Project::App.setup do |app|
9
+ app.name = 'Motion Migrate Spec Project'
10
+
11
+ app.pods do
12
+ pod 'MagicalRecord', '2.1'
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ class AppDelegate
2
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
3
+ MagicalRecord.setupCoreDataStackWithAutoMigratingSqliteStoreNamed("MotionMigrateSpec.sqlite")
4
+
5
+ true
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class Pilot < MotionMigrate::Model
2
+ property :name, :string
3
+
4
+ belongs_to :plane, :class_name => "Plane", :inverse_of => :pilot
5
+ has_many :owned_planes, :class_name => "Plane", :inverse_of => :owner
6
+ has_many :flown_planes, :class_name => "Plane", :inverse_of => :flown_by_pilots
7
+ end
@@ -0,0 +1,11 @@
1
+ class Plane < MotionMigrate::Model
2
+ property :name, :string
3
+ property :multi_engine, :boolean, :default => false
4
+ property :first_flight_at, :date
5
+ property :flight_info, :binary_data
6
+
7
+ belongs_to :pilot, :class_name => "Pilot", :inverse_of => :plane
8
+ belongs_to :owner, :class_name => "Pilot", :inverse_of => :owned_planes
9
+
10
+ has_many :flown_by_pilots, :class_name => "Pilot", :inverse_of => :flown_planes
11
+ end
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>_XCCurrentVersionName</key>
6
+ <string>schema.10.xcdatamodel</string>
7
+ </dict>
8
+ </plist>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1811" systemVersion="11D50" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
3
+ <entity name="Plane" representedClassName="Plane" syncable="YES">
4
+ <attribute name="name" attributeType="String" optional="YES" syncable="YES"/>
5
+ </entity>
6
+ </model>
@@ -0,0 +1,18 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1811" systemVersion="11D50" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
3
+ <entity name="Pilot" representedClassName="Pilot" syncable="YES">
4
+ <attribute name="name" attributeType="String" optional="YES" syncable="YES"/>
5
+ <relationship name="plane" optional="YES" deletionRule="No Action" syncable="YES" minCount="1" maxCount="1" inverseEntity="Plane" destinationEntity="Plane" inverseName="pilot" toMany="NO"/>
6
+ <relationship name="owned_planes" optional="YES" deletionRule="No Action" syncable="YES" inverseEntity="Plane" destinationEntity="Plane" inverseName="owner" toMany="YES"/>
7
+ <relationship name="flown_planes" optional="YES" deletionRule="No Action" syncable="YES" inverseEntity="Plane" destinationEntity="Plane" inverseName="flown_by_pilots" toMany="YES"/>
8
+ </entity>
9
+ <entity name="Plane" representedClassName="Plane" syncable="YES">
10
+ <attribute name="name" attributeType="String" optional="YES" syncable="YES"/>
11
+ <attribute name="multi_engine" attributeType="Boolean" optional="YES" syncable="YES" defaultValueString="NO"/>
12
+ <attribute name="first_flight_at" attributeType="Date" optional="YES" syncable="YES"/>
13
+ <attribute name="flight_info" attributeType="Binary" optional="YES" syncable="YES"/>
14
+ <relationship name="pilot" optional="YES" deletionRule="No Action" syncable="YES" minCount="1" maxCount="1" inverseEntity="Pilot" destinationEntity="Pilot" inverseName="plane" toMany="NO"/>
15
+ <relationship name="owner" optional="YES" deletionRule="No Action" syncable="YES" minCount="1" maxCount="1" inverseEntity="Pilot" destinationEntity="Pilot" inverseName="owned_planes" toMany="NO"/>
16
+ <relationship name="flown_by_pilots" optional="YES" deletionRule="No Action" syncable="YES" inverseEntity="Pilot" destinationEntity="Pilot" inverseName="flown_planes" toMany="YES"/>
17
+ </entity>
18
+ </model>
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1811" systemVersion="11D50" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
3
+ <entity name="Plane" representedClassName="Plane" syncable="YES">
4
+ <attribute name="name" attributeType="String" optional="YES" syncable="YES"/>
5
+ <attribute name="multi_engine" attributeType="Boolean" optional="YES" syncable="YES"/>
6
+ </entity>
7
+ </model>
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1811" systemVersion="11D50" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
3
+ <entity name="Plane" representedClassName="Plane" syncable="YES">
4
+ <attribute name="name" attributeType="String" optional="YES" syncable="YES"/>
5
+ <attribute name="multi_engine" attributeType="Boolean" optional="YES" syncable="YES" defaultValueString="NO"/>
6
+ </entity>
7
+ </model>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1811" systemVersion="11D50" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
3
+ <entity name="Plane" representedClassName="Plane" syncable="YES">
4
+ <attribute name="name" attributeType="String" optional="YES" syncable="YES"/>
5
+ <attribute name="multi_engine" attributeType="Boolean" optional="YES" syncable="YES" defaultValueString="NO"/>
6
+ <attribute name="first_flight_at" attributeType="Date" optional="YES" syncable="YES"/>
7
+ </entity>
8
+ </model>
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1811" systemVersion="11D50" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
3
+ <entity name="Plane" representedClassName="Plane" syncable="YES">
4
+ <attribute name="name" attributeType="String" optional="YES" syncable="YES"/>
5
+ <attribute name="multi_engine" attributeType="Boolean" optional="YES" syncable="YES" defaultValueString="NO"/>
6
+ <attribute name="first_flight_at" attributeType="Date" optional="YES" syncable="YES"/>
7
+ <attribute name="flight_info" attributeType="Binary" optional="YES" syncable="YES"/>
8
+ </entity>
9
+ </model>
@@ -0,0 +1,13 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1811" systemVersion="11D50" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
3
+ <entity name="Plane" representedClassName="Plane" syncable="YES">
4
+ <attribute name="name" attributeType="String" optional="YES" syncable="YES"/>
5
+ <attribute name="multi_engine" attributeType="Boolean" optional="YES" syncable="YES" defaultValueString="NO"/>
6
+ <attribute name="first_flight_at" attributeType="Date" optional="YES" syncable="YES"/>
7
+ <attribute name="flight_info" attributeType="Binary" optional="YES" syncable="YES"/>
8
+ <relationship name="pilot" optional="YES" deletionRule="No Action" syncable="YES" minCount="1" maxCount="1" inverseEntity="Pilot" destinationEntity="Pilot" inverseName="plane" toMany="NO"/>
9
+ </entity>
10
+ <entity name="Pilot" representedClassName="Pilot" syncable="YES">
11
+ <relationship name="plane" optional="YES" deletionRule="No Action" syncable="YES" minCount="1" maxCount="1" inverseEntity="Plane" destinationEntity="Plane" inverseName="pilot" toMany="NO"/>
12
+ </entity>
13
+ </model>
@@ -0,0 +1,14 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1811" systemVersion="11D50" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
3
+ <entity name="Pilot" representedClassName="Pilot" syncable="YES">
4
+ <attribute name="name" attributeType="String" optional="YES" syncable="YES"/>
5
+ <relationship name="plane" optional="YES" deletionRule="No Action" syncable="YES" minCount="1" maxCount="1" inverseEntity="Plane" destinationEntity="Plane" inverseName="pilot" toMany="NO"/>
6
+ </entity>
7
+ <entity name="Plane" representedClassName="Plane" syncable="YES">
8
+ <attribute name="name" attributeType="String" optional="YES" syncable="YES"/>
9
+ <attribute name="multi_engine" attributeType="Boolean" optional="YES" syncable="YES" defaultValueString="NO"/>
10
+ <attribute name="first_flight_at" attributeType="Date" optional="YES" syncable="YES"/>
11
+ <attribute name="flight_info" attributeType="Binary" optional="YES" syncable="YES"/>
12
+ <relationship name="pilot" optional="YES" deletionRule="No Action" syncable="YES" minCount="1" maxCount="1" inverseEntity="Pilot" destinationEntity="Pilot" inverseName="plane" toMany="NO"/>
13
+ </entity>
14
+ </model>