metamodel 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/lib/metamodel.rb +2 -0
- data/lib/metamodel/command.rb +6 -2
- data/lib/metamodel/command/install.rb +52 -0
- data/lib/metamodel/config.rb +4 -0
- data/lib/metamodel/installer.rb +92 -0
- data/lib/metamodel/installer/renderer.rb +116 -0
- data/lib/metamodel/installer/validator.rb +55 -0
- data/lib/metamodel/metafile.rb +61 -0
- data/lib/metamodel/metafile/dsl.rb +44 -0
- data/lib/metamodel/record/association.rb +24 -30
- data/lib/metamodel/record/model.rb +7 -11
- data/lib/metamodel/record/property.rb +0 -4
- data/lib/metamodel/template/belongs_to_association.swift +29 -0
- data/lib/metamodel/template/has_many_association.swift +99 -0
- data/lib/metamodel/template/helper.swift +8 -6
- data/lib/metamodel/template/metamodel.swift +7 -0
- data/lib/metamodel/template/model_delete.swift +2 -5
- data/lib/metamodel/template/model_initialize.swift +6 -18
- data/lib/metamodel/template/model_query.swift +7 -3
- data/lib/metamodel/template/model_update.swift +2 -2
- data/lib/metamodel/template/static_methods.swift +1 -1
- data/lib/metamodel/template/table_initialize.swift +1 -1
- data/lib/metamodel/version.rb +1 -1
- metadata +11 -7
- data/lib/metamodel/command/build.rb +0 -148
- data/lib/metamodel/command/build/renderer.rb +0 -55
- data/lib/metamodel/command/build/resolver.rb +0 -72
- data/lib/metamodel/command/build/translator.rb +0 -66
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a027c62103e0c1e22308807cbb65e60921e2cfc3
|
4
|
+
data.tar.gz: ef725ae4f7bdfbbfd06b306523f127346ac1b44f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d68afdc7a74ed5c62a66f5ba85a071c1ffdee8a45e30b2d23fe581e4c2b4212c5db55a3de07e2f799ab8a08d18b036f490379da9926faaf6abb74c3d17cfa61
|
7
|
+
data.tar.gz: 83c413f52a8c1fe141424395862d9640311fc0cbc114b943f02f5463a134da0ddef7661217bc0a087201a62d240ba020cb3e635961039f418f71d4a5f3f7db57
|
data/lib/metamodel.rb
CHANGED
data/lib/metamodel/command.rb
CHANGED
@@ -5,7 +5,7 @@ module MetaModel
|
|
5
5
|
class Command < CLAide::Command
|
6
6
|
require 'metamodel/command/init'
|
7
7
|
require 'metamodel/command/generate'
|
8
|
-
require 'metamodel/command/
|
8
|
+
require 'metamodel/command/install'
|
9
9
|
require 'metamodel/command/clean'
|
10
10
|
|
11
11
|
include Config::Mixin
|
@@ -18,7 +18,7 @@ module MetaModel
|
|
18
18
|
|
19
19
|
METAMODEL_COMMAND_ALIAS = {
|
20
20
|
"g" => "generate",
|
21
|
-
"i" => "
|
21
|
+
"i" => "install",
|
22
22
|
"b" => "build",
|
23
23
|
"c" => "clean"
|
24
24
|
}
|
@@ -48,6 +48,10 @@ module MetaModel
|
|
48
48
|
super
|
49
49
|
end
|
50
50
|
|
51
|
+
def installer_for_config
|
52
|
+
Installer.new(config.metafile)
|
53
|
+
end
|
54
|
+
|
51
55
|
#-------------------------------------------------------------------------#
|
52
56
|
|
53
57
|
private
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'git'
|
2
|
+
|
3
|
+
module MetaModel
|
4
|
+
class Command
|
5
|
+
class Install < Command
|
6
|
+
include Config::Mixin
|
7
|
+
self.summary = "Build a MetaModel.framework from Metafile"
|
8
|
+
self.description = <<-DESC
|
9
|
+
Clone a metamodel project template from GitHub, parsing Metafile, validating models,
|
10
|
+
generate model swift file and build MetaModel.framework.
|
11
|
+
DESC
|
12
|
+
|
13
|
+
attr_accessor :models
|
14
|
+
|
15
|
+
def initialize(argv)
|
16
|
+
validate!
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def run
|
21
|
+
UI.section "Building MetaModel.framework in project" do
|
22
|
+
prepare
|
23
|
+
installer = installer_for_config
|
24
|
+
installer.install!
|
25
|
+
end
|
26
|
+
UI.notice "Please drag MetaModel.framework into Embedded Binaries phrase.\n"
|
27
|
+
end
|
28
|
+
|
29
|
+
def prepare
|
30
|
+
clone_project
|
31
|
+
end
|
32
|
+
|
33
|
+
def clone_project
|
34
|
+
if File.exist? config.metamodel_xcode_project
|
35
|
+
UI.message "Existing project `#{config.metamodel_xcode_project}`"
|
36
|
+
else
|
37
|
+
UI.section "Cloning MetaModel project into `./metamodel` folder" do
|
38
|
+
Git.clone(config.metamodel_template_uri, 'metamodel', :depth => 1)
|
39
|
+
UI.message "Using `#{config.metamodel_xcode_project}` to build module"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def validate!
|
45
|
+
# super
|
46
|
+
raise Informative, 'No Metafile in current directory' unless config.metafile_in_dir(Pathname.pwd)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/metamodel/config.rb
CHANGED
@@ -0,0 +1,92 @@
|
|
1
|
+
module MetaModel
|
2
|
+
class Installer
|
3
|
+
require 'metamodel/installer/renderer'
|
4
|
+
require 'metamodel/installer/validator'
|
5
|
+
|
6
|
+
include Config::Mixin
|
7
|
+
|
8
|
+
attr_reader :metafile
|
9
|
+
|
10
|
+
attr_accessor :models
|
11
|
+
attr_accessor :associations
|
12
|
+
|
13
|
+
attr_accessor :current_model
|
14
|
+
|
15
|
+
def initialize(metafile)
|
16
|
+
@metafile = metafile
|
17
|
+
end
|
18
|
+
|
19
|
+
def install!
|
20
|
+
@models = metafile.models
|
21
|
+
@associations = metafile.associations
|
22
|
+
Renderer.new(@models, @associations).tap do |renderer|
|
23
|
+
renderer.render!
|
24
|
+
end
|
25
|
+
|
26
|
+
update_initialize_method
|
27
|
+
build_metamodel_framework unless config.skip_build?
|
28
|
+
end
|
29
|
+
|
30
|
+
def update_initialize_method
|
31
|
+
template = File.read File.expand_path(File.join(File.dirname(__FILE__), "./template/metamodel.swift"))
|
32
|
+
result = ErbalTemplate::render_from_hash(template, { :models => @models, :associations => @associations })
|
33
|
+
model_path = Pathname.new("./metamodel/MetaModel/MetaModel.swift")
|
34
|
+
File.write model_path, result
|
35
|
+
end
|
36
|
+
|
37
|
+
def build_metamodel_framework
|
38
|
+
UI.section "Generating MetaModel.framework" do
|
39
|
+
build_framework_on_iphoneos
|
40
|
+
build_framework_on_iphonesimulator
|
41
|
+
copy_framework_swiftmodule_files
|
42
|
+
lipo_frameworks_on_different_archs
|
43
|
+
end
|
44
|
+
UI.message "-> ".green + "MetaModel.framework located in current folder"
|
45
|
+
end
|
46
|
+
|
47
|
+
def build_framework_on_iphoneos
|
48
|
+
build_iphoneos = "xcodebuild -scheme MetaModel \
|
49
|
+
-project MetaModel/MetaModel.xcodeproj \
|
50
|
+
-configuration Release -sdk iphoneos \
|
51
|
+
-derivedDataPath './metamodel' \
|
52
|
+
BITCODE_GENERATION_MODE=bitcode \
|
53
|
+
ONLY_ACTIVE_ARCH=NO \
|
54
|
+
CODE_SIGNING_REQUIRED=NO \
|
55
|
+
CODE_SIGN_IDENTITY= \
|
56
|
+
clean build"
|
57
|
+
result = system "#{build_iphoneos} > /dev/null"
|
58
|
+
raise Informative, 'Building framework on iphoneos failed.' unless result
|
59
|
+
end
|
60
|
+
|
61
|
+
def build_framework_on_iphonesimulator
|
62
|
+
build_iphonesimulator = "xcodebuild -scheme MetaModel \
|
63
|
+
-project MetaModel/MetaModel.xcodeproj \
|
64
|
+
-configuration Release -sdk iphonesimulator \
|
65
|
+
-derivedDataPath './metamodel' \
|
66
|
+
ONLY_ACTIVE_ARCH=NO \
|
67
|
+
CODE_SIGNING_REQUIRED=NO \
|
68
|
+
CODE_SIGN_IDENTITY= \
|
69
|
+
clean build"
|
70
|
+
result = system "#{build_iphonesimulator} > /dev/null"
|
71
|
+
raise Informative, 'Building framework on iphonesimulator failed.' unless result
|
72
|
+
end
|
73
|
+
|
74
|
+
BUILD_PRODUCTS_FOLDER = "./metamodel/Build/Products"
|
75
|
+
|
76
|
+
def copy_framework_swiftmodule_files
|
77
|
+
copy_command = "cp -rf #{BUILD_PRODUCTS_FOLDER}/Release-iphoneos/MetaModel.framework . && \
|
78
|
+
cp -rf #{BUILD_PRODUCTS_FOLDER}/Release-iphonesimulator/MetaModel.framework/Modules/MetaModel.swiftmodule/* \
|
79
|
+
MetaModel.framework/Modules/MetaModel.swiftmodule/"
|
80
|
+
system copy_command
|
81
|
+
end
|
82
|
+
|
83
|
+
def lipo_frameworks_on_different_archs
|
84
|
+
lipo_command = "lipo -create -output MetaModel.framework/MetaModel \
|
85
|
+
#{BUILD_PRODUCTS_FOLDER}/Release-iphonesimulator/MetaModel.framework/MetaModel \
|
86
|
+
#{BUILD_PRODUCTS_FOLDER}/Release-iphoneos/MetaModel.framework/MetaModel"
|
87
|
+
result = system "#{lipo_command}"
|
88
|
+
raise Informative, 'Copy framework to current folder failed.' unless result
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'xcodeproj'
|
2
|
+
|
3
|
+
module MetaModel
|
4
|
+
class Installer
|
5
|
+
class Renderer
|
6
|
+
include Config::Mixin
|
7
|
+
|
8
|
+
attr_reader :project
|
9
|
+
|
10
|
+
attr_reader :models
|
11
|
+
attr_reader :associations
|
12
|
+
|
13
|
+
def initialize(models, associations)
|
14
|
+
@models = models
|
15
|
+
@associations = associations
|
16
|
+
@project = Xcodeproj::Project.open(Config.instance.metamodel_xcode_project)
|
17
|
+
end
|
18
|
+
|
19
|
+
SWIFT_TEMPLATES_FILES = %w(
|
20
|
+
file_header
|
21
|
+
table_initialize
|
22
|
+
model_initialize
|
23
|
+
model_update
|
24
|
+
model_query
|
25
|
+
model_delete
|
26
|
+
static_methods
|
27
|
+
helper
|
28
|
+
)
|
29
|
+
|
30
|
+
def model_swift_templates
|
31
|
+
[].tap do |templates|
|
32
|
+
SWIFT_TEMPLATES_FILES.each do |file_path|
|
33
|
+
template = File.read File.expand_path(File.join(File.dirname(__FILE__), "../template/#{file_path}.swift"))
|
34
|
+
templates << template
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def render!
|
40
|
+
remove_previous_files_refereneces
|
41
|
+
UI.section "Generating model files" do
|
42
|
+
render_model_files
|
43
|
+
end
|
44
|
+
UI.section "Generating association files" do
|
45
|
+
render_association_files
|
46
|
+
end
|
47
|
+
@project.save
|
48
|
+
end
|
49
|
+
|
50
|
+
def remove_previous_files_refereneces
|
51
|
+
target = @project.targets.first
|
52
|
+
|
53
|
+
@models.each do |model|
|
54
|
+
target.source_build_phase.files_references.each do |file_ref|
|
55
|
+
target.source_build_phase.remove_file_reference(file_ref) if file_ref && "#{model.name}.swift" == file_ref.name
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
@associations.each do |association|
|
60
|
+
target.source_build_phase.files_references.each do |file_ref|
|
61
|
+
target.source_build_phase.remove_file_reference(file_ref) if file_ref && "#{association.class_name}.swift" == file_ref.name
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def render_model_files
|
67
|
+
target = @project.targets.first
|
68
|
+
|
69
|
+
models_group = @project.main_group.find_subpath('MetaModel/Models', true)
|
70
|
+
models_group.clear
|
71
|
+
models_group.set_source_tree('SOURCE_ROOT')
|
72
|
+
|
73
|
+
file_refs = []
|
74
|
+
@models.each do |model|
|
75
|
+
result = model_swift_templates.map { |template|
|
76
|
+
ErbalTemplate::render_from_hash(template, { :model => model })
|
77
|
+
}.join("\n")
|
78
|
+
model_path = Pathname.new("./metamodel/MetaModel/#{model.name}.swift")
|
79
|
+
File.write model_path, result
|
80
|
+
|
81
|
+
file_refs << models_group.new_reference(Pathname.new("MetaModel/#{model.name}.swift"))
|
82
|
+
|
83
|
+
UI.message '-> '.green + "Using #{model.name}.swift file"
|
84
|
+
end
|
85
|
+
target.add_file_references file_refs
|
86
|
+
end
|
87
|
+
|
88
|
+
def render_association_files
|
89
|
+
target = @project.targets.first
|
90
|
+
|
91
|
+
association_group = @project.main_group.find_subpath('MetaModel/Associations', true)
|
92
|
+
association_group.clear
|
93
|
+
association_group.set_source_tree('SOURCE_ROOT')
|
94
|
+
|
95
|
+
has_many_association_template = File.read File.expand_path(File.join(File.dirname(__FILE__), "../template/has_many_association.swift"))
|
96
|
+
belongs_to_association_template = File.read File.expand_path(File.join(File.dirname(__FILE__), "../template/belongs_to_association.swift"))
|
97
|
+
|
98
|
+
file_refs = []
|
99
|
+
@associations.each do |association|
|
100
|
+
template = association.relation == :has_many ? has_many_association_template : belongs_to_association_template
|
101
|
+
result = ErbalTemplate::render_from_hash(template, { :association => association })
|
102
|
+
file_name = "#{association.class_name}.swift"
|
103
|
+
File.write Pathname.new("./metamodel/MetaModel/#{file_name}"), result
|
104
|
+
|
105
|
+
file_refs << association_group.new_reference(Pathname.new("MetaModel/#{file_name}"))
|
106
|
+
|
107
|
+
UI.message '-> '.green + "Using #{file_name} file"
|
108
|
+
end
|
109
|
+
target.add_file_references file_refs
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module MetaModel
|
2
|
+
class Installer
|
3
|
+
class Validator
|
4
|
+
require 'metamodel/record/model'
|
5
|
+
require 'metamodel/record/property'
|
6
|
+
require 'metamodel/record/association'
|
7
|
+
|
8
|
+
def translate!
|
9
|
+
satisfy_constraint = @associations.reduce([]) do |remain, association|
|
10
|
+
expect = remain.select { |assoc| assoc.expect_constraint? association }
|
11
|
+
if expect.empty?
|
12
|
+
remain << association
|
13
|
+
else
|
14
|
+
remain.delete expect.first
|
15
|
+
end
|
16
|
+
remain
|
17
|
+
end
|
18
|
+
raise Informative, "Unsatisfied constraints in #{satisfy_constraint.map \
|
19
|
+
{ |x| x.debug_description }}" if satisfy_constraint.size > 0
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
def validate_models
|
24
|
+
existing_types = @models.map { |m| m.properties.map { |property| property.type } }.flatten.uniq
|
25
|
+
unsupported_types = existing_types - supported_types
|
26
|
+
raise Informative, "Unsupported types #{unsupported_types}" unless unsupported_types == []
|
27
|
+
end
|
28
|
+
|
29
|
+
CURRENT_SUPPORTED_BUILT_IN_TYPES = %w[
|
30
|
+
Int
|
31
|
+
Double
|
32
|
+
Float
|
33
|
+
String
|
34
|
+
Bool
|
35
|
+
NSDate
|
36
|
+
]
|
37
|
+
|
38
|
+
def built_in_types
|
39
|
+
CURRENT_SUPPORTED_BUILT_IN_TYPES.map do |t|
|
40
|
+
[t, "#{t}?"]
|
41
|
+
end.flatten
|
42
|
+
end
|
43
|
+
|
44
|
+
def supported_types
|
45
|
+
@models.reduce(CURRENT_SUPPORTED_BUILT_IN_TYPES) { |types, model|
|
46
|
+
types << model.name.to_s
|
47
|
+
}.map { |type|
|
48
|
+
[type, "#{type}?"]
|
49
|
+
}.flatten
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "metamodel/metafile/dsl"
|
2
|
+
|
3
|
+
module MetaModel
|
4
|
+
class Metafile
|
5
|
+
include MetaModel::Metafile::DSL
|
6
|
+
|
7
|
+
attr_accessor :defined_in_file
|
8
|
+
attr_accessor :current_model
|
9
|
+
|
10
|
+
attr_accessor :models
|
11
|
+
attr_accessor :associations
|
12
|
+
|
13
|
+
def initialize(defined_in_file = nil, internal_hash = {})
|
14
|
+
@defined_in_file = defined_in_file
|
15
|
+
@models = []
|
16
|
+
@associations = []
|
17
|
+
|
18
|
+
evaluate_model_definition(defined_in_file)
|
19
|
+
amend_association
|
20
|
+
end
|
21
|
+
|
22
|
+
def evaluate_model_definition(path)
|
23
|
+
UI.section "Analyzing Metafile" do
|
24
|
+
contents ||= File.open(path, 'r:utf-8', &:read)
|
25
|
+
|
26
|
+
if contents.respond_to?(:encoding) && contents.encoding.name != 'UTF-8'
|
27
|
+
contents.encode!('UTF-8')
|
28
|
+
end
|
29
|
+
|
30
|
+
eval(contents, nil, path.to_s)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.from_file(path)
|
35
|
+
path = Pathname.new(path)
|
36
|
+
unless path.exist?
|
37
|
+
raise Informative, "No Metafile exists at path `#{path}`."
|
38
|
+
end
|
39
|
+
|
40
|
+
case path.extname
|
41
|
+
when '', '.metafile'
|
42
|
+
Metafile.new(path)
|
43
|
+
else
|
44
|
+
raise Informative, "Unsupported Metafile format `#{path}`."
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def amend_association
|
49
|
+
name_model_hash = Hash[@models.collect { |model| [model.name, model] }]
|
50
|
+
@associations.map! do |association|
|
51
|
+
major_model = name_model_hash[association.major_model]
|
52
|
+
major_model.associations << association
|
53
|
+
association.major_model = major_model
|
54
|
+
association.secondary_model = name_model_hash[association.secondary_model]
|
55
|
+
raise Informative, "Associations not satisfied in `Metafile`" unless [association.major_model, association.secondary_model].compact.size == 2
|
56
|
+
association
|
57
|
+
end
|
58
|
+
self
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module MetaModel
|
2
|
+
class Metafile
|
3
|
+
module DSL
|
4
|
+
require 'metamodel/record/model'
|
5
|
+
require 'metamodel/record/property'
|
6
|
+
require 'metamodel/record/association'
|
7
|
+
|
8
|
+
def metamodel_version(version)
|
9
|
+
raise Informative,
|
10
|
+
"Meta file #{version} not matched with current metamodel version #{VERSION}" if version != VERSION
|
11
|
+
end
|
12
|
+
|
13
|
+
def define(model_name)
|
14
|
+
UI.message '-> '.green + "Resolving `#{model_name.to_s.camelize}`"
|
15
|
+
@current_model = Record::Model.new(model_name)
|
16
|
+
yield if block_given?
|
17
|
+
@models << @current_model
|
18
|
+
end
|
19
|
+
|
20
|
+
def attr(key, type = :string, **args)
|
21
|
+
@current_model.properties << Record::Property.new(key, type, args)
|
22
|
+
end
|
23
|
+
|
24
|
+
def has_one(name, model_name = nil, **args)
|
25
|
+
model_name = name.to_s.singularize.camelize if model_name.nil?
|
26
|
+
association = Record::Association.new(name, current_model.name, model_name, :has_one, args)
|
27
|
+
@associations << association
|
28
|
+
end
|
29
|
+
|
30
|
+
def has_many(name, model_name = nil, **args)
|
31
|
+
model_name = name.to_s.singularize.camelize if model_name.nil?
|
32
|
+
raise Informative, "has_many relation can't be created with optional model name" if model_name.end_with? "?"
|
33
|
+
association = Record::Association.new(name, current_model.name, model_name, :has_many, args)
|
34
|
+
@associations << association
|
35
|
+
end
|
36
|
+
|
37
|
+
def belongs_to(name, model_name = nil, **args)
|
38
|
+
model_name = name.to_s.singularize.camelize if model_name.nil?
|
39
|
+
association = Record::Association.new(name, current_model.name, model_name, :belongs_to, args)
|
40
|
+
@associations << association
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|