metamodel 0.1.5 → 0.1.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 36ad33602070abacabd6daa90047708d6888e35b
4
- data.tar.gz: bfbdbeeab3881c3e592b62c1084dc505f7e68d7e
3
+ metadata.gz: d1b98b98f7bd126d83e0066d44beb8beb5708351
4
+ data.tar.gz: 48f9bb451aeb155aaa3a6cbd7ee426b7e0aa1467
5
5
  SHA512:
6
- metadata.gz: 0d464c4eac3190573387c23fb4f2ddd28bb632fbd17833bf618e23bdd64b7c0247f8039020d46d11b87801d269d5321e486b6eae952923955b55fb3bc64404ab
7
- data.tar.gz: e8a655ea0d4e244f3239a3b1c94ce8dd5b1b6391ab45af7483cbad83313b395b8e8e906800070c439bc7280bf6206f9e9780284d8310d8222fd449ee9f515b81
6
+ metadata.gz: a2de3e544e6e583d81a84d51bc82ec98aebe2b7fc0a0d73b11ebfe4624a0ab06a8c42b00c03132e3a210644d306247360376f0711ecd902ad6406d72413c1e48
7
+ data.tar.gz: 0947bc3d2862f25536d744e963bc0213ffaaaf63c4a9dbc60ff4f368813859dd5306d8b10c66822dcb15efcaef943ed9db9fc5e183d971c7566902d631b0eb9a
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ![MetaModel-banner](./images/banner.png)
2
2
 
3
- # MetaModel
3
+ # MetaModel [![Join the chat at https://gitter.im/MModel/MetaModel](https://badges.gitter.im/MModel/MetaModel.svg)](https://gitter.im/MModel/MetaModel?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
4
4
 
5
5
  [![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/draveness/metamodel/blob/master/LICENSE)
6
6
  [![Gem](https://img.shields.io/gem/v/metamodel.svg?style=flat)](http://rubygems.org/gems/metamodel)
@@ -0,0 +1,72 @@
1
+ module MetaModel
2
+ class Command
3
+ class Build
4
+ class Resolver
5
+
6
+ include Config::Mixin
7
+
8
+ require 'metamodel/record/model'
9
+ require 'metamodel/record/property'
10
+ require 'metamodel/record/association'
11
+
12
+ attr_accessor :models
13
+ attr_accessor :associations
14
+
15
+ attr_accessor :current_model
16
+
17
+ def initialize
18
+ @models = []
19
+ @associations = []
20
+ end
21
+
22
+ def resolve
23
+ UI.section "Analyzing Metafile" do
24
+ metafile_path = config.metafile_path
25
+ eval File.read(metafile_path)
26
+ end
27
+ return models, associations
28
+ end
29
+
30
+ private
31
+
32
+ def metamodel_version(version)
33
+ raise Informative,
34
+ "Meta file #{version} not matched with current metamodel version #{VERSION}" if version != VERSION
35
+ end
36
+
37
+ def define(model_name)
38
+ UI.message '-> '.green + "Resolving `#{model_name.to_s.camelize}`"
39
+ @current_model = Record::Model.new(model_name)
40
+ yield
41
+ @models << current_model
42
+ end
43
+
44
+ def attr(key, type = :string, *args)
45
+ current_model.properties << Record::Property.new(key, type, args)
46
+ end
47
+
48
+ def has_one(name, model_name = nil)
49
+ model_name = name.to_s.singularize.camelize if model_name.nil?
50
+ association = Record::Association.new(name, current_model.name, model_name, :has_one)
51
+ @associations << association
52
+ end
53
+
54
+ def has_many(name, model_name = nil)
55
+ model_name = name.to_s.singularize.camelize if model_name.nil?
56
+ raise Informative, "has_many relation can't be created with optional model name" if model_name.end_with? "?"
57
+ association = Record::Association.new(name, current_model.name, model_name, :has_many)
58
+ @associations << association
59
+ end
60
+
61
+ def belongs_to(name, model_name = nil)
62
+ model_name = name.to_s.singularize.camelize if model_name.nil?
63
+ association = Record::Association.new(name, current_model.name, model_name, :belongs_to)
64
+ @associations << association
65
+ end
66
+
67
+ private
68
+
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,52 @@
1
+ module MetaModel
2
+ class Command
3
+ class Build
4
+ class Translator
5
+ require 'metamodel/record/model'
6
+ require 'metamodel/record/property'
7
+ require 'metamodel/record/association'
8
+
9
+ attr_reader :models
10
+ attr_reader :associations
11
+
12
+ def initialize(models, associations)
13
+ @models = models
14
+ @associations = associations
15
+ end
16
+
17
+ def translate
18
+ name_model_hash = Hash[@models.collect { |model| [model.name, model] }]
19
+ @associations.map! do |association|
20
+ major_model = name_model_hash[association.major_model]
21
+ major_model.associations << association
22
+ association.major_model = major_model
23
+ association.secondary_model = name_model_hash[association.secondary_model]
24
+ raise Informative, "Associations not satisfied in `Metafile`" \
25
+ unless [association.major_model, association.secondary_model].compact.size == 2
26
+ association
27
+ end
28
+
29
+ @associations.each do |association|
30
+ major_model = association.major_model
31
+ secondary_model = association.secondary_model
32
+ case association.relation
33
+ when :has_one then
34
+ property = Record::Property.new(major_model.foreign_id, :int, :foreign, :default => 0)
35
+ secondary_model.properties << property
36
+ when :has_many then
37
+ property = Record::Property.new(major_model.foreign_id, :int, :foreign, :default => 0)
38
+ secondary_model.properties << property
39
+ when :belongs_to then
40
+ property = Record::Property.new(secondary_model.foreign_id, :int, :foreign, :default => 0)
41
+ major_model.properties << property
42
+ end
43
+ end
44
+
45
+ @models.each do |model|
46
+ model.properties.uniq! { |prop| [prop.name] }
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -3,8 +3,9 @@ require 'git'
3
3
  module MetaModel
4
4
  class Command
5
5
  class Build < Command
6
- require 'metamodel/command/build/parser'
6
+ require 'metamodel/command/build/resolver'
7
7
  require 'metamodel/command/build/renderer'
8
+ require 'metamodel/command/build/translator'
8
9
 
9
10
  self.summary = "Build a MetaModel.framework from Metafile"
10
11
  self.description = <<-DESC
@@ -21,7 +22,8 @@ module MetaModel
21
22
  def run
22
23
  UI.section "Building MetaModel.framework in project" do
23
24
  clone_project
24
- parse_template
25
+ models, associations = resolve_template
26
+ @models = compact_associtions_into_models models, associations
25
27
  validate_models
26
28
  render_model_files
27
29
  update_initialize_method
@@ -36,28 +38,24 @@ module MetaModel
36
38
  else
37
39
  UI.section "Cloning MetaModel project into `./metamodel` folder" do
38
40
  Git.clone(config.metamodel_template_uri, 'metamodel', :depth => 1)
39
- UI.message "Using `#{metamodel_xcode_project}` to build module"
41
+ UI.message "Using `#{config.metamodel_xcode_project}` to build module"
40
42
  end
41
43
  end
42
44
  end
43
45
 
44
- def parse_template
45
- parser = Parser.new
46
- @models = parser.parse
46
+ def resolve_template
47
+ resolver = Resolver.new
48
+ resolver.resolve
49
+ end
50
+
51
+ def compact_associtions_into_models(models, associations)
52
+ Translator.new(models, associations).translate
47
53
  end
48
54
 
49
55
  def validate_models
50
56
  existing_types = @models.map { |m| m.properties.map { |property| property.type } }.flatten.uniq
51
57
  unsupported_types = existing_types - supported_types
52
58
  raise Informative, "Unsupported types #{unsupported_types}" unless unsupported_types == []
53
-
54
- @models.each do |main|
55
- main.relation_properties.each do |property|
56
- @models.each do |secondary|
57
- property.relation_model = secondary if property.type == secondary.name
58
- end
59
- end
60
- end
61
59
  end
62
60
 
63
61
  def render_model_files
@@ -65,6 +63,7 @@ module MetaModel
65
63
  Renderer.render(@models)
66
64
  end
67
65
  end
66
+ #
68
67
  def update_initialize_method
69
68
  template = File.read File.expand_path(File.join(File.dirname(__FILE__), "../template/metamodel.swift"))
70
69
  result = ErbalT::render_from_hash(template, { :models => @models })
@@ -0,0 +1,40 @@
1
+ module MetaModel
2
+ module Record
3
+ class Association
4
+ attr_reader :name
5
+ attr_reader :type
6
+ attr_reader :relation
7
+ attr_reader :through
8
+ attr_accessor :major_model
9
+ attr_accessor :secondary_model
10
+
11
+ def initialize(name, major_model, secondary_model, relation, through = nil)
12
+ @name = name.to_s.camelize :lower
13
+ @relation = relation
14
+ @through = through
15
+ @major_model = major_model
16
+ @secondary_model = secondary_model
17
+ end
18
+
19
+ def has_one?
20
+ @relation == :has_one
21
+ end
22
+
23
+ def has_many?
24
+ @relation == :has_many
25
+ end
26
+
27
+ def belongs_to?
28
+ @relation == :belongs_to
29
+ end
30
+
31
+ def type
32
+ case @relation
33
+ when :has_one then secondary_model.name
34
+ when :has_many then secondary_model.name
35
+ when :belongs_to then major_model.name
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,118 @@
1
+ module MetaModel
2
+ module Record
3
+ class Model
4
+ attr_reader :name
5
+ attr_reader :properties
6
+ attr_reader :associations
7
+
8
+ def initialize(name)
9
+ @name = name.to_s.camelize
10
+ @properties = []
11
+ @associations = []
12
+
13
+ validate
14
+ end
15
+
16
+ def properties_exclude_id
17
+ @properties.select { |property| property.name != "id" }
18
+ end
19
+
20
+ def foreign_id
21
+ "#{name}_id".camelize(:lower)
22
+ end
23
+
24
+ def table_name
25
+ name.tableize
26
+ end
27
+
28
+ def relation_name
29
+ "#{name}Relation"
30
+ end
31
+
32
+ def all_properties
33
+ all_properties = properties.clone
34
+ all_properties.push Property.primary_id
35
+ all_properties
36
+ end
37
+
38
+ def validate
39
+ property_keys = @properties.map { |property| property.name }
40
+
41
+ @properties << Property.new(:id, :int, :unique, :default => 0) unless property_keys.include? "id"
42
+ end
43
+
44
+ def hash_value
45
+ self.hash.to_s(16)
46
+ end
47
+
48
+ def property_key_value_pairs(cast = false)
49
+ key_value_pairs_with_property @properties, cast
50
+ end
51
+
52
+ def property_key_value_pairs_without_property(property)
53
+ key_value_pairs_with_property @properties.select { |element| element.name != property }
54
+ end
55
+
56
+ def property_exclude_id_key_value_pairs(cast = false)
57
+ key_value_pairs_with_property properties_exclude_id, cast
58
+ end
59
+
60
+ def property_key_type_pairs
61
+ key_type_pairs_with_property @properties
62
+ end
63
+
64
+ def property_key_type_pairs_without_property(property)
65
+ key_type_pairs_with_property @properties.select { |element| element.name != property }
66
+ end
67
+
68
+ def property_exclude_id_key_type_pairs
69
+ key_type_pairs_with_property properties_exclude_id
70
+ end
71
+
72
+ def build_table
73
+ table = "CREATE TABLE #{table_name}"
74
+ main_sql = @properties.map do |property|
75
+ result = "#{property.name} #{property.database_type}"
76
+ result << " PRIMARY KEY" if property.is_primary?
77
+ result << " UNIQUE" if property.is_unique?
78
+ result << " DEFAULT #{property.default_value}" if property.has_default_value?
79
+ result
80
+ end
81
+ foreign_sql = @properties.map do |property|
82
+ next unless property.is_foreign?
83
+ reference_table_name = property.type.tableize
84
+ "FOREIGN KEY(#{property.name}) REFERENCES #{reference_table_name}(_id)"
85
+ end
86
+
87
+ table + "(_id INTEGER PRIMARY KEY, #{(main_sql + foreign_sql).compact.join(", ")});"
88
+ end
89
+
90
+ private
91
+
92
+ def key_value_pairs_with_property(properties, cast = false)
93
+ properties.map do |property|
94
+ if cast
95
+ "#{property.name}: #{property.type_without_optional}(#{property.name})"
96
+ else
97
+ "#{property.name}: #{property.name}"
98
+ end
99
+ end.join(", ")
100
+ end
101
+
102
+ def key_type_pairs_with_property(properties)
103
+ properties.enum_for(:each_with_index).map do |property, index|
104
+ has_default_value = property.has_default_value?
105
+ default_value = property.type_without_optional == "String" ? "\"#{property.default_value}\"" : property.default_value
106
+
107
+ result = "#{property.name}: #{property.type.to_s}#{if has_default_value then " = " + "#{default_value}" end}"
108
+ if index == 0
109
+ "#{property.name} #{result}"
110
+ else
111
+ result
112
+ end
113
+ end.join(", ")
114
+ end
115
+
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,99 @@
1
+ module MetaModel
2
+ module Record
3
+ class Property
4
+ attr_accessor :name
5
+ attr_reader :type
6
+ attr_reader :modifiers
7
+
8
+ def initialize(json_key, type = :string, *modifiers)
9
+ @name = json_key.to_s.camelize(:lower)
10
+ @type = convert_symbol_to_type type
11
+
12
+ @modifiers = {}
13
+ @modifiers.default = false
14
+
15
+ modifiers.flatten.map do |modifier|
16
+ @modifiers[modifier] = true if modifier.is_a? Symbol
17
+ @modifiers[:default] = modifier[:default] if modifier.is_a? Hash and modifier[:default]
18
+ end
19
+ end
20
+
21
+ class << self
22
+ def primary_id
23
+ property = Property.new(:_id, :int, :primary)
24
+ property.name = :_id
25
+ property
26
+ end
27
+ end
28
+
29
+ def type_without_optional
30
+ return type.to_s[0..-2] if type.to_s.end_with? "?"
31
+ type
32
+ end
33
+
34
+ def database_type
35
+ case type_without_optional
36
+ when "String" then "TEXT"
37
+ when "Int" then "INTEGER"
38
+ when "Bool" then "INTEGER"
39
+ when "Double" then "REAL"
40
+ when "NSDate" then "REAL"
41
+ else raise Informative, "Unsupported type #{self.type}"
42
+ end
43
+ end
44
+
45
+ def real_type
46
+ case type_without_optional
47
+ when "String" then "String"
48
+ when "Int" then "Int64"
49
+ when "Bool" then "Int64"
50
+ when "Double" then "Double"
51
+ when "NSDate" then "Double"
52
+ else raise Informative, "Unsupported type #{self.type}"
53
+ end
54
+ end
55
+
56
+ def convert_symbol_to_type(symbol)
57
+ case symbol
58
+ when :int then "Int"
59
+ when :double then "Double"
60
+ when :bool then "Bool"
61
+ when :string then "String"
62
+ when :date then "NSDate"
63
+ else symbol.to_s.camelize
64
+ end
65
+ end
66
+
67
+
68
+ def is_array?
69
+ @type.pluralize == str
70
+ end
71
+
72
+ def is_unique?
73
+ @modifiers.include? :unique
74
+ end
75
+
76
+ def is_primary?
77
+ @modifiers.include? :primary
78
+ end
79
+
80
+ def is_foreign?
81
+ @modifiers.include? :foreign
82
+ end
83
+
84
+ def is_optional?
85
+ @type.to_s.end_with? "?"
86
+ end
87
+
88
+ def has_default_value?
89
+ !!@modifiers[:default]
90
+ end
91
+
92
+ def default_value
93
+ has_default_value? ? modifiers[:default] : ""
94
+ end
95
+
96
+ private
97
+ end
98
+ end
99
+ end
@@ -1,4 +1,4 @@
1
- public struct <%= model.name %> {
1
+ public struct <%= model.name %> {
2
2
  <% model.properties.each do |property| %><%= """public var #{property.name}: #{property.type}""" %>
3
3
  <% end %>
4
4
  static let tableName = "<%= model.table_name %>"
@@ -1,23 +1,23 @@
1
- <% model.relation_properties.each do |property| %><% if property.has_many? %>
1
+ <% model.associations.each do |association| %><% if association.has_many? %>
2
2
  <%= """public extension #{model.name} {
3
- func append#{property.type}(element: #{property.type}) {
3
+ func append#{association.type}(element: #{association.type}) {
4
4
  var element = element
5
5
  element.update(#{model.foreign_id}: id)
6
6
  }
7
7
 
8
- func create#{property.type}(#{property.relation_model.property_key_type_pairs_without_property model.foreign_id}) -> #{property.type}? {
9
- return #{property.type}.create(#{property.relation_model.property_key_value_pairs_without_property model.foreign_id}, #{model.foreign_id}: self.id)
8
+ func create#{association.type}(#{association.secondary_model.property_key_type_pairs_without_property model.foreign_id}) -> #{association.type}? {
9
+ return #{association.type}.create(#{association.secondary_model.property_key_value_pairs_without_property model.foreign_id}, #{model.foreign_id}: self.id)
10
10
  }
11
11
 
12
- func delete#{property.type}(id: Int) {
13
- #{property.type}.filter(.#{model.foreign_id}, value: id).findBy(id: id).first?.delete
12
+ func delete#{association.type}(id: Int) {
13
+ #{association.type}.filter(.#{model.foreign_id}, value: id).findBy(id: id).first?.delete
14
14
  }
15
- var #{property.name}: [#{property.type}] {
15
+ var #{association.name}: [#{association.type}] {
16
16
  get {
17
- return #{property.type}.filter(.id, value: id).result
17
+ return #{association.type}.filter(.id, value: id).result
18
18
  }
19
19
  set {
20
- #{property.name}.forEach { (element) in
20
+ #{association.name}.forEach { (element) in
21
21
  var element = element
22
22
  element.update(#{model.foreign_id}: 0)
23
23
  }
@@ -27,28 +27,28 @@
27
27
  }
28
28
  }
29
29
  }
30
- }""" %><% elsif property.belongs_to? %>
30
+ }""" %><% elsif association.belongs_to? %>
31
31
  <%= """public extension #{model.name} {
32
- var #{property.name}: #{property.type}? {
32
+ var #{association.name}: #{association.type}? {
33
33
  get {
34
- return #{property.type}.find(id)
34
+ return #{association.type}.find(id)
35
35
  }
36
36
  set {
37
37
  guard let newValue = newValue else { return }
38
- update(#{property.type.camelize(:lower)}Id: newValue.id)
38
+ update(#{association.secondary_model.foreign_id}: newValue.id)
39
39
  }
40
40
  }
41
41
 
42
- }""" %><% elsif property.has_one? %>
42
+ }""" %><% elsif association.has_one? %>
43
43
  <%= """public extension #{model.name} {
44
- var #{property.name}: #{property.type}? {
44
+ var #{association.name}: #{association.type}? {
45
45
  get {
46
- return #{property.type}.find(id)
46
+ return #{association.type}.find(id)
47
47
  }
48
48
  set {
49
- #{property.type}.filter(.#{model.name.camelize(:lower)}Id, value: id).deleteAll
49
+ #{association.type}.filter(.#{model.foreign_id}, value: id).deleteAll
50
50
  guard var newValue = newValue else { return }
51
- newValue.update(articleId: id)
51
+ newValue.update(#{model.foreign_id}: id)
52
52
  }
53
53
  }
54
54
  }"""%><% end %><% end %>
@@ -3,6 +3,18 @@ public extension <%= model.name %> {
3
3
  get { return <%= model.relation_name %>() }
4
4
  }
5
5
 
6
+ static var first: <%= model.name %>? {
7
+ get {
8
+ return <%= model.relation_name %>().orderBy(.id, asc: true).first
9
+ }
10
+ }
11
+
12
+ static var last: <%= model.name %>? {
13
+ get {
14
+ return <%= model.relation_name %>().orderBy(.id, asc: false).first
15
+ }
16
+ }
17
+
6
18
  static func first(length: UInt) -> <%= model.relation_name %> {
7
19
  return <%= model.relation_name %>().orderBy(.id, asc: true).limit(length)
8
20
  }
@@ -18,8 +30,8 @@ public extension <%= model.name %> {
18
30
  static func findBy(id id: Int) -> <%= model.name %>? {
19
31
  return <%= model.relation_name %>().findBy(id: id).first
20
32
  }
21
-
22
- <% model.properties_exclude_id.each do |property| %><%= """static func findBy(#{property.name} #{property.name}: #{property.type_without_optional}) -\> #{model.name}? {
33
+ <% model.properties_exclude_id.each do |property| %><%= """
34
+ static func findBy(#{property.name} #{property.name}: #{property.type_without_optional}) -\> #{model.name}? {
23
35
  return #{model.relation_name}().findBy(#{property.name}: #{property.name}).first
24
36
  }
25
37
  """ %><% end %>
@@ -1,5 +1,5 @@
1
1
  module MetaModel
2
2
  # The version of the MetaModel command line tool.
3
3
  #
4
- VERSION = '0.1.5' unless defined? MetaModel::VERSION
4
+ VERSION = '0.1.6' unless defined? MetaModel::VERSION
5
5
  end
data/lib/metamodel.rb CHANGED
@@ -14,7 +14,6 @@ module MetaModel
14
14
 
15
15
  require 'pathname'
16
16
  require 'active_support/inflector'
17
- require 'active_support/core_ext/string'
18
17
 
19
18
  require 'metamodel/version'
20
19
  require 'metamodel/config'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metamodel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Draveness Zuo
@@ -148,14 +148,16 @@ files:
148
148
  - lib/metamodel.rb
149
149
  - lib/metamodel/command.rb
150
150
  - lib/metamodel/command/build.rb
151
- - lib/metamodel/command/build/parser.rb
152
151
  - lib/metamodel/command/build/renderer.rb
152
+ - lib/metamodel/command/build/resolver.rb
153
+ - lib/metamodel/command/build/translator.rb
153
154
  - lib/metamodel/command/clean.rb
154
155
  - lib/metamodel/command/generate.rb
155
156
  - lib/metamodel/command/init.rb
156
157
  - lib/metamodel/config.rb
157
- - lib/metamodel/model/model.rb
158
- - lib/metamodel/model/property.rb
158
+ - lib/metamodel/record/association.rb
159
+ - lib/metamodel/record/model.rb
160
+ - lib/metamodel/record/property.rb
159
161
  - lib/metamodel/template/attributes.swift
160
162
  - lib/metamodel/template/file_header.swift
161
163
  - lib/metamodel/template/foreign_key.swift
@@ -189,7 +191,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
189
191
  version: '0'
190
192
  requirements: []
191
193
  rubyforge_project:
192
- rubygems_version: 2.5.1
194
+ rubygems_version: 2.6.4
193
195
  signing_key:
194
196
  specification_version: 4
195
197
  summary: The Cocoa models generator.
@@ -1,67 +0,0 @@
1
- module MetaModel
2
- class Command
3
- class Build
4
- class Parser
5
-
6
- include Config::Mixin
7
-
8
- require 'metamodel/model/model'
9
- require 'metamodel/model/property'
10
- require 'metamodel/command/build/renderer'
11
-
12
- def initialize
13
- @models = []
14
- end
15
-
16
- def parse
17
- UI.section "Analyzing Metafile" do
18
- metafile_path = config.metafile_path
19
- eval File.read(metafile_path)
20
- end
21
- @models
22
- end
23
-
24
- private
25
-
26
- def metamodel_version(version)
27
- raise Informative,
28
- "Meta file #{version} not matched with current metamodel version #{VERSION}" if version != VERSION
29
- end
30
-
31
- def define(model_name)
32
- UI.message '-> '.green + "Resolving `#{model_name.to_s.camelize}`"
33
- @models << Model.new(model_name)
34
- yield
35
- end
36
-
37
- def attr(key, type = :string, *args)
38
- current_model.properties << Property.new(key, type, args)
39
- end
40
-
41
- def has_one(name, model = nil)
42
- model = name.to_s.singularize.camelize if model.nil?
43
- current_model.relation_properties << Property.new(name, model, :has_one)
44
- end
45
-
46
- def has_many(name, model = nil)
47
- model = name.to_s.singularize.camelize if model.nil?
48
- property = Property.new(name, model, :has_many)
49
- raise Informative, "Property type in has_many relation can't be optional" if property.is_optional?
50
- current_model.relation_properties << property
51
- end
52
-
53
- def belongs_to(name, model = nil)
54
- model = name.to_s.singularize.camelize if model.nil?
55
- current_model.relation_properties << Property.new(name, model, :belongs_to)
56
- current_model.properties << Property.new("#{name}_id".camelize, "Int", :foreign, :default => 0)
57
- end
58
-
59
- private
60
-
61
- def current_model
62
- @models.last
63
- end
64
- end
65
- end
66
- end
67
- end
@@ -1,116 +0,0 @@
1
- module MetaModel
2
-
3
- class Model
4
- attr_reader :name
5
- attr_reader :properties
6
- attr_reader :relation_properties
7
-
8
- def initialize(name)
9
- @name = name.to_s
10
- @properties = []
11
- @relation_properties = []
12
-
13
- validate
14
- end
15
-
16
- def properties_exclude_id
17
- @properties.select { |property| property.name != "id" }
18
- end
19
-
20
- def foreign_id
21
- "#{name}_id".camelize(:lower)
22
- end
23
-
24
- def table_name
25
- name.tableize
26
- end
27
-
28
- def relation_name
29
- "#{name}Relation"
30
- end
31
-
32
- def all_properties
33
- all_properties = properties.clone
34
- all_properties.push Property.primary_id
35
- all_properties
36
- end
37
-
38
- def validate
39
- property_keys = @properties.map { |property| property.name }
40
-
41
- unless property_keys.include? "id"
42
- property_id = Property.new(:id, :int, :unique, :default => 0)
43
- @properties << property_id
44
- end
45
- end
46
-
47
- def hash_value
48
- self.hash.to_s(16)
49
- end
50
-
51
- def property_key_value_pairs(cast = false)
52
- key_value_pairs_with_property @properties, cast
53
- end
54
-
55
- def property_key_value_pairs_without_property(property)
56
- key_value_pairs_with_property @properties.select { |element| element.name != property }
57
- end
58
-
59
- def property_exclude_id_key_value_pairs(cast = false)
60
- key_value_pairs_with_property properties_exclude_id, cast
61
- end
62
-
63
- def property_key_type_pairs
64
- key_type_pairs_with_property @properties
65
- end
66
-
67
- def property_key_type_pairs_without_property(property)
68
- key_type_pairs_with_property @properties.select { |element| element.name != property }
69
- end
70
-
71
- def property_exclude_id_key_type_pairs
72
- key_type_pairs_with_property properties_exclude_id
73
- end
74
-
75
- def build_table
76
- table = "CREATE TABLE #{table_name}"
77
- main_sql = @properties.map do |property|
78
- result = "#{property.name} #{property.database_type}"
79
- result << " PRIMARY KEY" if property.is_primary?
80
- result << " UNIQUE" if property.is_unique?
81
- result << " DEFAULT #{property.default_value}" if property.has_default_value?
82
- result
83
- end
84
- foreign_sql = @properties.map do |property|
85
- next unless property.is_foreign?
86
- reference_table_name = property.type.tableize
87
- "FOREIGN KEY(#{property.name}) REFERENCES #{reference_table_name}(_id)"
88
- end
89
-
90
- table + "(_id INTEGER PRIMARY KEY, #{(main_sql + foreign_sql).compact.join(", ")});"
91
-
92
- end
93
-
94
- private
95
-
96
- def key_value_pairs_with_property(properties, cast = false)
97
- properties.map do |property|
98
- if cast
99
- "#{property.name}: #{property.type_without_optional}(#{property.name})"
100
- else
101
- "#{property.name}: #{property.name}"
102
- end
103
- end.join(", ")
104
- end
105
-
106
- def key_type_pairs_with_property(properties)
107
- properties.map do |property|
108
- has_default_value = property.has_default_value?
109
- default_value = property.type_without_optional == "String" ? "\"#{property.default_value}\"" : property.default_value
110
- "#{property.name} #{property.name}: #{property.type.to_s}#{if has_default_value then " = " + "#{default_value}" end}"
111
- end.join(", ")
112
- end
113
-
114
- end
115
-
116
- end
@@ -1,114 +0,0 @@
1
- module MetaModel
2
-
3
- class Property
4
- attr_accessor :name
5
- attr_accessor :relation_model
6
- attr_reader :json_key
7
- attr_reader :type
8
- attr_reader :modifiers
9
-
10
- def initialize(json_key, type = :string, *modifiers)
11
- @json_key = json_key
12
- @name = json_key.to_s.camelize(:lower)
13
- @type = convert_symbol_to_type type
14
-
15
- @modifiers = {}
16
- @modifiers.default = false
17
-
18
- modifiers.flatten.map do |modifier|
19
- @modifiers[modifier] = true if modifier.is_a? Symbol
20
- @modifiers[:default] = modifier[:default] if modifier.is_a? Hash and modifier[:default]
21
- end
22
- end
23
-
24
- class << self
25
- def primary_id
26
- property = Property.new(:_id, :int, :primary)
27
- property.name = :_id
28
- property
29
- end
30
- end
31
-
32
- def type_without_optional
33
- return type.to_s[0..-2] if type.to_s.end_with? "?"
34
- type
35
- end
36
-
37
- def database_type
38
- case type_without_optional
39
- when "String" then "TEXT"
40
- when "Int" then "INTEGER"
41
- when "Bool" then "INTEGER"
42
- when "Double" then "REAL"
43
- when "NSDate" then "REAL"
44
- else raise Informative, "Unsupported type #{self.type}"
45
- end
46
- end
47
-
48
- def real_type
49
- case type_without_optional
50
- when "String" then "String"
51
- when "Int" then "Int64"
52
- when "Bool" then "Int64"
53
- when "Double" then "Double"
54
- when "NSDate" then "Double"
55
- else raise Informative, "Unsupported type #{self.type}"
56
- end
57
- end
58
-
59
- def convert_symbol_to_type(symbol)
60
- case symbol
61
- when :int then "Int"
62
- when :double then "Double"
63
- when :bool then "Bool"
64
- when :string then "String"
65
- when :date then "NSDate"
66
- else symbol.to_s.camelize
67
- end
68
- end
69
-
70
-
71
- def is_array?
72
- @type.pluralize == str
73
- end
74
-
75
- def is_unique?
76
- @modifiers.include? :unique
77
- end
78
-
79
- def is_primary?
80
- @modifiers.include? :primary
81
- end
82
-
83
- def is_foreign?
84
- @modifiers.include? :foreign
85
- end
86
-
87
- def is_optional?
88
- @type.to_s.end_with? "?"
89
- end
90
-
91
- def has_one?
92
- @modifiers.include? :has_one
93
- end
94
-
95
- def has_many?
96
- @modifiers.include? :has_many
97
- end
98
-
99
- def belongs_to?
100
- @modifiers.include? :belongs_to
101
- end
102
-
103
- def has_default_value?
104
- !!@modifiers[:default]
105
- end
106
-
107
- def default_value
108
- has_default_value? ? modifiers[:default] : ""
109
- end
110
-
111
- private
112
- end
113
-
114
- end