gyro 0.4.0

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