ruby-xcdm 0.0.1
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.
- data/.gitignore +25 -0
- data/.rbenv-version +1 -0
- data/.rvmrc +48 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +24 -0
- data/LICENSE.txt +22 -0
- data/README.md +64 -0
- data/Rakefile +12 -0
- data/bin/xcdm +8 -0
- data/lib/ruby-xcdm.rb +32 -0
- data/lib/xcdm.rb +3 -0
- data/lib/xcdm/entity.rb +140 -0
- data/lib/xcdm/schema.rb +108 -0
- data/lib/xcdm/version.rb +3 -0
- data/ruby-xcdm.gemspec +28 -0
- data/test/entity_test.rb +124 -0
- data/test/fixtures/001_baseline.rb +41 -0
- data/test/fixtures/Article.xcdatamodeld/Article.xcdatamodel/contents +16 -0
- data/test/schema_test.rb +50 -0
- data/test/test_schema.rb +19 -0
- metadata +168 -0
data/.gitignore
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
coverage
|
6
|
+
InstalledFiles
|
7
|
+
lib/bundler/man
|
8
|
+
pkg
|
9
|
+
rdoc
|
10
|
+
spec/reports
|
11
|
+
test/tmp
|
12
|
+
test/version_tmp
|
13
|
+
tmp
|
14
|
+
|
15
|
+
doc/
|
16
|
+
.repl_history
|
17
|
+
build
|
18
|
+
tags
|
19
|
+
.DS_Store
|
20
|
+
nbproject
|
21
|
+
.redcar
|
22
|
+
#*#
|
23
|
+
*~
|
24
|
+
*.sw[po]
|
25
|
+
local_notes.rb
|
data/.rbenv-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.9.3-p286
|
data/.rvmrc
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# This is an RVM Project .rvmrc file, used to automatically load the ruby
|
4
|
+
# development environment upon cd'ing into the directory
|
5
|
+
|
6
|
+
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
|
7
|
+
# Only full ruby name is supported here, for short names use:
|
8
|
+
# echo "rvm use 1.9.3" > .rvmrc
|
9
|
+
environment_id="ruby-1.9.3-p392@ir_temple"
|
10
|
+
|
11
|
+
# Uncomment the following lines if you want to verify rvm version per project
|
12
|
+
# rvmrc_rvm_version="1.18.14 (stable)" # 1.10.1 seams as a safe start
|
13
|
+
# eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
|
14
|
+
# echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
|
15
|
+
# return 1
|
16
|
+
# }
|
17
|
+
|
18
|
+
# First we attempt to load the desired environment directly from the environment
|
19
|
+
# file. This is very fast and efficient compared to running through the entire
|
20
|
+
# CLI and selector. If you want feedback on which environment was used then
|
21
|
+
# insert the word 'use' after --create as this triggers verbose mode.
|
22
|
+
if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
|
23
|
+
&& -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
|
24
|
+
then
|
25
|
+
\. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
|
26
|
+
[[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] &&
|
27
|
+
\. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true
|
28
|
+
else
|
29
|
+
# If the environment file has not yet been created, use the RVM CLI to select.
|
30
|
+
rvm --create "$environment_id" || {
|
31
|
+
echo "Failed to create RVM environment '${environment_id}'."
|
32
|
+
return 1
|
33
|
+
}
|
34
|
+
fi
|
35
|
+
|
36
|
+
# If you use bundler, this might be useful to you:
|
37
|
+
# if [[ -s Gemfile ]] && {
|
38
|
+
# ! builtin command -v bundle >/dev/null ||
|
39
|
+
# builtin command -v bundle | GREP_OPTIONS= \grep $rvm_path/bin/bundle >/dev/null
|
40
|
+
# }
|
41
|
+
# then
|
42
|
+
# printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
|
43
|
+
# gem install bundler
|
44
|
+
# fi
|
45
|
+
# if [[ -s Gemfile ]] && builtin command -v bundle >/dev/null
|
46
|
+
# then
|
47
|
+
# bundle install | GREP_OPTIONS= \grep -vE '^Using|Your bundle is complete'
|
48
|
+
# fi
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (3.2.13)
|
5
|
+
i18n (= 0.6.1)
|
6
|
+
multi_json (~> 1.0)
|
7
|
+
ansi (1.4.3)
|
8
|
+
builder (3.2.0)
|
9
|
+
i18n (0.6.1)
|
10
|
+
multi_json (1.7.3)
|
11
|
+
plist (3.1.0)
|
12
|
+
rake (10.0.4)
|
13
|
+
turn (0.9.6)
|
14
|
+
ansi
|
15
|
+
|
16
|
+
PLATFORMS
|
17
|
+
ruby
|
18
|
+
|
19
|
+
DEPENDENCIES
|
20
|
+
activesupport
|
21
|
+
builder
|
22
|
+
plist
|
23
|
+
rake
|
24
|
+
turn
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 InfiniteRed LLC
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# ruby-xcdm
|
2
|
+
|
3
|
+
This is a tool for generating the same xcdatamodeld files that XCode does when designing a datamodel for Core Data.
|
4
|
+
It is written in pure ruby, but it will be of particular interest to RubyMotion developers.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'ruby-xcdm'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install ruby-xcdm
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
1. Make a directory to hold your schemas (a.k.a. data model in XCode parlance)
|
23
|
+
2. Create one schema version per file within the directory
|
24
|
+
3. Run the command to generate a datamodel:
|
25
|
+
|
26
|
+
xcdm MyApplicationName schemadir datamodeldestdir
|
27
|
+
|
28
|
+
|
29
|
+
## Schema File Format
|
30
|
+
|
31
|
+
Here's a sample schema file:
|
32
|
+
|
33
|
+
schema "0.0.1" do
|
34
|
+
|
35
|
+
entity "Article" do
|
36
|
+
|
37
|
+
string :body, optional: false
|
38
|
+
integer32 :length
|
39
|
+
boolean :published, default: false
|
40
|
+
datetime :publishedAt, default: false
|
41
|
+
string :title, optional: false
|
42
|
+
|
43
|
+
has_one :author
|
44
|
+
end
|
45
|
+
|
46
|
+
entity "Author" do
|
47
|
+
float :fee
|
48
|
+
string :name, optional: false
|
49
|
+
has_many :articles
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
It's pretty self-explanatory. All the built-in data types are supported, and inverse relationships are
|
55
|
+
generated automatically. If you need to set some of the more esoteric options on properties or
|
56
|
+
relationships, you can include the raw parameters, like renamingIdentifier or defaultValueString.
|
57
|
+
|
58
|
+
## Contributing
|
59
|
+
|
60
|
+
1. Fork it
|
61
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
62
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
63
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
64
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/xcdm
ADDED
data/lib/ruby-xcdm.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
p "Basfasdf"
|
3
|
+
|
4
|
+
if const_defined?(:Motion) && Motion.const_defined?(:Project)
|
5
|
+
|
6
|
+
namespace :schema do
|
7
|
+
|
8
|
+
desc "Clear the datamodel outputs"
|
9
|
+
task :clean do
|
10
|
+
files = Dir.glob(File.join(App.config.project_dir, 'resources', App.config.name) + ".{momd,xcdatamodeld}")
|
11
|
+
files.each do |f|
|
12
|
+
rm_rf f
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Generate the xcdatamodel file"
|
17
|
+
task :build => :clean do
|
18
|
+
Dir.chdir App.config.project_dir
|
19
|
+
system("xcdm", App.config.name, "schemas", "resources")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
task :"build:simulator" => :"schema:build"
|
24
|
+
task :"build:device" => :"schema:build"
|
25
|
+
|
26
|
+
|
27
|
+
else
|
28
|
+
|
29
|
+
require 'xcdm/schema'
|
30
|
+
require 'xcdm/entity'
|
31
|
+
|
32
|
+
end
|
data/lib/xcdm.rb
ADDED
data/lib/xcdm/entity.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
|
2
|
+
require 'active_support/all'
|
3
|
+
require 'builder'
|
4
|
+
|
5
|
+
module XCDM
|
6
|
+
class Entity
|
7
|
+
|
8
|
+
DEFAULT_PROPERTY_ATTRIBUTES = { optional: 'YES', syncable: 'YES' }
|
9
|
+
|
10
|
+
DEFAULT_RELATIONSHIP_ATTRIBUTES = { optional: 'YES', deletionRule: 'Nullify', syncable: 'YES' }
|
11
|
+
|
12
|
+
TYPE_MAPPING = {
|
13
|
+
integer16: 'Integer 16',
|
14
|
+
integer32: 'Integer 32',
|
15
|
+
integer64: 'Integer 64',
|
16
|
+
decimal: 'Decimal',
|
17
|
+
double: 'Double',
|
18
|
+
float: 'Float',
|
19
|
+
string: 'String',
|
20
|
+
boolean: 'Boolean',
|
21
|
+
datetime: 'Date',
|
22
|
+
binary: 'Binary Data',
|
23
|
+
transformable: 'Transformable'
|
24
|
+
}
|
25
|
+
|
26
|
+
|
27
|
+
attr_reader :name, :properties, :relationships
|
28
|
+
|
29
|
+
|
30
|
+
def initialize(name, options = {})
|
31
|
+
@name = name
|
32
|
+
@properties = []
|
33
|
+
@relationships = []
|
34
|
+
end
|
35
|
+
|
36
|
+
def raw_property(options)
|
37
|
+
@properties << DEFAULT_PROPERTY_ATTRIBUTES.merge(options)
|
38
|
+
end
|
39
|
+
|
40
|
+
def property(name, type, options = {})
|
41
|
+
property = {}
|
42
|
+
|
43
|
+
property[:attributeType] = self.class.convert_type(type)
|
44
|
+
property[:name] = name.to_s
|
45
|
+
|
46
|
+
if !options[:default].nil?
|
47
|
+
property[:defaultValueString] = normalize_value(options.delete(:default))
|
48
|
+
elsif [:integer16, :integer32, :integer64].include?(type)
|
49
|
+
property[:defaultValueString] = "0"
|
50
|
+
elsif [:float, :double, :decimal].include?(type)
|
51
|
+
property[:defaultValueString] = "0.0"
|
52
|
+
end
|
53
|
+
|
54
|
+
normalize_values(options, property)
|
55
|
+
raw_property(property)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Make shortcut property methods for each data type
|
59
|
+
TYPE_MAPPING.keys.each do |type|
|
60
|
+
define_method(type) do |name, options = {}|
|
61
|
+
property(name, type, options)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
def raw_relationship(options)
|
67
|
+
@relationships << DEFAULT_RELATIONSHIP_ATTRIBUTES.merge(options)
|
68
|
+
end
|
69
|
+
|
70
|
+
def relationship(name, options = {})
|
71
|
+
relationship = {}
|
72
|
+
relationship[:name] = name.to_s
|
73
|
+
|
74
|
+
if options[:inverse]
|
75
|
+
entity, relation = options.delete(:inverse).split('.')
|
76
|
+
relationship[:destinationEntity] = relationship[:inverseEntity] = entity
|
77
|
+
relationship[:inverseName] = relation
|
78
|
+
options.delete(:plural_inverse)
|
79
|
+
else
|
80
|
+
relationship[:destinationEntity] = relationship[:inverseEntity] = name.to_s.classify
|
81
|
+
if options.delete(:plural_inverse)
|
82
|
+
relationship[:inverseName] = self.name.underscore.pluralize
|
83
|
+
else
|
84
|
+
relationship[:inverseName] = self.name.underscore
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
normalize_values(options, relationship)
|
89
|
+
|
90
|
+
raw_relationship(relationship)
|
91
|
+
end
|
92
|
+
|
93
|
+
def belongs_to(name, options = {})
|
94
|
+
relationship(name, {maxCount: 1, minCount: 1, plural_inverse: true}.merge(options))
|
95
|
+
end
|
96
|
+
|
97
|
+
def has_one(name, options = {})
|
98
|
+
relationship(name, {maxCount: 1, minCount: 1}.merge(options))
|
99
|
+
end
|
100
|
+
|
101
|
+
def has_many(name, options = {})
|
102
|
+
relationship(name, {maxCount: -1, minCount: 1}.merge(options))
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def self.convert_type(type)
|
107
|
+
TYPE_MAPPING[type]
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_xml(builder = nil)
|
111
|
+
builder ||= Builder::XmlMarkup.new(:indent => 2)
|
112
|
+
builder.entity(name: name, syncable: 'YES') do |xml|
|
113
|
+
properties.each do |property|
|
114
|
+
xml.attribute(property)
|
115
|
+
end
|
116
|
+
|
117
|
+
relationships.each do |relationship|
|
118
|
+
xml.relationship(relationship)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def normalize_values(source, destination)
|
126
|
+
source.each do |key, value|
|
127
|
+
destination[key] = normalize_value(value)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def normalize_value(value)
|
132
|
+
case value
|
133
|
+
when false; 'NO'
|
134
|
+
when true; 'YES'
|
135
|
+
else value.to_s
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
end
|
data/lib/xcdm/schema.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
|
2
|
+
require 'xcdm/entity'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'plist'
|
5
|
+
|
6
|
+
module XCDM
|
7
|
+
class Schema
|
8
|
+
|
9
|
+
attr_reader :version, :entities
|
10
|
+
|
11
|
+
def initialize(version)
|
12
|
+
@version = version
|
13
|
+
@entities = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def entity(name, &block)
|
17
|
+
@entities << Entity.new(name).tap { |e| e.instance_eval(&block) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_xml(builder = nil)
|
21
|
+
|
22
|
+
builder ||= Builder::XmlMarkup.new(:indent => 2)
|
23
|
+
|
24
|
+
builder.instruct! :xml, :standalone => 'yes'
|
25
|
+
|
26
|
+
attrs = {
|
27
|
+
name: "",
|
28
|
+
userDefinedModelVersionIdentifier: version,
|
29
|
+
type: "com.apple.IDECoreDataModeler.DataModel",
|
30
|
+
documentVersion: "1.0",
|
31
|
+
lastSavedToolsVersion: "2061",
|
32
|
+
systemVersion: "12D78",
|
33
|
+
minimumToolsVersion: "Xcode 4.3",
|
34
|
+
macOSVersion: "Automatic",
|
35
|
+
iOSVersion: "Automatic"
|
36
|
+
}
|
37
|
+
|
38
|
+
builder.model(attrs) do |builder|
|
39
|
+
entities.each do |entity|
|
40
|
+
entity.to_xml(builder)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Loader
|
46
|
+
|
47
|
+
attr_reader :schemas
|
48
|
+
|
49
|
+
def initialize
|
50
|
+
@schemas = []
|
51
|
+
end
|
52
|
+
|
53
|
+
def schema(version, &block)
|
54
|
+
@found_schema = Schema.new(version).tap { |s| s.instance_eval(&block) }
|
55
|
+
@schemas << @found_schema
|
56
|
+
end
|
57
|
+
|
58
|
+
def load_file(file)
|
59
|
+
File.open(file) do |ff|
|
60
|
+
instance_eval(ff.read, file)
|
61
|
+
end
|
62
|
+
@found_schema
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
class Runner
|
68
|
+
def initialize(name, inpath, outpath)
|
69
|
+
@inpath = inpath
|
70
|
+
@name = name
|
71
|
+
@container_path = File.join(outpath, "#{name}.xcdatamodeld")
|
72
|
+
@loader = Loader.new
|
73
|
+
end
|
74
|
+
|
75
|
+
def datamodel_file(version)
|
76
|
+
dir = File.join(@container_path, "#{version}.xcdatamodel")
|
77
|
+
FileUtils.mkdir_p(dir)
|
78
|
+
File.join(dir, 'contents')
|
79
|
+
end
|
80
|
+
|
81
|
+
def load_all
|
82
|
+
Dir["#{@inpath}/*.rb"].each do |file|
|
83
|
+
if File.file?(file)
|
84
|
+
puts "loading #{file}..."
|
85
|
+
@loader.load_file(file)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def write_all
|
91
|
+
@loader.schemas.each do |schema|
|
92
|
+
filename = datamodel_file(schema.version)
|
93
|
+
puts "writing #{filename}"
|
94
|
+
File.open(filename, "w+") do |f|
|
95
|
+
f.write(schema.to_xml)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
max = @loader.schemas.map(&:version).max
|
100
|
+
File.open(File.join(@container_path, ".xccurrentversion"), "w+") do |f|
|
101
|
+
f.write({ "_XCCurrentVersionName" => "#{max}.xcdatamodel" }.to_plist)
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/lib/xcdm/version.rb
ADDED
data/ruby-xcdm.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'xcdm/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ruby-xcdm"
|
8
|
+
spec.version = XCDM::VERSION
|
9
|
+
spec.authors = ["Ken Miller"]
|
10
|
+
spec.email = ["ken@infinitered.com"]
|
11
|
+
spec.description = %q{Ruby DSL for creating Core Data Data Model files without XCode}
|
12
|
+
spec.summary = %q{Ruby XCDM}
|
13
|
+
spec.homepage = "https://github.com/infinitered/ruby-xcdm"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "builder", "~> 3.2"
|
22
|
+
spec.add_dependency "activesupport", "~> 3.2"
|
23
|
+
spec.add_dependency "plist", "~> 3.1"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "turn"
|
28
|
+
end
|
data/test/entity_test.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
require 'xcdm/entity'
|
4
|
+
require 'turn'
|
5
|
+
require 'rexml/document'
|
6
|
+
|
7
|
+
module XCDM
|
8
|
+
class EntityTest < Test::Unit::TestCase
|
9
|
+
|
10
|
+
def e
|
11
|
+
@e ||= Entity.new("Article")
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_initialize
|
15
|
+
assert_equal "Article", e.name
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_raw_property
|
19
|
+
opts = { attributeType: "Integer 32", name: 'foobar' }
|
20
|
+
e.raw_property(opts)
|
21
|
+
assert_equal [opts.merge(optional: 'YES', syncable: 'YES')], e.properties
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_property_integer32
|
25
|
+
e.property 'foobar', :integer32, optional: false
|
26
|
+
assert_equal [{ optional: 'NO', syncable: 'YES', attributeType: 'Integer 32', name: 'foobar', defaultValueString: "0" }], e.properties
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_property_datetime
|
30
|
+
e.property 'fazbit', :datetime, optional: false
|
31
|
+
assert_equal [{ optional: 'NO', syncable: 'YES', attributeType: 'Date', name: 'fazbit' }], e.properties
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_property_short_form_string
|
35
|
+
e.string 'frobnoz', optional: false
|
36
|
+
assert_equal [{ optional: 'NO', syncable: 'YES', attributeType: 'String', name: 'frobnoz' }], e.properties
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_property_default
|
40
|
+
e.integer32 'count', default: 1
|
41
|
+
assert_equal [{ optional: 'YES', syncable: 'YES', attributeType: 'Integer 32', name: 'count', defaultValueString: '1' }], e.properties
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_convert_type
|
45
|
+
assert_equal 'Integer 16', Entity.convert_type(:integer16)
|
46
|
+
assert_equal 'Integer 32', Entity.convert_type(:integer32)
|
47
|
+
assert_equal 'Integer 64', Entity.convert_type(:integer64)
|
48
|
+
assert_equal 'Decimal', Entity.convert_type(:decimal)
|
49
|
+
assert_equal 'Double', Entity.convert_type(:double)
|
50
|
+
assert_equal 'Float', Entity.convert_type(:float)
|
51
|
+
assert_equal 'String', Entity.convert_type(:string)
|
52
|
+
assert_equal 'Boolean', Entity.convert_type(:boolean)
|
53
|
+
assert_equal 'Date', Entity.convert_type(:datetime)
|
54
|
+
assert_equal 'Binary Data', Entity.convert_type(:binary)
|
55
|
+
assert_equal 'Transformable', Entity.convert_type(:transformable)
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_raw_relationship
|
59
|
+
opts = { name: "author", minCount: "1", maxCount: "1", destinationEntity: "Author", inverseName: "articles", inverseEntity: "Author" }
|
60
|
+
e.raw_relationship(opts)
|
61
|
+
assert_equal [opts.merge(optional: "YES", deletionRule: "Nullify", syncable: "YES")], e.relationships
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_relationship
|
65
|
+
e.relationship('author', maxCount: 1, minCount: 1)
|
66
|
+
assert_equal [{ optional: "YES", deletionRule: "Nullify", syncable: "YES",
|
67
|
+
name: "author", minCount: "1", maxCount: "1", destinationEntity:
|
68
|
+
"Author", inverseName: "article", inverseEntity: "Author" }], e.relationships
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_has_one
|
72
|
+
e.has_one 'author'
|
73
|
+
assert_equal [{ optional: "YES", deletionRule: "Nullify", syncable: "YES",
|
74
|
+
name: "author", minCount: "1", maxCount: "1", destinationEntity:
|
75
|
+
"Author", inverseName: "article", inverseEntity: "Author" }], e.relationships
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_belongs_to
|
79
|
+
e.belongs_to 'author'
|
80
|
+
assert_equal [{ optional: "YES", deletionRule: "Nullify", syncable: "YES",
|
81
|
+
name: "author", minCount: "1", maxCount: "1", destinationEntity:
|
82
|
+
"Author", inverseName: "articles", inverseEntity: "Author" }], e.relationships
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_has_many
|
86
|
+
e.has_many 'authors'
|
87
|
+
assert_equal [{ optional: "YES", deletionRule: "Nullify", syncable: "YES",
|
88
|
+
name: "authors", minCount: "1", maxCount: "-1", destinationEntity:
|
89
|
+
"Author", inverseName: "article", inverseEntity: "Author" }], e.relationships
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_non_inferrable_relationship_with_inverse
|
93
|
+
e.belongs_to 'primary_author', inverse: 'Author.primary_articles'
|
94
|
+
assert_equal [{ optional: "YES", deletionRule: "Nullify", syncable: "YES",
|
95
|
+
name: "primary_author", minCount: "1", maxCount: "1", destinationEntity:
|
96
|
+
"Author", inverseName: "primary_articles", inverseEntity: "Author" }], e.relationships
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_to_xml
|
100
|
+
expected = REXML::Document.new %{
|
101
|
+
<entity name="Article" syncable="YES">
|
102
|
+
<attribute name="body" optional="NO" attributeType="String" syncable="YES"/>
|
103
|
+
<attribute name="length" optional="YES" attributeType="Integer 32" defaultValueString="0" syncable="YES"/>
|
104
|
+
<attribute name="published" optional="YES" attributeType="Boolean" defaultValueString="NO" syncable="YES"/>
|
105
|
+
<attribute name="publishedAt" optional="YES" attributeType="Date" defaultValueString="NO" syncable="YES"/>
|
106
|
+
<attribute name="title" optional="NO" attributeType="String" syncable="YES"/>
|
107
|
+
<relationship name="author" optional="YES" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="Author" inverseName="articles" inverseEntity="Author" syncable="YES"/>
|
108
|
+
</entity>
|
109
|
+
}
|
110
|
+
|
111
|
+
e.string :body, optional: false
|
112
|
+
e.integer32 :length
|
113
|
+
e.boolean :published, default: false
|
114
|
+
e.datetime :publishedAt, default: false
|
115
|
+
e.string :title, optional: false
|
116
|
+
|
117
|
+
e.belongs_to :author
|
118
|
+
|
119
|
+
assert_equal expected.to_s.strip, REXML::Document.new(e.to_xml).to_s.strip
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
schema "0.0.1" do
|
3
|
+
|
4
|
+
entity "Article" do
|
5
|
+
|
6
|
+
string :body, optional: false
|
7
|
+
integer32 :length
|
8
|
+
boolean :published, default: false
|
9
|
+
datetime :publishedAt, default: false
|
10
|
+
string :title, optional: false
|
11
|
+
|
12
|
+
belongs_to :author
|
13
|
+
end
|
14
|
+
|
15
|
+
entity "Author" do
|
16
|
+
float :fee
|
17
|
+
string :name, optional: false
|
18
|
+
has_many :articles
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
__END__
|
25
|
+
|
26
|
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
27
|
+
<model name="" userDefinedModelVersionIdentifier="0.0.1" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="2061" systemVersion="12D78" minimumToolsVersion="Xcode 4.3" macOSVersion="Automatic" iOSVersion="Automatic">
|
28
|
+
<entity name="Article" syncable="YES">
|
29
|
+
<attribute name="body" optional="YES" attributeType="String" syncable="YES"/>
|
30
|
+
<attribute name="length" optional="YES" attributeType="Integer 32" defaultValueString="0" syncable="YES"/>
|
31
|
+
<attribute name="published" optional="YES" attributeType="Boolean" syncable="YES"/>
|
32
|
+
<attribute name="publishedAt" optional="YES" attributeType="Date" syncable="YES"/>
|
33
|
+
<attribute name="title" optional="YES" attributeType="String" syncable="YES"/>
|
34
|
+
<relationship name="author" optional="YES" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="Author" inverseName="articles" inverseEntity="Article" syncable="YES"/>
|
35
|
+
</entity>
|
36
|
+
<entity name="Author" syncable="YES">
|
37
|
+
<attribute name="fee" optional="YES" attributeType="Float" defaultValueString="0.0" syncable="YES"/>
|
38
|
+
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
|
39
|
+
<relationship name="articles" optional="YES" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="Article" inverseName="author" inverseEntity="Author" syncable="YES"/>
|
40
|
+
</entity>
|
41
|
+
</model>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
2
|
+
<model name="" userDefinedModelVersionIdentifier="0.0.1" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="2061" systemVersion="12D78" minimumToolsVersion="Xcode 4.3" macOSVersion="Automatic" iOSVersion="Automatic">
|
3
|
+
<entity name="Article" syncable="YES">
|
4
|
+
<attribute name="body" optional="NO" attributeType="String" syncable="YES"/>
|
5
|
+
<attribute name="length" optional="YES" attributeType="Integer 32" defaultValueString="0" syncable="YES"/>
|
6
|
+
<attribute name="published" optional="YES" attributeType="Boolean" defaultValueString="NO" syncable="YES"/>
|
7
|
+
<attribute name="publishedAt" optional="YES" attributeType="Date" defaultValueString="NO" syncable="YES"/>
|
8
|
+
<attribute name="title" optional="NO" attributeType="String" syncable="YES"/>
|
9
|
+
<relationship name="author" optional="YES" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="Author" inverseName="articles" inverseEntity="Author" syncable="YES"/>
|
10
|
+
</entity>
|
11
|
+
<entity name="Author" syncable="YES">
|
12
|
+
<attribute name="fee" optional="YES" attributeType="Float" defaultValueString="0.0" syncable="YES"/>
|
13
|
+
<attribute name="name" optional="NO" attributeType="String" syncable="YES"/>
|
14
|
+
<relationship name="articles" optional="YES" minCount="1" maxCount="-1" deletionRule="Nullify" destinationEntity="Article" inverseName="author" inverseEntity="Article" syncable="YES"/>
|
15
|
+
</entity>
|
16
|
+
</model>
|
data/test/schema_test.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
require 'xcdm/schema'
|
4
|
+
require 'rexml/document'
|
5
|
+
require 'active_support/all'
|
6
|
+
|
7
|
+
module XCDM
|
8
|
+
class SchemaTest < Test::Unit::TestCase
|
9
|
+
|
10
|
+
def test_initialize
|
11
|
+
s = Schema.new("0.0.1")
|
12
|
+
assert_equal "0.0.1", s.version
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_entity
|
16
|
+
s = Schema.new("0.0.1")
|
17
|
+
entity = nil
|
18
|
+
s.entity("MyType") { entity = self; nil }
|
19
|
+
assert entity.is_a?(Entity), "Block should be executed in context of the new entity"
|
20
|
+
assert_equal [entity], s.entities
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_loader
|
24
|
+
fixture = File.join(File.dirname(__FILE__), 'fixtures', '001_baseline.rb')
|
25
|
+
|
26
|
+
loader = Schema::Loader.new
|
27
|
+
schema = loader.load_file(fixture)
|
28
|
+
assert_not_nil schema
|
29
|
+
assert_equal '0.0.1', schema.version
|
30
|
+
assert_equal schema, loader.schemas.first
|
31
|
+
assert_equal ['Article', 'Author'], schema.entities.map(&:name)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_to_xml
|
35
|
+
in_fixture = File.join(File.dirname(__FILE__), 'fixtures', '001_baseline.rb')
|
36
|
+
loader = Schema::Loader.new
|
37
|
+
schema = loader.load_file(in_fixture)
|
38
|
+
|
39
|
+
out_fixture = File.join(File.dirname(__FILE__), 'fixtures', 'Article.xcdatamodeld', 'Article.xcdatamodel', 'contents')
|
40
|
+
|
41
|
+
inlines = REXML::Document.new(File.read(out_fixture)).to_s.split("\n").map(&:strip)
|
42
|
+
outlines = REXML::Document.new(schema.to_xml).to_s.split("\n").map(&:strip)
|
43
|
+
inlines.each_with_index do |line, i|
|
44
|
+
assert_equal line, outlines[i]
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
data/test/test_schema.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
require 'test/unit'
|
3
|
+
require 'schema'
|
4
|
+
|
5
|
+
class SchemaTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def test_initialize
|
8
|
+
s = Schema.new("0.0.1")
|
9
|
+
assert_equal "0.0.1", s.version
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_entity
|
13
|
+
s = Schema.new("0.0.1")
|
14
|
+
e = s.entity("MyType") { |e| assert e.is_a?(Entity) }
|
15
|
+
assert e.is_a?(Entity)
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-xcdm
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ken Miller
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-07-02 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: builder
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.2'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.2'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: activesupport
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '3.2'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '3.2'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: plist
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '3.1'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.1'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: bundler
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.3'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '1.3'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rake
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: turn
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
description: Ruby DSL for creating Core Data Data Model files without XCode
|
111
|
+
email:
|
112
|
+
- ken@infinitered.com
|
113
|
+
executables:
|
114
|
+
- xcdm
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- .gitignore
|
119
|
+
- .rbenv-version
|
120
|
+
- .rvmrc
|
121
|
+
- Gemfile
|
122
|
+
- Gemfile.lock
|
123
|
+
- LICENSE.txt
|
124
|
+
- README.md
|
125
|
+
- Rakefile
|
126
|
+
- bin/xcdm
|
127
|
+
- lib/ruby-xcdm.rb
|
128
|
+
- lib/xcdm.rb
|
129
|
+
- lib/xcdm/entity.rb
|
130
|
+
- lib/xcdm/schema.rb
|
131
|
+
- lib/xcdm/version.rb
|
132
|
+
- ruby-xcdm.gemspec
|
133
|
+
- test/entity_test.rb
|
134
|
+
- test/fixtures/001_baseline.rb
|
135
|
+
- test/fixtures/Article.xcdatamodeld/Article.xcdatamodel/contents
|
136
|
+
- test/schema_test.rb
|
137
|
+
- test/test_schema.rb
|
138
|
+
homepage: https://github.com/infinitered/ruby-xcdm
|
139
|
+
licenses:
|
140
|
+
- MIT
|
141
|
+
post_install_message:
|
142
|
+
rdoc_options: []
|
143
|
+
require_paths:
|
144
|
+
- lib
|
145
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
146
|
+
none: false
|
147
|
+
requirements:
|
148
|
+
- - ! '>='
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: '0'
|
151
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
152
|
+
none: false
|
153
|
+
requirements:
|
154
|
+
- - ! '>='
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
version: '0'
|
157
|
+
requirements: []
|
158
|
+
rubyforge_project:
|
159
|
+
rubygems_version: 1.8.23
|
160
|
+
signing_key:
|
161
|
+
specification_version: 3
|
162
|
+
summary: Ruby XCDM
|
163
|
+
test_files:
|
164
|
+
- test/entity_test.rb
|
165
|
+
- test/fixtures/001_baseline.rb
|
166
|
+
- test/fixtures/Article.xcdatamodeld/Article.xcdatamodel/contents
|
167
|
+
- test/schema_test.rb
|
168
|
+
- test/test_schema.rb
|