metamodel 0.0.1

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,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