metamodel 0.1.5 → 0.1.6

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