gyro 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,31 @@
1
+ =begin
2
+ Copyright 2016 - Niji
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ =end
16
+
17
+ module Gyro
18
+
19
+ def self.find_xcdatamodel(dir)
20
+ Dir.chdir(dir) do
21
+ files = Dir.glob('*.xcdatamodel')
22
+ files.first.nil? ? nil : File.expand_path(files.first, dir)
23
+ end
24
+ end
25
+
26
+ def self.write_file_with_name(dir, name_file, content)
27
+ file_path = File.expand_path(name_file, dir)
28
+ File.write(file_path, content)
29
+ end
30
+
31
+ end
@@ -0,0 +1,68 @@
1
+ =begin
2
+ Copyright 2016 - Niji
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ =end
16
+
17
+ module Gyro
18
+ module Log
19
+
20
+ def self.title(str) # bg yellow
21
+ puts "\e[44;37m#{str}\e[0m"
22
+ end
23
+
24
+ def self.error(str)
25
+ puts "\e[1;31m! #{str}\e[0m"
26
+ end
27
+
28
+ def self.info(str)
29
+ puts "\e[1;33m> #{str}\e[0m"
30
+ end
31
+
32
+ def self.success(str)
33
+ puts "\e[1;32m√ #{str}\e[0m"
34
+ end
35
+
36
+ def self.prompt(str, url = nil)
37
+ prompt = "\e[1;36m ! #{str} [y/n]?\e[0m "
38
+ url_info = ' '*10 + "\e[0;37m (use '?' to show in browser)\e[0m"
39
+ print prompt
40
+ print "#{url_info}\r#{prompt}" if url
41
+
42
+ answer = get_char do |c|
43
+ `open '#{url}'` if url && (c == '?')
44
+ "yn\003".include?(c.downcase) # \003 = ctrl-C
45
+ end
46
+ puts answer + (url ? ' '*url_info.length : '')
47
+ answer.downcase == 'y'
48
+ end
49
+
50
+ private ######################################################################
51
+
52
+ def self.get_char
53
+ stop = false
54
+ typed_char = ''
55
+ begin
56
+ system('stty raw -echo')
57
+ until stop
58
+ typed_char = STDIN.getc.chr
59
+ stop = yield typed_char
60
+ end
61
+ ensure
62
+ system('stty -raw echo')
63
+ end
64
+ typed_char
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,23 @@
1
+ =begin
2
+ Copyright 2016 - Niji
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ =end
16
+
17
+ module Raise
18
+
19
+ def self.error(str)
20
+ raise "\e[1;31m! #{str}\e[0m"
21
+ end
22
+
23
+ end
@@ -0,0 +1,58 @@
1
+ =begin
2
+ Copyright 2016 - Niji
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ =end
16
+
17
+ class String
18
+
19
+ def delete_objc_prefix
20
+ i = 0
21
+ while i < self.length - 1 and /[[:upper:]]/.match(self[i+1])
22
+ i += 1
23
+ end
24
+ self[i..self.length]
25
+ end
26
+
27
+ def delete_inverse_suffix
28
+ self.gsub('_', '')
29
+ end
30
+
31
+ def capitalize_first_letter
32
+ self.slice(0, 1).capitalize + self.slice(1..-1)
33
+ end
34
+
35
+ def camel_case
36
+ words = self.scan(/[A-Z][a-z]+/)
37
+ words.map!(&:upcase)
38
+ words.join('_')
39
+ end
40
+
41
+ def underscore
42
+ self.gsub(/::/, '/').
43
+ gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
44
+ gsub(/([a-z\d])([A-Z])/, '\1_\2').
45
+ tr('-', '_').
46
+ downcase
47
+ end
48
+
49
+ def add_quotes
50
+ "\"" + self + "\""
51
+ end
52
+
53
+ def add_parentheses
54
+ '(' + self + ')'
55
+ end
56
+
57
+
58
+ end
@@ -0,0 +1,98 @@
1
+ =begin
2
+ Copyright 2016 - Niji
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ =end
16
+
17
+ module Gyro
18
+ module XCDataModel
19
+ module Parser
20
+
21
+ class Attribute
22
+
23
+ attr_accessor :entity_name, :name, :type, :optional, :indexed, :default
24
+ attr_accessor :realm_ignored, :realm_read_only, :enum_type, :enum_values
25
+ attr_accessor :json_key_path, :json_values, :transformer, :comment, :support_annotation
26
+
27
+ alias_method :optional?, :optional
28
+ alias_method :indexed?, :indexed
29
+ alias_method :realm_ignored?, :realm_ignored
30
+
31
+ def initialize (attribute_xml, entity_name)
32
+ @entity_name = entity_name
33
+ @name = attribute_xml.xpath('@name').to_s
34
+ @optional = attribute_xml.xpath('@optional').to_s == 'YES' ? true : false
35
+ @indexed = attribute_xml.xpath('@indexed').to_s == 'YES' ? true : false
36
+ @default = attribute_xml.xpath('@defaultValueString').to_s
37
+ @type = attribute_xml.xpath('@attributeType').to_s.downcase.gsub(' ', '_').to_sym
38
+ @realm_ignored = attribute_xml.xpath(USERINFO_VALUE%['realmIgnored']).to_s.empty? ? false : true
39
+ @realm_read_only = attribute_xml.xpath(USERINFO_VALUE%['realmReadOnly']).to_s
40
+ @enum_type = attribute_xml.xpath(USERINFO_VALUE%['enumType']).to_s
41
+ @enum_values = attribute_xml.xpath(USERINFO_VALUE%['enumValues']).to_s.split(',')
42
+ @json_key_path = attribute_xml.xpath(USERINFO_VALUE%['JSONKeyPath']).to_s
43
+ @json_values = attribute_xml.xpath(USERINFO_VALUE%['JSONValues']).to_s.split(',')
44
+ @transformer = attribute_xml.xpath(USERINFO_VALUE%['transformer']).to_s.strip
45
+ @comment = attribute_xml.xpath(USERINFO_VALUE%['comment']).to_s
46
+ @support_annotation = attribute_xml.xpath(USERINFO_VALUE%['supportAnnotation']).to_s
47
+ search_for_error
48
+ end
49
+
50
+ def enum?
51
+ !@enum_type.empty?
52
+ end
53
+
54
+ def read_only?
55
+ !@realm_read_only.empty?
56
+ end
57
+
58
+ def has_default?
59
+ !@default.empty?
60
+ end
61
+
62
+ def to_s
63
+ "\tAttribute => name=#{@name} | type=#{@type} | optional=#{@optional} | default=#{@default} | indexed=#{@indexed}\n"
64
+ end
65
+
66
+ def is_decimal?
67
+ @type == :decimal or @type == :double or @type == :float
68
+ end
69
+
70
+ def is_integer?
71
+ @type == :integer_16 or @type == :integer_32 or @type == :integer_64
72
+ end
73
+
74
+ def is_number?
75
+ is_decimal? or is_integer?
76
+ end
77
+
78
+ def is_bool?
79
+ @type == :boolean
80
+ end
81
+
82
+ def need_transformer?
83
+ !@enum_type.empty? or @type == :boolean or @type == :date or !@transformer.empty?
84
+ end
85
+
86
+ private ################################################################
87
+
88
+ def search_for_error
89
+ Raise::error("The attribute \"%s\" from \"%s\" has no type - please fix it"%[@name, @entity_name]) if @type == :undefined || @type.empty?
90
+ Raise::error("The attribute \"%s\" from \"%s\" is enum with incorrect type (not Integer) - please fix it"%[@name, @entity_name]) if !@enum_type.empty? and !@enum_values.empty? and !is_integer?
91
+ Raise::error("The attribute \"%s\" from \"%s\" is wrongly annotated: when declaring an type with enum and JSONKeyPath, you must have the same number of items in the 'enumValues' and 'JSONValues' annotations - please fix it"%[@name, @entity_name]) if !@json_key_path.empty? and !@enum_values.empty? and @enum_values.size != @json_values.size
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,234 @@
1
+ =begin
2
+ Copyright 2016 - Niji
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ =end
16
+
17
+ require 'nokogiri'
18
+ require 'set'
19
+ require File.expand_path('relationship', File.dirname(__FILE__))
20
+ require File.expand_path('attribute', File.dirname(__FILE__))
21
+
22
+ module Gyro
23
+ module XCDataModel
24
+ module Parser
25
+ class Entity
26
+
27
+ attr_accessor :name, :parent, :abstract, :attributes, :relationships, :identity_attribute, :comment
28
+ alias_method :abstract?, :abstract
29
+
30
+ def initialize(entity_xml)
31
+ @name = entity_xml.xpath('@name').to_s
32
+ @parent = entity_xml.xpath('@parentEntity').to_s
33
+ @abstract = entity_xml.xpath('@isAbstract').to_s == 'YES' ? true : false
34
+ @clean = false
35
+ @identity_attribute = entity_xml.xpath(USERINFO_VALUE%['identityAttribute']).to_s
36
+ @comment = entity_xml.xpath(USERINFO_VALUE%['comment']).to_s
37
+ @attributes = Hash.new
38
+ @relationships = Hash.new
39
+ load_entity(entity_xml)
40
+ end
41
+
42
+ def to_s
43
+ str = "\nEntity => #{@name}\n"
44
+ @attributes.each do |_, attribute|
45
+ str += attribute.to_s
46
+ end
47
+ @relationships.each do |_, relationship|
48
+ str += relationship.to_s
49
+ end
50
+ str
51
+ end
52
+
53
+ def used_as_list_by_other?(entities)
54
+ entities.each do |_, entity|
55
+ entity.relationships.each do |_, relationship|
56
+ return true if relationship.inverse_type == @name and relationship.type == :to_many
57
+ end
58
+ end
59
+ false
60
+ end
61
+
62
+ def has_list_attributes?(inverse = false)
63
+ @relationships.each do |_, relationship|
64
+ return true if relationship.type == :to_many and (!inverse ? !relationship.inverse? : true)
65
+ end
66
+ false
67
+ end
68
+
69
+ def has_no_inverse_relationship?
70
+ @relationships.each do |_, relationship|
71
+ return true unless relationship.inverse?
72
+ end
73
+ false
74
+ end
75
+
76
+ def has_ignored?
77
+ @attributes.each do |_, attribute|
78
+ return true if attribute.realm_ignored?
79
+ end
80
+ @relationships.each do |_, relationship|
81
+ return true if relationship.realm_ignored?
82
+ end
83
+ false
84
+ end
85
+
86
+ def has_primary_key?
87
+ !@identity_attribute.empty?
88
+ end
89
+
90
+ def has_required?
91
+ @attributes.each do |_, attribute|
92
+ return true if is_required?(attribute)
93
+ end
94
+ false
95
+ end
96
+
97
+ def is_required?(attribute)
98
+ unless attribute.optional?
99
+ return true unless self.has_primary_key?
100
+ return true if self.has_primary_key? && !attribute.name.eql?(identity_attribute)
101
+ end
102
+ false
103
+ end
104
+
105
+ def has_default_value?(attribute = @identity_attribute)
106
+ attribute.name != @identity_attribute
107
+ end
108
+
109
+ def has_indexed_attributes?
110
+ @attributes.each do |_, attribute|
111
+ return true if attribute.indexed?
112
+ end
113
+ false
114
+ end
115
+
116
+ def has_json_key_path?
117
+ @attributes.each do |_, attribute|
118
+ return true unless attribute.json_key_path.empty?
119
+ end
120
+ @relationships.each do |_, relationship|
121
+ return true if !relationship.inverse? and !relationship.json_key_path.empty?
122
+ end
123
+ false
124
+ end
125
+
126
+ def has_enum_attributes?
127
+ @attributes.each do |_, attribute|
128
+ return true unless attribute.enum_type.empty?
129
+ end
130
+ false
131
+ end
132
+
133
+ def transformers
134
+ transformers = Set.new
135
+ @attributes.each do |_, attribute|
136
+ transformers.add attribute.transformer unless attribute.transformer.empty?
137
+ end
138
+ transformers
139
+ end
140
+
141
+ def has_custom_transformers?
142
+ @attributes.each do |_, attribute|
143
+ return true unless attribute.transformer.empty?
144
+ end
145
+ false
146
+ end
147
+
148
+ def need_transformer?
149
+ has_enum_attributes? || has_bool_attributes? || has_custom_transformers? || has_date_attribute?
150
+ end
151
+
152
+ def has_bool_attributes?
153
+ has_bool_attributes = false
154
+ @attributes.each do |_, attribute|
155
+ has_bool_attributes = true if attribute.type == :boolean
156
+ end
157
+ has_bool_attributes
158
+ end
159
+
160
+ def has_number_attributes?
161
+ has_number_attributes = false
162
+ @attributes.each do |_, attribute|
163
+ if attribute.enum_type.empty?
164
+ case attribute.type
165
+ when :integer_16 then
166
+ has_number_attributes = true
167
+ when :integer_32 then
168
+ has_number_attributes = true
169
+ when :integer_64 then
170
+ has_number_attributes = true
171
+ when :decimal then
172
+ has_number_attributes = true
173
+ when :double then
174
+ has_number_attributes = true
175
+ when :float then
176
+ has_number_attributes = true
177
+ else
178
+ has_number_attributes = false
179
+ end
180
+ break if has_number_attributes
181
+ end
182
+ end
183
+ has_number_attributes
184
+ end
185
+
186
+ def has_date_attribute?
187
+ @attributes.each do |_, attribute|
188
+ return true if attribute.type == :date
189
+ end
190
+ false
191
+ end
192
+
193
+ def has_list_relationship?
194
+ @relationships.each do |_, relationship|
195
+ return true unless relationship.destination.empty?
196
+ end
197
+ false
198
+ end
199
+
200
+ def has_only_inverse?
201
+ nb_inverses = 0
202
+ @relationships.each do |_, relationship|
203
+ nb_inverses+=1 if relationship.inverse?
204
+ end
205
+ nb_inverses==@relationships.size
206
+ end
207
+
208
+ private ################################################################
209
+
210
+ def load_entity(entity_xml)
211
+ load_attributes(entity_xml)
212
+ load_relationships(entity_xml)
213
+ end
214
+
215
+ def load_attributes(entity_xml)
216
+ entity_xml.xpath('attribute').each do |node|
217
+ attribute = Attribute.new(node, @name)
218
+ if attribute.type != 'Transformable'
219
+ @attributes[attribute.name] = attribute
220
+ end
221
+ end
222
+ end
223
+
224
+ def load_relationships(entity_xml)
225
+ entity_xml.xpath('relationship').each do |node|
226
+ relationship = Relationship.new(node, @name)
227
+ @relationships[relationship.name] = relationship
228
+ end
229
+ end
230
+ end
231
+
232
+ end
233
+ end
234
+ end