ruby-xcdm 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|