metamodel 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +115 -0
- data/lib/metamodel/command/build/parser.rb +3 -7
- data/lib/metamodel/command/build/renderer.rb +28 -13
- data/lib/metamodel/command/build.rb +20 -6
- data/lib/metamodel/command/init.rb +1 -1
- data/lib/metamodel/config.rb +1 -1
- data/lib/metamodel/model/cocoa_model.rb +32 -18
- data/lib/metamodel/model/cocoa_property.rb +20 -0
- data/lib/metamodel/template/attributes.swift.erb +11 -0
- data/lib/metamodel/template/file_header.swift.erb +10 -0
- data/lib/metamodel/template/initialize.swift.erb +6 -0
- data/lib/metamodel/template/instance_methods.swift.erb +40 -0
- data/lib/metamodel/template/json.swift.erb +16 -0
- data/lib/metamodel/template/metamodel.swift.erb +49 -0
- data/lib/metamodel/template/model_query.swift.erb +61 -0
- data/lib/metamodel/template/model_relation.swift.erb +82 -0
- data/lib/metamodel/template/recordable.swift.erb +10 -0
- data/lib/metamodel/template/static_methods.swift.erb +39 -0
- data/lib/metamodel/user_interface.rb +3 -2
- data/lib/metamodel/version.rb +1 -1
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7736cf1e138517c6079cfc3c290f943ad285bbee
|
4
|
+
data.tar.gz: 75cc11f484c32d1a54ef70d457ba9d83a89f75d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dbad5e08fb83943368a289a2bdf7602b20297a7267f5bcabc5ada3f9264b0ee8a1d8c19e0ee6c5fdc5034094897000486a1b0e3ddcbf8eaa2e304c36d326b2fd
|
7
|
+
data.tar.gz: 0359d81ec9bcd81b30fa7ae9d7056fc7214dde6ed7b8e57dccba885a7195113f7671f8db6aa791602f7818168d28a846d2b271761a50a130915f818a4dbcbc18
|
data/README.md
CHANGED
@@ -2,3 +2,118 @@
|
|
2
2
|
|
3
3
|
# MetaModel
|
4
4
|
|
5
|
+
[![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/draveness/metamodel/blob/master/LICENSE)
|
6
|
+
[![Gem](https://img.shields.io/gem/v/metamodel.svg?style=flat)](http://rubygems.org/gems/metamodel)
|
7
|
+
|
8
|
+
MetaModel is an iOS framework that includes everything about model layer, data persistent, JSON to model constructor and a bunch of APIs which provides a way of dealing with client side database very easily.
|
9
|
+
|
10
|
+
> MetaModel is under-development, API may constantly change before gets to 1.0.0.
|
11
|
+
|
12
|
+
+ [x] Fastest JSON to model API
|
13
|
+
+ [x] Dealing with database without writing SQL
|
14
|
+
+ [x] Most concise API to retrieve data from persistent level
|
15
|
+
|
16
|
+
Use a scaffold file to define your model:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
metamodel_version '0.0.1'
|
20
|
+
|
21
|
+
define :User do |j|
|
22
|
+
# define User model like this
|
23
|
+
j.nickname :string
|
24
|
+
j.avatar :string
|
25
|
+
j.email :string, :unique, default: "default@gmail.com"
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
And then run `meta build` will automatically generate all the code you need.
|
30
|
+
|
31
|
+
## Installation
|
32
|
+
|
33
|
+
```
|
34
|
+
sudo gem install metamodel --verbose
|
35
|
+
```
|
36
|
+
|
37
|
+
**System Requirements**: Current version of MetaModel requires macOS with Ruby 2.2.2 or above
|
38
|
+
|
39
|
+
## Quick Start
|
40
|
+
|
41
|
+
After installation , run `meta init` in your iOS project root folder which will make a `scaffold` directory in current folder.
|
42
|
+
|
43
|
+
```shell
|
44
|
+
$ cd /path/to/project
|
45
|
+
$ meta init
|
46
|
+
|
47
|
+
Initialing MetaModel project
|
48
|
+
|
49
|
+
Creating `scaffold` folder for MetaModel
|
50
|
+
```
|
51
|
+
|
52
|
+
Generate your model scaffold file with `meta generate`.
|
53
|
+
|
54
|
+
```shell
|
55
|
+
$ meta generate User
|
56
|
+
|
57
|
+
Generating model scaffold file
|
58
|
+
|
59
|
+
-> Adding `user.rb` to scaffold folder
|
60
|
+
|
61
|
+
[!] `user.rb` has already generated, use the command below to edit it.
|
62
|
+
|
63
|
+
vim scaffold/user.rb
|
64
|
+
```
|
65
|
+
|
66
|
+
Edit scaffold file using vim, Emacs or other editor and run `meta build`.
|
67
|
+
|
68
|
+
```shell
|
69
|
+
$ meta build
|
70
|
+
|
71
|
+
Building MetaModel project
|
72
|
+
|
73
|
+
Cloning MetaModel project into `./MetaModel` folder
|
74
|
+
Using `./MetaModel/MetaModel.xcodeproj` to build module
|
75
|
+
|
76
|
+
Analyzing scaffold files
|
77
|
+
-> Resolving `user.rb`
|
78
|
+
|
79
|
+
Generating model files
|
80
|
+
-> Using User.swift file
|
81
|
+
```
|
82
|
+
|
83
|
+
This command create a brand new xcode project in `./MetaModel` folder, just add the `MetaModel.project` into your project or workspace. And add `MetaModel.framework` to **Linked frameworks and Libraries** phrase which located in `General` tab.
|
84
|
+
|
85
|
+
![add-metamodel-project-demo](images/add-metamodel-project-demo.png)
|
86
|
+
|
87
|
+
Add this line of code in your project.
|
88
|
+
|
89
|
+
```swift
|
90
|
+
import MetaModel
|
91
|
+
```
|
92
|
+
|
93
|
+
![import-metamodel-module](images/import-metamodel-module.png)
|
94
|
+
|
95
|
+
## License
|
96
|
+
|
97
|
+
This project is licensed under the terms of the MIT license. See the [LICENSE](./LICENSE) file.
|
98
|
+
|
99
|
+
The MIT License (MIT)
|
100
|
+
|
101
|
+
Copyright (c) 2016 Draveness
|
102
|
+
|
103
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
104
|
+
of this software and associated documentation files (the "Software"), to deal
|
105
|
+
in the Software without restriction, including without limitation the rights
|
106
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
107
|
+
copies of the Software, and to permit persons to whom the Software is
|
108
|
+
furnished to do so, subject to the following conditions:
|
109
|
+
|
110
|
+
The above copyright notice and this permission notice shall be included in all
|
111
|
+
copies or substantial portions of the Software.
|
112
|
+
|
113
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
114
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
115
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
116
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
117
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
118
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
119
|
+
SOFTWARE.
|
@@ -15,15 +15,13 @@ module MetaModel
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def parse
|
18
|
-
title_options = { :verbose_prefix => '-> '.green }
|
19
18
|
UI.section "Analyzing scaffold files" do
|
20
19
|
scaffold_path = config.scaffold_path
|
21
20
|
scaffolds = Dir[scaffold_path + "*.rb"]
|
22
21
|
scaffolds.each do |scaffold_file|
|
23
|
-
UI.
|
24
|
-
|
25
|
-
|
26
|
-
end
|
22
|
+
UI.message '-> '.green + "Resolving `#{File.basename(scaffold_file)}`"
|
23
|
+
scaffold_code = File.read(scaffold_path + scaffold_file)
|
24
|
+
eval scaffold_code
|
27
25
|
end
|
28
26
|
end
|
29
27
|
@models
|
@@ -38,9 +36,7 @@ module MetaModel
|
|
38
36
|
|
39
37
|
def define(model_name)
|
40
38
|
model = CocoaModel.new(model_name)
|
41
|
-
|
42
39
|
yield PropertyConstructor.new(model)
|
43
|
-
|
44
40
|
@models << model
|
45
41
|
end
|
46
42
|
end
|
@@ -17,29 +17,44 @@ module MetaModel
|
|
17
17
|
end
|
18
18
|
|
19
19
|
class << self
|
20
|
-
def render(model)
|
21
|
-
template_path = File.expand_path(File.join(File.dirname(__FILE__), "../../template/model.swift.erb"))
|
22
|
-
template = File.read template_path
|
23
20
|
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
def templates
|
22
|
+
results = []
|
23
|
+
file_paths = %w{file_header attributes json recordable initialize static_methods instance_methods model_query model_relation}
|
24
|
+
file_paths.each do |file_path|
|
25
|
+
template = File.read File.expand_path(File.join(File.dirname(__FILE__), "../../template/#{file_path}.swift.erb"))
|
26
|
+
results << template
|
27
|
+
end
|
28
|
+
results
|
29
|
+
end
|
27
30
|
|
31
|
+
def render(models)
|
28
32
|
project = Xcodeproj::Project.open(Config.instance.metamodel_xcode_project)
|
29
33
|
target = project.targets.first
|
30
|
-
|
31
|
-
|
32
|
-
|
34
|
+
models.each do |model|
|
35
|
+
target.source_build_phase.files_references.each do |file_ref|
|
36
|
+
target.source_build_phase.remove_file_reference(file_ref) if file_ref && "#{model.name}.swift" == file_ref.name
|
37
|
+
end
|
33
38
|
end
|
34
|
-
|
35
39
|
models_group = project.main_group.find_subpath('MetaModel/Models', true)
|
36
40
|
models_group.clear
|
37
41
|
models_group.set_source_tree('SOURCE_ROOT')
|
38
|
-
file_ref = models_group.new_reference Pathname.new("MetaModel/#{model.name}.swift")
|
39
|
-
target.add_file_references [file_ref]
|
40
42
|
|
41
|
-
|
43
|
+
file_refs = []
|
44
|
+
models.each do |model|
|
45
|
+
result = templates.map { |template|
|
46
|
+
ErbalT::render_from_hash(template, { :model => model })
|
47
|
+
}.join("\n")
|
48
|
+
model_path = Pathname.new("./MetaModel/MetaModel/#{model.name}.swift")
|
49
|
+
File.write model_path, result
|
42
50
|
|
51
|
+
file_refs << models_group.new_reference(Pathname.new("MetaModel/#{model.name}.swift"))
|
52
|
+
|
53
|
+
UI.message '-> '.green + "Using #{model.name}.swift file"
|
54
|
+
end
|
55
|
+
target.add_file_references file_refs
|
56
|
+
|
57
|
+
project.save
|
43
58
|
end
|
44
59
|
end
|
45
60
|
|
@@ -22,6 +22,7 @@ module MetaModel
|
|
22
22
|
clone_project
|
23
23
|
parse_template
|
24
24
|
render_model_files
|
25
|
+
update_initialize_method
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
@@ -42,22 +43,35 @@ module MetaModel
|
|
42
43
|
end
|
43
44
|
|
44
45
|
def render_model_files
|
45
|
-
title_options = { :verbose_prefix => '-> '.green }
|
46
46
|
UI.section "Generating model files" do
|
47
|
-
@models
|
48
|
-
UI.titled_section "Using #{model.name}.swift file", title_options do
|
49
|
-
Renderer.render(model)
|
50
|
-
end
|
51
|
-
end
|
47
|
+
Renderer.render(@models)
|
52
48
|
end
|
53
49
|
end
|
54
50
|
|
51
|
+
def update_initialize_method
|
52
|
+
template = File.read File.expand_path(File.join(File.dirname(__FILE__), "../template/metamodel.swift.erb"))
|
53
|
+
result = ErbalT::render_from_hash(template, { :models => @models })
|
54
|
+
model_path = Pathname.new("./MetaModel/MetaModel/MetaModel.swift")
|
55
|
+
File.write model_path, result
|
56
|
+
end
|
57
|
+
|
55
58
|
def validate!
|
56
59
|
super
|
57
60
|
raise Informative, 'No scaffold folder in directory' unless config.scaffold_path_in_dir(Pathname.pwd)
|
58
61
|
end
|
59
62
|
|
60
63
|
private
|
64
|
+
|
65
|
+
class ErbalT < OpenStruct
|
66
|
+
def self.render_from_hash(t, h)
|
67
|
+
ErbalT.new(h).render(t)
|
68
|
+
end
|
69
|
+
|
70
|
+
def render(template)
|
71
|
+
ERB.new(template).result(binding)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
61
75
|
end
|
62
76
|
end
|
63
77
|
end
|
data/lib/metamodel/config.rb
CHANGED
@@ -16,7 +16,7 @@ module MetaModel
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def table_name
|
19
|
-
name.to_s.
|
19
|
+
name.to_s.tableize
|
20
20
|
end
|
21
21
|
|
22
22
|
def relation_name
|
@@ -32,24 +32,38 @@ module MetaModel
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
+
def property_key_value_pairs
|
36
|
+
@properties.map { |property| "#{property.key.to_s}: #{property.key.to_s}" }.join(", ")
|
37
|
+
end
|
38
|
+
|
39
|
+
def property_key_type_pairs
|
40
|
+
@properties.map { |property| "#{property.key.to_s}: #{property.type.to_s}" }.join(", ")
|
41
|
+
end
|
42
|
+
|
43
|
+
def property_exclude_id_key_value_pairs(prefix = true)
|
44
|
+
result = properties_exclude_id.map { |property| "#{property.key.to_s}: #{property.key.to_s}" }.join(", ")
|
45
|
+
return result unless prefix
|
46
|
+
return result.length > 0 ? ", #{result}" : ""
|
47
|
+
end
|
48
|
+
|
49
|
+
def property_exclude_id_key_type_pairs(prefix = true)
|
50
|
+
result = properties_exclude_id.map { |property| "#{property.key.to_s}: #{property.type.to_s}" }.join(", ")
|
51
|
+
return result unless prefix
|
52
|
+
return result.length > 0 ? ", #{result}" : ""
|
53
|
+
end
|
54
|
+
|
35
55
|
def build_table
|
36
|
-
table = ""
|
37
|
-
@properties.
|
38
|
-
|
39
|
-
if property.
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
table << "t.column(#{property_key})\n\t\t\t"
|
48
|
-
end
|
49
|
-
table << "t.primaryKey(#{property_key})\n\t\t\t" if property.is_primary?
|
50
|
-
table << "t.unique(#{property_key})\n\t\t\t" if property.is_unique?
|
51
|
-
end
|
52
|
-
table
|
56
|
+
table = "CREATE TABLE #{table_name}"
|
57
|
+
main_sql = @properties.map do |property|
|
58
|
+
result = "#{property.key} #{property.database_type}"
|
59
|
+
result << " NOT NULL" if !property.is_optional?
|
60
|
+
result << " PRIMARY KEY" if property.is_primary?
|
61
|
+
result << " UNIQUE" if property.is_unique?
|
62
|
+
result << " DEFAULT #{property.default_value}" if property.has_default_value?
|
63
|
+
result
|
64
|
+
end.join(", ")
|
65
|
+
main_sql = "(#{main_sql});"
|
66
|
+
table + main_sql
|
53
67
|
end
|
54
68
|
end
|
55
69
|
|
@@ -20,6 +20,22 @@ module MetaModel
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
+
def type_without_optional
|
24
|
+
return type.to_s[0..-2] if key.to_s.end_with? "?"
|
25
|
+
type
|
26
|
+
end
|
27
|
+
|
28
|
+
def database_type
|
29
|
+
lowercase_type = self.type.downcase
|
30
|
+
if lowercase_type == "string"
|
31
|
+
return "TEXT"
|
32
|
+
elsif lowercase_type == "int"
|
33
|
+
return "INTEGER"
|
34
|
+
elsif lowercase_type == "double"
|
35
|
+
return "REAL"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
23
39
|
def is_unique?
|
24
40
|
@modifiers.include? :unique
|
25
41
|
end
|
@@ -28,6 +44,10 @@ module MetaModel
|
|
28
44
|
@modifiers.include? :primary
|
29
45
|
end
|
30
46
|
|
47
|
+
def is_optional?
|
48
|
+
@type.to_s.end_with? "?"
|
49
|
+
end
|
50
|
+
|
31
51
|
def has_default_value?
|
32
52
|
@modifiers[:default].nil?
|
33
53
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
public struct <%= model.name %> {
|
2
|
+
<% model.properties.each do |property| %><%= """public var #{property.key}: #{property.type}""" %>
|
3
|
+
<% end %>
|
4
|
+
static let tableName = "<%= model.table_name %>"
|
5
|
+
|
6
|
+
public enum Column: String, Unwrapped {
|
7
|
+
<% model.properties.each do |property| %><%= """case #{property.key} = \"#{property.key}\"""" %>
|
8
|
+
<% end %>
|
9
|
+
var unwrapped: String { get { return self.rawValue.unwrapped } }
|
10
|
+
}
|
11
|
+
}
|
@@ -0,0 +1,40 @@
|
|
1
|
+
public extension <%= model.name %> {
|
2
|
+
var itself: String { get { return "WHERE \(<%= model.name %>.tableName.unwrapped).\("id".unwrapped) = \(id)" } }
|
3
|
+
|
4
|
+
func delete() {
|
5
|
+
let deleteSQL = "DELETE FROM \(<%= model.name %>.tableName.unwrapped) \(itself)"
|
6
|
+
executeSQL(deleteSQL)
|
7
|
+
}
|
8
|
+
|
9
|
+
<% model.properties_exclude_id.each do |property| %>
|
10
|
+
<%= """mutating func update(#{property.key} #{property.key}: #{property.type}) -> #{model.name} {
|
11
|
+
return self.update([.#{property.key}: #{property.key}])
|
12
|
+
}""" %>
|
13
|
+
<% end %>
|
14
|
+
|
15
|
+
mutating func update(attributes: [<%= model.name %>.Column: Any]) -> <%= model.name %> {
|
16
|
+
var setSQL: [String] = []
|
17
|
+
for (key, _) in attributes {
|
18
|
+
switch key {
|
19
|
+
<% model.properties_exclude_id.each do |property| %>
|
20
|
+
<%= """case .#{property.key}: setSQL.append(\"\(key.unwrapped) = \(self.(#{property.key}#{property.is_optional? ? "?" : ""}.unwrapped)\")
|
21
|
+
""" %>
|
22
|
+
<% end %>
|
23
|
+
default: break
|
24
|
+
}
|
25
|
+
}
|
26
|
+
let updateSQL = "UPDATE \(<%= model.name %>.tableName.unwrapped) SET \(setSQL.joinWithSeparator(", ")) \(itself)"
|
27
|
+
executeSQL(updateSQL) {
|
28
|
+
for (key, value) in attributes {
|
29
|
+
switch key {
|
30
|
+
<% model.properties_exclude_id.each do |property| %>
|
31
|
+
<%= """case .#{property.key}: self.#{property.key} = value as#{property.is_optional? ? "?" : "!"} #{property.type_without_optional}
|
32
|
+
""" %>
|
33
|
+
<% end %>
|
34
|
+
default: break
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
return self
|
39
|
+
}
|
40
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
extension <%= model.name %> {
|
2
|
+
public init(json: [String: Any]) {
|
3
|
+
let id: Int = json["id"] as! Int
|
4
|
+
<% model.properties_exclude_id.each do |property| %>
|
5
|
+
<%= """let #{property.key}: #{property.type} = json[\"#{property.key}\"] as! #{property.type}
|
6
|
+
""" %>
|
7
|
+
<% end %>
|
8
|
+
|
9
|
+
self.init(<%= model.property_key_value_pairs %>)
|
10
|
+
}
|
11
|
+
|
12
|
+
public init(jsonData: NSData) throws {
|
13
|
+
let json = try NSJSONSerialization.JSONObjectWithData(jsonData, options: .AllowFragments) as! [String: Any]
|
14
|
+
self.init(json: json)
|
15
|
+
}
|
16
|
+
}
|
@@ -0,0 +1,49 @@
|
|
1
|
+
//
|
2
|
+
// MetaModel.swift
|
3
|
+
// MetaModel
|
4
|
+
//
|
5
|
+
// Created by MetaModel.
|
6
|
+
// Copyright © 2016 MetaModel. All rights reserved.
|
7
|
+
//
|
8
|
+
|
9
|
+
import Foundation
|
10
|
+
import SQLite
|
11
|
+
|
12
|
+
let path = NSSearchPathForDirectoriesInDomains(
|
13
|
+
.DocumentDirectory, .UserDomainMask, true
|
14
|
+
).first! as String
|
15
|
+
|
16
|
+
let db = try! Connection("\(path)/metamodel_db.sqlite3")
|
17
|
+
|
18
|
+
public class MetaModel {
|
19
|
+
public static func initialize() {
|
20
|
+
<% models.each do |model| %><%= "#{model.name}.initialize()" %>
|
21
|
+
<% end %>
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
func executeSQL(sql: String, success: (() -> ())? = nil) -> Statement? {
|
26
|
+
defer { print("\n") }
|
27
|
+
print("-> Begin Transaction")
|
28
|
+
let startDate = NSDate()
|
29
|
+
do {
|
30
|
+
let result = try db.run(sql)
|
31
|
+
let endDate = NSDate()
|
32
|
+
let interval = endDate.timeIntervalSinceDate(startDate) * 1000
|
33
|
+
print("\tSQL (\(interval.format("0.2"))ms) \(sql)")
|
34
|
+
print("-> Commit Transaction")
|
35
|
+
|
36
|
+
if let success = success {
|
37
|
+
success()
|
38
|
+
}
|
39
|
+
|
40
|
+
return result
|
41
|
+
} catch let error {
|
42
|
+
let endDate = NSDate()
|
43
|
+
let interval = endDate.timeIntervalSinceDate(startDate) * 1000
|
44
|
+
print("\tSQL (\(interval.format("0.2"))ms) \(sql)")
|
45
|
+
print("\t\(error)")
|
46
|
+
print("-> Rollback transaction")
|
47
|
+
}
|
48
|
+
return nil
|
49
|
+
}
|
@@ -0,0 +1,61 @@
|
|
1
|
+
public extension <%= model.name %> {
|
2
|
+
static var all: <%= model.relation_name %> {
|
3
|
+
get { return <%= model.relation_name %>() }
|
4
|
+
}
|
5
|
+
|
6
|
+
static func first(length: UInt) -> <%= model.relation_name %> {
|
7
|
+
return <%= model.relation_name %>().orderBy(.id, asc: true).limit(length)
|
8
|
+
}
|
9
|
+
|
10
|
+
static func last(length: UInt) -> <%= model.relation_name %> {
|
11
|
+
return <%= model.relation_name %>().orderBy(.id, asc: false).limit(length)
|
12
|
+
}
|
13
|
+
|
14
|
+
static func find(id: Int) -> <%= model.name %>? {
|
15
|
+
return <%= model.relation_name %>().find(id).first
|
16
|
+
}
|
17
|
+
|
18
|
+
static func findBy(id id: Int) -> <%= model.name %>? {
|
19
|
+
return <%= model.relation_name %>().findBy(id: id).first
|
20
|
+
}
|
21
|
+
|
22
|
+
<% model.properties_exclude_id.each do |property| %><%= """static func findBy(#{property.key} #{property.key}: #{property.type_without_optional}) -\> #{model.name}? {
|
23
|
+
return #{model.relation_name}().findBy(#{property.key}: #{property.key}).first
|
24
|
+
}""" %>
|
25
|
+
<% end %>
|
26
|
+
static func filter(column: <%= model.name %>.Column, value: Any) -> <%= model.relation_name %> {
|
27
|
+
return <%= model.relation_name %>().filter([column: value])
|
28
|
+
}
|
29
|
+
|
30
|
+
static func filter(conditions: [<%= model.name %>.Column: Any]) -> <%= model.relation_name %> {
|
31
|
+
return <%= model.relation_name %>().filter(conditions)
|
32
|
+
}
|
33
|
+
|
34
|
+
static func limit(length: UInt, offset: UInt = 0) -> <%= model.relation_name %> {
|
35
|
+
return <%= model.relation_name %>().limit(length, offset: offset)
|
36
|
+
}
|
37
|
+
|
38
|
+
static func take(length: UInt) -> <%= model.relation_name %> {
|
39
|
+
return limit(length)
|
40
|
+
}
|
41
|
+
|
42
|
+
static func offset(offset: UInt) -> <%= model.relation_name %> {
|
43
|
+
return <%= model.relation_name %>().offset(offset)
|
44
|
+
}
|
45
|
+
|
46
|
+
static func groupBy(columns: <%= model.name %>.Column...) -> <%= model.relation_name %> {
|
47
|
+
return <%= model.relation_name %>().groupBy(columns)
|
48
|
+
}
|
49
|
+
|
50
|
+
static func groupBy(columns: [<%= model.name %>.Column]) -> <%= model.relation_name %> {
|
51
|
+
return <%= model.relation_name %>().groupBy(columns)
|
52
|
+
}
|
53
|
+
|
54
|
+
static func orderBy(column: <%= model.name %>.Column) -> <%= model.relation_name %> {
|
55
|
+
return <%= model.relation_name %>().orderBy(column)
|
56
|
+
}
|
57
|
+
|
58
|
+
static func orderBy(column: <%= model.name %>.Column, asc: Bool) -> <%= model.relation_name %> {
|
59
|
+
return <%= model.relation_name %>().orderBy(column, asc: asc)
|
60
|
+
}
|
61
|
+
}
|
@@ -0,0 +1,82 @@
|
|
1
|
+
public class <%= model.relation_name %>: Relation<<%= model.name %>> {
|
2
|
+
override init() {
|
3
|
+
super.init()
|
4
|
+
self.select = "SELECT \(<%= model.name %>.tableName.unwrapped).* FROM \(<%= model.name %>.tableName.unwrapped)"
|
5
|
+
}
|
6
|
+
|
7
|
+
func expandColumn(column: <%= model.name %>.Column) -> String {
|
8
|
+
return "\(<%= model.name %>.tableName.unwrapped).\(column.unwrapped)"
|
9
|
+
}
|
10
|
+
|
11
|
+
// MARK: Query
|
12
|
+
|
13
|
+
public func find(id: Int) -> Self {
|
14
|
+
return self.findBy(id: id)
|
15
|
+
}
|
16
|
+
|
17
|
+
public func findBy(id id: Int) -> Self {
|
18
|
+
return self.filter([.id: id]).limit(1)
|
19
|
+
}
|
20
|
+
|
21
|
+
<% model.properties_exclude_id.each do |property| %>
|
22
|
+
<%= """public func findBy(#{property.key} #{property.key}: #{property.type_without_optional}) -\> Self {
|
23
|
+
return self.filter([.#{property.key}: #{property.key}]).limit(1)
|
24
|
+
}""" %>
|
25
|
+
<% end %>
|
26
|
+
|
27
|
+
public func filter(conditions: [<%= model.name %>.Column: Any]) -> Self {
|
28
|
+
for (column, value) in conditions {
|
29
|
+
let columnSQL = "\(expandColumn(column))"
|
30
|
+
|
31
|
+
func filterByEqual(value: Any) {
|
32
|
+
self.filter.append("\(columnSQL) = \(value)")
|
33
|
+
}
|
34
|
+
|
35
|
+
func filterByIn(value: [String]) {
|
36
|
+
self.filter.append("\(columnSQL) IN (\(value.joinWithSeparator(", ")))")
|
37
|
+
}
|
38
|
+
|
39
|
+
if let value = value as? String {
|
40
|
+
filterByEqual(value.unwrapped)
|
41
|
+
} else if let value = value as? Int {
|
42
|
+
filterByEqual(value)
|
43
|
+
} else if let value = value as? Double {
|
44
|
+
filterByEqual(value)
|
45
|
+
} else if let value = value as? [String] {
|
46
|
+
filterByIn(value.map { $0.unwrapped })
|
47
|
+
} else if let value = value as? [Int] {
|
48
|
+
filterByIn(value.map { $0.description })
|
49
|
+
} else if let value = value as? [Double] {
|
50
|
+
filterByIn(value.map { $0.description })
|
51
|
+
} else {
|
52
|
+
let valueMirror = Mirror(reflecting: value)
|
53
|
+
print("!!!: UNSUPPORTED TYPE \(valueMirror.subjectType)")
|
54
|
+
}
|
55
|
+
|
56
|
+
}
|
57
|
+
return self
|
58
|
+
}
|
59
|
+
|
60
|
+
public func groupBy(columns: <%= model.name %>.Column...) -> Self {
|
61
|
+
return self.groupBy(columns)
|
62
|
+
}
|
63
|
+
|
64
|
+
public func groupBy(columns: [<%= model.name %>.Column]) -> Self {
|
65
|
+
func groupBy(column: <%= model.name %>.Column) {
|
66
|
+
self.group.append("\(expandColumn(column))")
|
67
|
+
}
|
68
|
+
_ = columns.flatMap(groupBy)
|
69
|
+
return self
|
70
|
+
}
|
71
|
+
|
72
|
+
public func orderBy(column: <%= model.name %>.Column) -> Self {
|
73
|
+
self.order.append("\(expandColumn(column))")
|
74
|
+
return self
|
75
|
+
}
|
76
|
+
|
77
|
+
public func orderBy(column: <%= model.name %>.Column, asc: Bool) -> Self {
|
78
|
+
self.order.append("\(expandColumn(column)) \(asc ? "ASC".unwrapped : "DESC".unwrapped)")
|
79
|
+
return self
|
80
|
+
}
|
81
|
+
|
82
|
+
}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
extension <%= model.name %>: Recordable {
|
2
|
+
public init(values: Array<Optional<Binding>>) {
|
3
|
+
let id: Int64 = values[0] as! Int64
|
4
|
+
<% model.properties_exclude_id.each_with_index do |property, index| %>
|
5
|
+
<%= """let #{property.key}: #{property.type} = values[#{index + 1}] as! #{property.type}
|
6
|
+
""" %>
|
7
|
+
<% end %>
|
8
|
+
self.init(id: Int(id)<%= model.property_exclude_id_key_value_pairs %>)
|
9
|
+
}
|
10
|
+
}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
public extension <%= model.name %> {
|
2
|
+
static func deleteAll() {
|
3
|
+
let deleteAllSQL = "DELETE FROM \(tableName.unwrapped)"
|
4
|
+
executeSQL(deleteAllSQL)
|
5
|
+
}
|
6
|
+
static func count() -> Int {
|
7
|
+
let countSQL = "SELECT count(*) FROM \(tableName.unwrapped)"
|
8
|
+
guard let count = executeSQL(countSQL)?.next()?.first as? Int64 else { return 0 }
|
9
|
+
return Int(count)
|
10
|
+
}
|
11
|
+
|
12
|
+
static func new(<%= model.property_exclude_id_key_type_pairs(false) %>) -> <%= model.name %> {
|
13
|
+
return <%= model.name %>(id: -1<%= model.property_exclude_id_key_value_pairs %>)
|
14
|
+
}
|
15
|
+
|
16
|
+
static func create(<%= model.property_key_type_pairs %>) -> <%= model.name %>? {
|
17
|
+
var columnsSQL: [<%= model.name %>.Column] = []
|
18
|
+
var valuesSQL: [Unwrapped] = []
|
19
|
+
|
20
|
+
columnsSQL.append(.id)
|
21
|
+
valuesSQL.append(id)
|
22
|
+
|
23
|
+
<% model.properties_exclude_id.each do |property| %><% if property.is_optional? %>
|
24
|
+
<%= """if let #{property.key} = #{property.key} {
|
25
|
+
columnsSQL.append(.#{property.key})
|
26
|
+
valuesSQL.append(#{property.key})
|
27
|
+
}
|
28
|
+
""" %>
|
29
|
+
<% else %>
|
30
|
+
<%= """columnsSQL.append(.#{property.key})
|
31
|
+
valuesSQL.append(#{property.key})
|
32
|
+
""" %>
|
33
|
+
<% end %><% end %>
|
34
|
+
|
35
|
+
let insertSQL = "INSERT INTO \(tableName.unwrapped) (\(columnsSQL.map { $0.rawValue }.joinWithSeparator(", "))) VALUES (\(valuesSQL.map { $0.unwrapped }.joinWithSeparator(", ")))"
|
36
|
+
guard let _ = executeSQL(insertSQL) else { return nil }
|
37
|
+
return <%= model.name %>(<%= model.property_key_value_pairs %>)
|
38
|
+
}
|
39
|
+
}
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module MetaModel
|
2
|
-
|
2
|
+
# The code in this file is mainly borrowed from cocoapods/user_interface.rb
|
3
|
+
# which used to generate output messages to user.
|
3
4
|
module UserInterface
|
4
5
|
require 'colored'
|
5
6
|
|
@@ -393,7 +394,7 @@ module MetaModel
|
|
393
394
|
#
|
394
395
|
# @return [String] The formatted string.
|
395
396
|
#
|
396
|
-
# @note If
|
397
|
+
# @note If MetaModel is not being run in a terminal or the width of the
|
397
398
|
# terminal is too small a width of 80 is assumed.
|
398
399
|
#
|
399
400
|
def wrap_string(string, indent = 0)
|
data/lib/metamodel/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metamodel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Draveness Zuo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-09-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: claide
|
@@ -151,7 +151,17 @@ files:
|
|
151
151
|
- lib/metamodel/model/cocoa_model.rb
|
152
152
|
- lib/metamodel/model/cocoa_property.rb
|
153
153
|
- lib/metamodel/model/property_constructor.rb
|
154
|
+
- lib/metamodel/template/attributes.swift.erb
|
155
|
+
- lib/metamodel/template/file_header.swift.erb
|
156
|
+
- lib/metamodel/template/initialize.swift.erb
|
157
|
+
- lib/metamodel/template/instance_methods.swift.erb
|
158
|
+
- lib/metamodel/template/json.swift.erb
|
159
|
+
- lib/metamodel/template/metamodel.swift.erb
|
154
160
|
- lib/metamodel/template/model.swift.erb
|
161
|
+
- lib/metamodel/template/model_query.swift.erb
|
162
|
+
- lib/metamodel/template/model_relation.swift.erb
|
163
|
+
- lib/metamodel/template/recordable.swift.erb
|
164
|
+
- lib/metamodel/template/static_methods.swift.erb
|
155
165
|
- lib/metamodel/user_interface.rb
|
156
166
|
- lib/metamodel/version.rb
|
157
167
|
homepage: https://github.com/Draveness/MetaModel
|