metamodel 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,122 @@
1
+ //
2
+ // <%= model.name %>.swift
3
+ // MetaModel
4
+ //
5
+ // Created by MetaModel script.
6
+ //
7
+
8
+ import Foundation
9
+ import SQLite
10
+
11
+ public struct <%= model.name %> {
12
+ public let id: Int
13
+ <% model.properties_exclude_id.each do |property| %>
14
+ <%= """public var #{property.key}: #{property.type} {
15
+ didSet { try! db.run(itself.update(#{model.name}.#{property.key} <- #{property.key})) }
16
+ }""" %>
17
+ <% end %>
18
+ }
19
+
20
+ extension <%= model.name %>: Recordable {
21
+ public init(record: SQLite.Row) {
22
+ self.init(<%= model.properties.map { |property| "#{property.key}: record[#{model.name}.#{property.key}]" }.join(", ") %>)
23
+ }
24
+ }
25
+
26
+ extension <%= model.name %> {
27
+ static let table = Table("<%= "#{model.table_name}" %>")
28
+ <% model.properties.each do |property| %><%= "static let #{property.key} = Expression<#{property.type}>(\"#{property.key}\")" %>
29
+ <% end %>
30
+ var itself: QueryType { get { return <%= model.name %>.table.filter(<%= model.name %>.id == self.id) } }
31
+
32
+ static func createTable() {
33
+ let _ = try? db.run(table.create { t in
34
+ <%= model.build_table.chop %>
35
+ })
36
+ }
37
+ }
38
+
39
+ public extension <%= model.name %> {
40
+ static func deleteAll() {
41
+ let _ = try? db.run(<%= model.name %>.table.delete())
42
+ }
43
+
44
+ static func count() -> Int {
45
+ return db.scalar(<%= model.name %>.table.count)
46
+ }
47
+
48
+ static func create(<%= model.properties.map { |property| "#{property.key}: #{property.type}" }.join(", ") %>) -> <%= model.name %> {
49
+ let insert = <%= model.name %>.table.insert(<%= model.properties.map { |property| "#{model.name}.#{property.key} <- #{property.key}" }.join(", ") %>)
50
+ let _ = try? db.run(insert)
51
+ return <%= model.name %>(<%= model.properties.map { |property| "#{property.key}: #{property.key}" }.join(", ") %>)
52
+ }
53
+
54
+ }
55
+
56
+ public extension <%= model.name %> {
57
+ func delete() {
58
+ try! db.run(itself.delete())
59
+ }
60
+ <% model.properties_exclude_id.each do |property| %>
61
+ <%= """mutating func update(#{property.key} #{property.key}: #{property.type}) -> #{model.name} {
62
+ self.#{property.key} = #{property.key}
63
+ return self
64
+ }""" %>
65
+ <% end %>
66
+ }
67
+
68
+
69
+ public extension <%= model.name %> {
70
+ static private func findAll(query: QueryType) -> [<%= model.name %>] {
71
+ var result: [<%= model.name %>] = []
72
+ for record in try! db.prepare(query) {
73
+ result.append(<%= model.name %>(record: record))
74
+ }
75
+ return result
76
+ }
77
+
78
+ static var all: <%= model.relation_name %> {
79
+ get {
80
+ return <%= model.relation_name %>()
81
+ }
82
+ }
83
+ <% model.properties.each do |property| %>
84
+ <%= """static func findBy(#{property.key} #{property.key}: #{property.type}) -> #{model.relation_name} {
85
+ return #{model.relation_name}().findBy(#{property.key}: #{property.key})
86
+ }""" %>
87
+ <% end %>
88
+ static func limit(length: Int, offset: Int = 0) -> <%= model.relation_name %> {
89
+ return <%= model.relation_name %>().limit(length, offset: offset)
90
+ }
91
+
92
+ static func group(params: Expressible...) -> <%= model.relation_name %> {
93
+ return <%= model.relation_name %>().group(params)
94
+ }
95
+ }
96
+
97
+ public class <%= model.relation_name %>: Relation<<%= model.name %>> {
98
+ init() {
99
+ super.init(query: <%= model.name %>.table)
100
+ }
101
+
102
+ <% model.properties.each do |property| %>
103
+ <%= """public func findBy(#{property.key} #{property.key}: #{property.type}) -> Self {
104
+ query = query.filter(#{model.name}.#{property.key} == #{property.key})
105
+ return self
106
+ }""" %>
107
+ <% end %>
108
+ public func limit(length: Int, offset: Int = 0) -> Self {
109
+ query = query.limit(length, offset: offset)
110
+ return self
111
+ }
112
+
113
+ public func group(params: Expressible...) -> Self {
114
+ return group(params)
115
+ }
116
+
117
+ public func group(params: [Expressible]) -> Self {
118
+ query = query.group(params)
119
+ return self
120
+ }
121
+
122
+ }
@@ -0,0 +1,411 @@
1
+ module MetaModel
2
+
3
+ module UserInterface
4
+ require 'colored'
5
+
6
+ @title_colors = %w( yellow green )
7
+ @title_level = 0
8
+ @indentation_level = 2
9
+ @treat_titles_as_messages = false
10
+ @warnings = []
11
+
12
+ class << self
13
+ include Config::Mixin
14
+
15
+ attr_accessor :indentation_level
16
+ attr_accessor :title_level
17
+ attr_accessor :warnings
18
+
19
+ # @return [IO] IO object to which UI output will be directed.
20
+ #
21
+ attr_accessor :output_io
22
+
23
+ # @return [Bool] Whether the wrapping of the strings to the width of the
24
+ # terminal should be disabled.
25
+ #
26
+ attr_accessor :disable_wrap
27
+ alias_method :disable_wrap?, :disable_wrap
28
+
29
+ # Prints a title taking an optional verbose prefix and
30
+ # a relative indentation valid for the UI action in the passed
31
+ # block.
32
+ #
33
+ # In verbose mode titles are printed with a color according
34
+ # to their level. In normal mode titles are printed only if
35
+ # they have nesting level smaller than 2.
36
+ #
37
+ # @todo Refactor to title (for always visible titles like search)
38
+ # and sections (titles that represent collapsible sections).
39
+ #
40
+ # @param [String] title
41
+ # The title to print
42
+ #
43
+ # @param [String] verbose_prefix
44
+ # See #message
45
+ #
46
+ # @param [FixNum] relative_indentation
47
+ # The indentation level relative to the current,
48
+ # when the message is printed.
49
+ #
50
+ def section(title, verbose_prefix = '', relative_indentation = 0)
51
+ if config.verbose?
52
+ title(title, verbose_prefix, relative_indentation)
53
+ elsif title_level < 1
54
+ puts title
55
+ end
56
+
57
+ self.indentation_level += relative_indentation
58
+ self.title_level += 1
59
+ yield if block_given?
60
+ self.indentation_level -= relative_indentation
61
+ self.title_level -= 1
62
+ end
63
+
64
+ # In verbose mode it shows the sections and the contents.
65
+ # In normal mode it just prints the title.
66
+ #
67
+ # @return [void]
68
+ #
69
+ def titled_section(title, options = {})
70
+ relative_indentation = options[:relative_indentation] || 0
71
+ verbose_prefix = options[:verbose_prefix] || ''
72
+ if config.verbose?
73
+ title(title, verbose_prefix, relative_indentation)
74
+ else
75
+ puts title
76
+ end
77
+
78
+ self.indentation_level += relative_indentation
79
+ self.title_level += 1
80
+ yield if block_given?
81
+ self.indentation_level -= relative_indentation
82
+ self.title_level -= 1
83
+ end
84
+
85
+ # A title opposed to a section is always visible
86
+ #
87
+ # @param [String] title
88
+ # The title to print
89
+ #
90
+ # @param [String] verbose_prefix
91
+ # See #message
92
+ #
93
+ # @param [FixNum] relative_indentation
94
+ # The indentation level relative to the current,
95
+ # when the message is printed.
96
+ #
97
+ def title(title, verbose_prefix = '', relative_indentation = 2)
98
+ if @treat_titles_as_messages
99
+ message(title, verbose_prefix)
100
+ else
101
+ title = verbose_prefix + title if config.verbose?
102
+ title = "\n#{title}" if @title_level < 2
103
+ if (color = @title_colors[@title_level])
104
+ title = title.send(color)
105
+ end
106
+ puts "#{title}"
107
+ end
108
+
109
+ self.indentation_level += relative_indentation
110
+ self.title_level += 1
111
+ yield if block_given?
112
+ self.indentation_level -= relative_indentation
113
+ self.title_level -= 1
114
+ end
115
+
116
+ # Prints a verbose message taking an optional verbose prefix and
117
+ # a relative indentation valid for the UI action in the passed
118
+ # block.
119
+ #
120
+ # @todo Clean interface.
121
+ #
122
+ # @param [String] message
123
+ # The message to print.
124
+ #
125
+ # @param [String] verbose_prefix
126
+ # See #message
127
+ #
128
+ # @param [FixNum] relative_indentation
129
+ # The indentation level relative to the current,
130
+ # when the message is printed.
131
+ #
132
+ def message(message, verbose_prefix = '', relative_indentation = 2)
133
+ message = verbose_prefix + message if config.verbose?
134
+ puts_indented message if config.verbose?
135
+
136
+ self.indentation_level += relative_indentation
137
+ yield if block_given?
138
+ self.indentation_level -= relative_indentation
139
+ end
140
+
141
+ # Prints an info to the user. The info is always displayed.
142
+ # It respects the current indentation level only in verbose
143
+ # mode.
144
+ #
145
+ # Any title printed in the optional block is treated as a message.
146
+ #
147
+ # @param [String] message
148
+ # The message to print.
149
+ #
150
+ def info(message)
151
+ indentation = config.verbose? ? self.indentation_level : 0
152
+ indented = wrap_string(message, indentation)
153
+ puts(indented)
154
+
155
+ self.indentation_level += 2
156
+ @treat_titles_as_messages = true
157
+ yield if block_given?
158
+ @treat_titles_as_messages = false
159
+ self.indentation_level -= 2
160
+ end
161
+
162
+ # Prints an important message to the user.
163
+ #
164
+ # @param [String] message The message to print.
165
+ #
166
+ # return [void]
167
+ #
168
+ def notice(message)
169
+ puts("\n[!] #{message}".green)
170
+ end
171
+
172
+ # Returns a string containing relative location of a path from the Podfile.
173
+ # The returned path is quoted. If the argument is nil it returns the
174
+ # empty string.
175
+ #
176
+ # @param [#to_str] pathname
177
+ # The path to print.
178
+ #
179
+ def path(pathname)
180
+ if pathname
181
+ from_path = config.podfile_path.dirname if config.podfile_path
182
+ from_path ||= Pathname.pwd
183
+ path = begin
184
+ Pathname(pathname).relative_path_from(from_path)
185
+ rescue
186
+ pathname
187
+ end
188
+ "`#{path}`"
189
+ else
190
+ ''
191
+ end
192
+ end
193
+
194
+ # Prints the textual representation of a given set.
195
+ #
196
+ # @param [Set] set
197
+ # the set that should be presented.
198
+ #
199
+ # @param [Symbol] mode
200
+ # the presentation mode, either `:normal` or `:name_and_version`.
201
+ #
202
+ def pod(set, mode = :normal)
203
+ if mode == :name_and_version
204
+ puts_indented "#{set.name} #{set.versions.first.version}"
205
+ else
206
+ pod = Specification::Set::Presenter.new(set)
207
+ title = "-> #{pod.name} (#{pod.version})"
208
+ if pod.spec.deprecated?
209
+ title += " #{pod.deprecation_description}"
210
+ colored_title = title.red
211
+ else
212
+ colored_title = title.green
213
+ end
214
+
215
+ title(colored_title, '', 1) do
216
+ puts_indented pod.summary if pod.summary
217
+ puts_indented "pod '#{pod.name}', '~> #{pod.version}'"
218
+ labeled('Homepage', pod.homepage)
219
+ labeled('Source', pod.source_url)
220
+ labeled('Versions', pod.versions_by_source)
221
+ if mode == :stats
222
+ labeled('Authors', pod.authors) if pod.authors =~ /,/
223
+ labeled('Author', pod.authors) if pod.authors !~ /,/
224
+ labeled('License', pod.license)
225
+ labeled('Platform', pod.platform)
226
+ labeled('Stars', pod.github_stargazers)
227
+ labeled('Forks', pod.github_forks)
228
+ end
229
+ labeled('Subspecs', pod.subspecs)
230
+ end
231
+ end
232
+ end
233
+
234
+ # Prints a message with a label.
235
+ #
236
+ # @param [String] label
237
+ # The label to print.
238
+ #
239
+ # @param [#to_s] value
240
+ # The value to print.
241
+ #
242
+ # @param [FixNum] justification
243
+ # The justification of the label.
244
+ #
245
+ def labeled(label, value, justification = 12)
246
+ if value
247
+ title = "- #{label}:"
248
+ if value.is_a?(Array)
249
+ lines = [wrap_string(title, self.indentation_level)]
250
+ value.each do |v|
251
+ lines << wrap_string("- #{v}", self.indentation_level + 2)
252
+ end
253
+ puts lines.join("\n")
254
+ else
255
+ puts wrap_string(title.ljust(justification) + "#{value}", self.indentation_level)
256
+ end
257
+ end
258
+ end
259
+
260
+ # Prints a message respecting the current indentation level and
261
+ # wrapping it to the terminal width if necessary.
262
+ #
263
+ # @param [String] message
264
+ # The message to print.
265
+ #
266
+ def puts_indented(message = '')
267
+ indented = wrap_string(message, self.indentation_level)
268
+ puts(indented)
269
+ end
270
+
271
+ # Prints the stored warnings. This method is intended to be called at the
272
+ # end of the execution of the binary.
273
+ #
274
+ # @return [void]
275
+ #
276
+ def print_warnings
277
+ STDOUT.flush
278
+ warnings.each do |warning|
279
+ next if warning[:verbose_only] && !config.verbose?
280
+ STDERR.puts("\n[!] #{warning[:message]}".yellow)
281
+ warning[:actions].each do |action|
282
+ string = "- #{action}"
283
+ string = wrap_string(string, 4)
284
+ puts(string)
285
+ end
286
+ end
287
+ end
288
+
289
+ # Presents a choice among the elements of an array to the user.
290
+ #
291
+ # @param [Array<#to_s>] array
292
+ # The list of the elements among which the user should make his
293
+ # choice.
294
+ #
295
+ # @param [String] message
296
+ # The message to display to the user.
297
+ #
298
+ # @return [Fixnum] The index of the chosen array item.
299
+ #
300
+ def choose_from_array(array, message)
301
+ array.each_with_index do |item, index|
302
+ UI.puts "#{index + 1}: #{item}"
303
+ end
304
+
305
+ UI.puts message
306
+
307
+ index = UI.gets.chomp.to_i - 1
308
+ if index < 0 || index > array.count - 1
309
+ raise Informative, "#{index + 1} is invalid [1-#{array.count}]"
310
+ else
311
+ index
312
+ end
313
+ end
314
+
315
+ public
316
+
317
+ # @!group Basic methods
318
+ #-----------------------------------------------------------------------#
319
+
320
+ # prints a message followed by a new line.
321
+ #
322
+ # @param [String] message
323
+ # The message to print.
324
+ #
325
+ def puts(message = '')
326
+ begin
327
+ (output_io || STDOUT).puts(message)
328
+ rescue Errno::EPIPE
329
+ exit 0
330
+ end
331
+ end
332
+
333
+ # prints a message followed by a new line.
334
+ #
335
+ # @param [String] message
336
+ # The message to print.
337
+ #
338
+ def print(message)
339
+ begin
340
+ (output_io || STDOUT).print(message)
341
+ rescue Errno::EPIPE
342
+ exit 0
343
+ end
344
+ end
345
+
346
+ # gets input from $stdin
347
+ #
348
+ def gets
349
+ $stdin.gets
350
+ end
351
+
352
+ # Stores important warning to the user optionally followed by actions
353
+ # that the user should take. To print them use {#print_warnings}.
354
+ #
355
+ # @param [String] message The message to print.
356
+ # @param [Array] actions The actions that the user should take.
357
+ # @param [Bool] verbose_only
358
+ # Restrict the appearance of the warning to verbose mode only
359
+ #
360
+ # return [void]
361
+ #
362
+ def warn(message, actions = [], verbose_only = false)
363
+ warnings << { :message => message, :actions => actions, :verbose_only => verbose_only }
364
+ end
365
+
366
+ # Pipes all output inside given block to a pager.
367
+ #
368
+ # @yield Code block in which inputs to {#puts} and {#print} methods will be printed to the piper.
369
+ #
370
+ def with_pager
371
+ prev_handler = Signal.trap('INT', 'IGNORE')
372
+ IO.popen((ENV['PAGER'] || 'less -R'), 'w') do |io|
373
+ UI.output_io = io
374
+ yield
375
+ end
376
+ ensure
377
+ Signal.trap('INT', prev_handler)
378
+ UI.output_io = nil
379
+ end
380
+
381
+ private
382
+
383
+ # @!group Helpers
384
+ #-----------------------------------------------------------------------#
385
+
386
+ # @return [String] Wraps a string taking into account the width of the
387
+ # terminal and an option indent. Adapted from
388
+ # http://blog.macromates.com/2006/wrapping-text-with-regular-expressions/
389
+ #
390
+ # @param [String] txt The string to wrap
391
+ #
392
+ # @param [String] indent The string to use to indent the result.
393
+ #
394
+ # @return [String] The formatted string.
395
+ #
396
+ # @note If CocoaPods is not being run in a terminal or the width of the
397
+ # terminal is too small a width of 80 is assumed.
398
+ #
399
+ def wrap_string(string, indent = 0)
400
+ if disable_wrap
401
+ (' ' * indent) + string
402
+ else
403
+ first_space = ' ' * indent
404
+ indented = CLAide::Command::Banner::TextWrapper.wrap_with_indent(string, indent, 9999)
405
+ first_space + indented
406
+ end
407
+ end
408
+ end
409
+ end
410
+ UI = UserInterface
411
+ end
@@ -0,0 +1,5 @@
1
+ module MetaModel
2
+ # The version of the MetaModel command line tool.
3
+ #
4
+ VERSION = '0.0.1'.freeze unless defined? MetaModel::MetaModel
5
+ end
data/lib/metamodel.rb ADDED
@@ -0,0 +1,28 @@
1
+ require 'active_support/core_ext/string/strip'
2
+
3
+ module MetaModel
4
+
5
+ class PlainInformative < StandardError; end
6
+
7
+ # Indicates an user error. This is defined in cocoapods-core.
8
+ #
9
+ class Informative < PlainInformative
10
+ def message
11
+ "[!] #{super}".red
12
+ end
13
+ end
14
+
15
+ require 'pathname'
16
+ require 'active_record'
17
+
18
+ require 'metamodel/version'
19
+ require 'metamodel/config'
20
+
21
+ # Loaded immediately after dependencies to ensure proper override of their
22
+ # UI methods.
23
+ #
24
+ require 'metamodel/user_interface'
25
+
26
+ autoload :Command, 'metamodel/command'
27
+ autoload :Parser, 'metamodel/parser'
28
+ end