metamodel 0.0.1 → 0.0.2
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/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
|
+
[](https://github.com/draveness/metamodel/blob/master/LICENSE)
|
6
|
+
[](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
|
+

|
86
|
+
|
87
|
+
Add this line of code in your project.
|
88
|
+
|
89
|
+
```swift
|
90
|
+
import MetaModel
|
91
|
+
```
|
92
|
+
|
93
|
+

|
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
|