nimbu 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,38 @@
1
+ require "nimbu/command/base"
2
+ require 'term/ansicolor'
3
+
4
+ # authentication (login, logout)
5
+ #
6
+ class Nimbu::Command::Init < Nimbu::Command::Base
7
+ include Term::ANSIColor
8
+
9
+ # index
10
+ #
11
+ # log in with your nimbu credentials
12
+ #
13
+ def index
14
+ if Nimbu::Auth.read_configuration
15
+ print green(bold("CONGRATULATIONS!")), ": this directory is already configured as a Nimbu theme."
16
+ else
17
+ display "Initialize the Nimbu configuration file."
18
+ config = Nimbu::Auth.get_configuration
19
+
20
+ display "Configuration ready: #{config}"
21
+ config = Nimbu::Auth.get_credentials
22
+
23
+ display "Initializing directories:"
24
+ display " - layouts"
25
+ FileUtils.mkdir_p(File.join(Dir.pwd,'layouts'))
26
+ display " - templates"
27
+ FileUtils.mkdir_p(File.join(Dir.pwd,'templates'))
28
+ display " - stylesheets"
29
+ FileUtils.mkdir_p(File.join(Dir.pwd,'stylesheets'))
30
+ display " - javascripts"
31
+ FileUtils.mkdir_p(File.join(Dir.pwd,'javascripts'))
32
+ display " - images"
33
+ FileUtils.mkdir_p(File.join(Dir.pwd,'images'))
34
+ print green(bold("Done. Happy coding!\n"))
35
+ end
36
+ end
37
+ end
38
+
@@ -0,0 +1,22 @@
1
+ require "nimbu/command/base"
2
+ require "nimbu/server/base"
3
+ require 'term/ansicolor'
4
+
5
+ # running a local server to speed up designing Nimbu themes
6
+ #
7
+ class Nimbu::Command::Server < Nimbu::Command::Base
8
+ include Term::ANSIColor
9
+ # server
10
+ #
11
+ # list available commands or display help for a specific command
12
+ #
13
+ def index
14
+ # Check if config file is present?
15
+ if !Nimbu::Auth.read_configuration
16
+ print red(bold("WARNING")), ": this directory does not seem to contain any Nimbu theme. \n ==> Run \"", bold { "nimbu init ."}, "\" to initialize a new Nimbu project."
17
+ else
18
+ puts "Starting the server..."
19
+ Nimbu::Server::Base.run!
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,86 @@
1
+ require "nimbu/command/base"
2
+
3
+ # working with themes (upload / download)
4
+ #
5
+ class Nimbu::Command::Themes < Nimbu::Command::Base
6
+
7
+ # server
8
+ #
9
+ # list available commands or display help for a specific command
10
+ #
11
+ def index
12
+ themes = json_decode(nimbu.list_themes)
13
+ if themes.any?
14
+ puts "You have following themes for this website:"
15
+ themes.each do |theme|
16
+ puts " - #{theme['theme']['name']} (#{theme['theme']['id']})"
17
+ end
18
+ else
19
+ puts "Hm. You seem to have no themes. Is that normal?"
20
+ end
21
+ puts ""
22
+ puts "Currently this directory is configured for '#{Nimbu::Auth.theme}'"
23
+ end
24
+
25
+ # list
26
+ #
27
+ # list all layouts, templates and assets
28
+ #
29
+ def list
30
+ input = args.shift.downcase rescue nil
31
+ if !input.to_s.strip.empty?
32
+ theme = input.to_s.strip
33
+ else
34
+ theme = Nimbu::Auth.theme
35
+ end
36
+ display "Showing layouts, templates and assets for '#{theme}':"
37
+ contents = json_decode(nimbu.show_theme_contents(theme))
38
+ contents["layouts"].each do |l|
39
+ display " - layouts/#{l["name"]}"
40
+ end unless contents["layouts"].nil?
41
+ contents["templates"].each do |t|
42
+ display " - templates/#{t["name"]}"
43
+ end unless contents["templates"].nil?
44
+ contents["assets"].each do |a|
45
+ display " - #{a["folder"]}/#{a["name"]}"
46
+ end unless contents["assets"].nil?
47
+ end
48
+
49
+ # download
50
+ #
51
+ # download all layouts, templates and assets
52
+ #
53
+ def download
54
+ input = args.shift.downcase rescue nil
55
+ if !input.to_s.strip.empty?
56
+ theme = input.to_s.strip
57
+ else
58
+ theme = Nimbu::Auth.theme
59
+ end
60
+ display "Downloading layouts, templates and assets for '#{theme}':"
61
+ contents = json_decode(nimbu.show_theme_contents(theme))
62
+ contents["layouts"].each do |asset|
63
+ print " - layouts/#{asset["name"]}"
64
+ data = json_decode(nimbu.fetch_theme_layout(theme,asset["id"]))
65
+ filename = File.join(Dir.pwd,"layouts",asset["name"])
66
+ FileUtils.mkdir_p(File.dirname(filename))
67
+ File.open(filename, 'w') do |file|
68
+ file.puts(data["code"])
69
+ end
70
+
71
+ print " (ok)\n"
72
+ end
73
+
74
+ contents["templates"].each do |asset|
75
+ print " - templates/#{asset["name"]}"
76
+ data = json_decode(nimbu.fetch_theme_template(theme,asset["id"]))
77
+ filename = File.join(Dir.pwd,"templates",asset["name"])
78
+ FileUtils.mkdir_p(File.dirname(filename))
79
+ File.open(filename, 'w') do |file|
80
+ file.puts(data["code"])
81
+ end
82
+
83
+ print " (ok)\n"
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,382 @@
1
+ require "vendor/nimbu/okjson"
2
+
3
+ module Nimbu
4
+ module Helpers
5
+ def home_directory
6
+ running_on_windows? ? ENV['USERPROFILE'].gsub("\\","/") : ENV['HOME']
7
+ end
8
+
9
+ def running_on_windows?
10
+ RUBY_PLATFORM =~ /mswin32|mingw32/
11
+ end
12
+
13
+ def running_on_a_mac?
14
+ RUBY_PLATFORM =~ /-darwin\d/
15
+ end
16
+
17
+ def display(msg="", new_line=true)
18
+ if new_line
19
+ puts(msg)
20
+ else
21
+ print(msg)
22
+ STDOUT.flush
23
+ end
24
+ end
25
+
26
+ def redisplay(line, line_break = false)
27
+ display("\r\e[0K#{line}", line_break)
28
+ end
29
+
30
+ def deprecate(version)
31
+ display "!!! DEPRECATION WARNING: This command will be removed in version #{version}"
32
+ display
33
+ end
34
+
35
+ def error(msg)
36
+ STDERR.puts(format_with_bang(msg))
37
+ exit 1
38
+ end
39
+
40
+ def confirm_billing
41
+ display
42
+ display "This action will cause your account to be billed at the end of the month"
43
+ display "For more information, see http://devcenter.nimbu.com/articles/billing"
44
+ display "Are you sure you want to do this? (y/n) ", false
45
+ if ask.downcase == 'y'
46
+ nimbu.confirm_billing
47
+ return true
48
+ end
49
+ end
50
+
51
+ def confirm(message="Are you sure you wish to continue? (y/n)?")
52
+ display("#{message} ", false)
53
+ ask.downcase == 'y'
54
+ end
55
+
56
+ def confirm_command(app_to_confirm = app, message=nil)
57
+ raise(Nimbu::Command::CommandFailed, "No app specified.\nRun this command from app folder or set it adding --app <app name>") unless app_to_confirm
58
+
59
+ if respond_to?(:extract_option) && confirmed_app = extract_option('--confirm', false)
60
+ unless confirmed_app == app_to_confirm
61
+ raise(Nimbu::Command::CommandFailed, "Confirmed app #{confirmed_app} did not match the selected app #{app_to_confirm}.")
62
+ end
63
+ return true
64
+ else
65
+ display
66
+ message ||= "WARNING: Potentially Destructive Action\nThis command will affect the app: #{app_to_confirm}"
67
+ message << "\nTo proceed, type \"#{app_to_confirm}\" or re-run this command with --confirm #{app_to_confirm}"
68
+ output_with_bang(message)
69
+ display
70
+ display "> ", false
71
+ if ask.downcase != app_to_confirm
72
+ output_with_bang "Input did not match #{app_to_confirm}. Aborted."
73
+ false
74
+ else
75
+ true
76
+ end
77
+ end
78
+ end
79
+
80
+ def format_date(date)
81
+ date = Time.parse(date) if date.is_a?(String)
82
+ date.strftime("%Y-%m-%d %H:%M %Z")
83
+ end
84
+
85
+ def ask
86
+ STDIN.gets.strip
87
+ end
88
+
89
+ def shell(cmd)
90
+ FileUtils.cd(Dir.pwd) {|d| return `#{cmd}`}
91
+ end
92
+
93
+ def run_command(command, args=[])
94
+ Nimbu::Command.run(command, args)
95
+ end
96
+
97
+ def retry_on_exception(*exceptions)
98
+ retry_count = 0
99
+ begin
100
+ yield
101
+ rescue *exceptions => ex
102
+ raise ex if retry_count >= 3
103
+ sleep 3
104
+ retry_count += 1
105
+ retry
106
+ end
107
+ end
108
+
109
+ def has_git?
110
+ %x{ git --version }
111
+ $?.success?
112
+ end
113
+
114
+ def git(args)
115
+ return "" unless has_git?
116
+ flattened_args = [args].flatten.compact.join(" ")
117
+ %x{ git #{flattened_args} 2>&1 }.strip
118
+ end
119
+
120
+ def time_ago(elapsed)
121
+ if elapsed < 60
122
+ "#{elapsed.floor}s ago"
123
+ elsif elapsed < (60 * 60)
124
+ "#{(elapsed / 60).floor}m ago"
125
+ else
126
+ "#{(elapsed / 60 / 60).floor}h ago"
127
+ end
128
+ end
129
+
130
+ def truncate(text, length)
131
+ if text.size > length
132
+ text[0, length - 2] + '..'
133
+ else
134
+ text
135
+ end
136
+ end
137
+
138
+ @@kb = 1024
139
+ @@mb = 1024 * @@kb
140
+ @@gb = 1024 * @@mb
141
+ def format_bytes(amount)
142
+ amount = amount.to_i
143
+ return '(empty)' if amount == 0
144
+ return amount if amount < @@kb
145
+ return "#{(amount / @@kb).round}k" if amount < @@mb
146
+ return "#{(amount / @@mb).round}M" if amount < @@gb
147
+ return "#{(amount / @@gb).round}G"
148
+ end
149
+
150
+ def quantify(string, num)
151
+ "%d %s" % [ num, num.to_i == 1 ? string : "#{string}s" ]
152
+ end
153
+
154
+ def create_git_remote(remote, url)
155
+ return unless has_git?
156
+ return if git('remote').split("\n").include?(remote)
157
+ return unless File.exists?(".git")
158
+ git "remote add #{remote} #{url}"
159
+ display "Git remote #{remote} added"
160
+ end
161
+
162
+ def longest(items)
163
+ items.map { |i| i.to_s.length }.sort.last
164
+ end
165
+
166
+ def display_table(objects, columns, headers)
167
+ lengths = []
168
+ columns.each_with_index do |column, index|
169
+ header = headers[index]
170
+ lengths << longest([header].concat(objects.map { |o| o[column].to_s }))
171
+ end
172
+ display_row headers, lengths
173
+ display_row lengths.map { |length| "-" * length }, lengths
174
+ objects.each do |row|
175
+ display_row columns.map { |column| row[column] }, lengths
176
+ end
177
+ end
178
+
179
+ def display_row(row, lengths)
180
+ row.zip(lengths).each do |column, length|
181
+ format = column.is_a?(Fixnum) ? "%#{length}s " : "%-#{length}s "
182
+ display format % column, false
183
+ end
184
+ display
185
+ end
186
+
187
+ def json_encode(object)
188
+ Nimbu::OkJson.encode(object)
189
+ rescue Nimbu::OkJson::ParserError
190
+ nil
191
+ end
192
+
193
+ def json_decode(json)
194
+ Nimbu::OkJson.decode(json)
195
+ rescue Nimbu::OkJson::ParserError
196
+ nil
197
+ end
198
+
199
+ def set_buffer(enable)
200
+ with_tty do
201
+ if enable
202
+ `stty icanon echo`
203
+ else
204
+ `stty -icanon -echo`
205
+ end
206
+ end
207
+ end
208
+
209
+ def with_tty(&block)
210
+ return unless $stdin.tty?
211
+ begin
212
+ yield
213
+ rescue
214
+ # fails on windows
215
+ end
216
+ end
217
+
218
+ def get_terminal_environment
219
+ { "TERM" => ENV["TERM"], "COLUMNS" => `tput cols`, "LINES" => `tput lines` }
220
+ rescue
221
+ { "TERM" => ENV["TERM"] }
222
+ end
223
+
224
+ def fail(message)
225
+ raise Nimbu::Command::CommandFailed, message
226
+ end
227
+
228
+ ## DISPLAY HELPERS
229
+
230
+ def action(message)
231
+ output_with_arrow("#{message}... ", false)
232
+ Nimbu::Helpers.enable_error_capture
233
+ yield
234
+ Nimbu::Helpers.disable_error_capture
235
+ display "done", false
236
+ display(", #{@status}", false) if @status
237
+ display
238
+ end
239
+
240
+ def status(message)
241
+ @status = message
242
+ end
243
+
244
+ def output(message="", new_line=true)
245
+ return if message.to_s.strip == ""
246
+ display(" " + message.split("\n").join("\n "), new_line)
247
+ end
248
+
249
+ def output_with_arrow(message="", new_line=true)
250
+ return if message.to_s.strip == ""
251
+ display("----> " + message.split("\n").join("\n "), new_line)
252
+ end
253
+
254
+ def format_with_bang(message)
255
+ return '' if message.to_s.strip == ""
256
+ " ! " + message.split("\n").join("\n ! ")
257
+ end
258
+
259
+ def output_with_bang(message="", new_line=true)
260
+ return if message.to_s.strip == ""
261
+ display(format_with_bang(message), new_line)
262
+ end
263
+
264
+ def error_with_failure(message)
265
+ display "failed"
266
+ output_with_bang(message)
267
+ exit 1
268
+ end
269
+
270
+ def self.included_into
271
+ @@included_into ||= []
272
+ end
273
+
274
+ def self.extended_into
275
+ @@extended_into ||= []
276
+ end
277
+
278
+ def self.included(base)
279
+ included_into << base
280
+ end
281
+
282
+ def self.extended(base)
283
+ extended_into << base
284
+ end
285
+
286
+ def self.enable_error_capture
287
+ included_into.each do |base|
288
+ base.send(:alias_method, :error_without_failure, :error)
289
+ base.send(:alias_method, :error, :error_with_failure)
290
+ end
291
+ extended_into.each do |base|
292
+ class << base
293
+ alias_method :error_without_failure, :error
294
+ alias_method :error, :error_with_failure
295
+ end
296
+ end
297
+ end
298
+
299
+ def self.disable_error_capture
300
+ included_into.each do |base|
301
+ base.send(:alias_method, :error, :error_without_failure)
302
+ end
303
+ extended_into.each do |base|
304
+ class << base
305
+ alias_method :error, :error_without_failure
306
+ end
307
+ end
308
+ end
309
+
310
+
311
+ def display_header(message="", new_line=true)
312
+ return if message.to_s.strip == ""
313
+ display("=== " + message.to_s.split("\n").join("\n=== "), new_line)
314
+ end
315
+
316
+ def display_object(object)
317
+ case object
318
+ when Array
319
+ # list of objects
320
+ object.each do |item|
321
+ display_object(item)
322
+ end
323
+ when Hash
324
+ # if all values are arrays, it is a list with headers
325
+ # otherwise it is a single header with pairs of data
326
+ if object.values.all? {|value| value.is_a?(Array)}
327
+ object.keys.sort_by {|key| key.to_s}.each do |key|
328
+ display_header(key)
329
+ display_object(object[key])
330
+ hputs
331
+ end
332
+ end
333
+ else
334
+ hputs(object.to_s)
335
+ end
336
+ end
337
+
338
+ def hputs(string='')
339
+ Kernel.puts(string)
340
+ end
341
+
342
+ def hprint(string='')
343
+ Kernel.print(string)
344
+ STDOUT.flush
345
+ end
346
+
347
+ def string_distance(first, last)
348
+ distances = [] # 0x0s
349
+ 0.upto(first.length) do |index|
350
+ distances << [index] + [0] * last.length
351
+ end
352
+ distances[0] = 0.upto(last.length).to_a
353
+ 1.upto(last.length) do |last_index|
354
+ 1.upto(first.length) do |first_index|
355
+ first_char = first[first_index - 1, 1]
356
+ last_char = last[last_index - 1, 1]
357
+ if first_char == last_char
358
+ distances[first_index][last_index] = distances[first_index - 1][last_index - 1] # noop
359
+ else
360
+ distances[first_index][last_index] = [
361
+ distances[first_index - 1][last_index], # deletion
362
+ distances[first_index][last_index - 1], # insertion
363
+ distances[first_index - 1][last_index - 1] # substitution
364
+ ].min + 1 # cost
365
+ if first_index > 1 && last_index > 1
366
+ first_previous_char = first[first_index - 2, 1]
367
+ last_previous_char = last[last_index - 2, 1]
368
+ if first_char == last_previous_char && first_previous_char == last_char
369
+ distances[first_index][last_index] = [
370
+ distances[first_index][last_index],
371
+ distances[first_index - 2][last_index - 2] + 1 # transposition
372
+ ].min
373
+ end
374
+ end
375
+ end
376
+ end
377
+ end
378
+ distances[first.length][last.length]
379
+ end
380
+
381
+ end
382
+ end