minvee 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +23 -0
  3. data/lib/minvee/serverbox.rb +2038 -0
  4. data/lib/minvee.rb +885 -0
  5. metadata +46 -0
data/lib/minvee.rb ADDED
@@ -0,0 +1,885 @@
1
+ #!/usr/bin/ruby -w
2
+ require 'json'
3
+ require 'uri'
4
+
5
+ # requires
6
+ require 'fileutils'
7
+
8
+
9
+ #===============================================================================
10
+ # Minvee
11
+ # This module is just to initialize the namespace. It doesn't do anything.
12
+ #
13
+ module Minvee
14
+ # Version
15
+ VERSION = '0.0.1'
16
+ end
17
+ #
18
+ # Minvee
19
+ #===============================================================================
20
+
21
+
22
+
23
+ #===============================================================================
24
+ # Minvee::Client
25
+ #
26
+ module Minvee::Client
27
+ #---------------------------------------------------------------------------
28
+ # tmp dir
29
+ #
30
+ @@tmp_dir = '/tmp'
31
+
32
+ def self.tmp_dir
33
+ return @@tmp_dir
34
+ end
35
+
36
+ def self.tmp_dir=(p_dir)
37
+ @@tmp_dir = p_dir
38
+ end
39
+ #
40
+ # tmp dir
41
+ #---------------------------------------------------------------------------
42
+
43
+
44
+ #---------------------------------------------------------------------------
45
+ # process_command_line
46
+ #
47
+ def self.process_command_line(params)
48
+ # $tm.hrm
49
+
50
+ # action
51
+ command = params.shift
52
+
53
+ # must have action
54
+ if not command
55
+ puts 'syntax: ' + script_name() + ' command [command options]'
56
+ exit 1
57
+ end
58
+
59
+ # normalize command
60
+ command = command.downcase
61
+ command = command.gsub(/\s+/mu, '')
62
+
63
+ # run command
64
+ if cmd = @@COMMANDS[command]
65
+ puts cmd
66
+ else
67
+ puts 'unknown command: ' + command
68
+ end
69
+ end
70
+ #
71
+ # process_command_line
72
+ #---------------------------------------------------------------------------
73
+
74
+
75
+ # commands
76
+ @@COMMANDS = {}
77
+
78
+ #---------------------------------------------------------------------------
79
+ # prep
80
+ #
81
+ @@COMMANDS['prep'] = {}
82
+
83
+ @@COMMANDS['prep']['sub'] = Proc.new do
84
+ puts 'hello world'
85
+ end
86
+ #
87
+ # prep
88
+ #---------------------------------------------------------------------------
89
+
90
+
91
+ #---------------------------------------------------------------------------
92
+ # help
93
+ #
94
+ def self.help
95
+
96
+
97
+ puts 'syntax: ' + script_name() + ' command [command options]'
98
+
99
+ end
100
+ #
101
+ # help
102
+ #---------------------------------------------------------------------------
103
+
104
+
105
+ #---------------------------------------------------------------------------
106
+ # script_name
107
+ #
108
+ def self.script_name
109
+ return File.basename(__FILE__)
110
+ end
111
+ #
112
+ # script_name
113
+ #---------------------------------------------------------------------------
114
+
115
+
116
+ #---------------------------------------------------------------------------
117
+ # zip
118
+ #
119
+ def self.zip(root, opts={})
120
+ # $tm.hrm
121
+
122
+ # default options
123
+ opts = {'auto_delete'=>true}.merge(opts)
124
+
125
+ # get temp path to zip file
126
+ tgt_path = Minvee::Utils::TempPath.new(self.tmp_dir, 'ext'=>'tgz')
127
+
128
+ # build command
129
+ cmd = [
130
+ 'tar',
131
+ '-czf',
132
+ tgt_path.to_s,
133
+ './',
134
+ ]
135
+
136
+ # go to working copy and zip there
137
+ Dir.chdir(root) do
138
+ system(*cmd)
139
+ end
140
+
141
+ # return path
142
+ return tgt_path
143
+ end
144
+ #
145
+ # zip
146
+ #---------------------------------------------------------------------------
147
+
148
+
149
+ #---------------------------------------------------------------------------
150
+ # find_working_copy_dir
151
+ # TODO: Allow for possibility that effective user does not own the
152
+ # working copy.
153
+ #
154
+ def self.find_working_copy_dir(opts={})
155
+ # $tm.hrm
156
+
157
+ # use explicit or current directory
158
+ if current_dir = opts['dir']
159
+ current_dir = File.expand_path(current_dir, Dir::pwd)
160
+ else
161
+ current_dir = Dir::pwd
162
+ end
163
+
164
+ # loop while the current dir is owned by the effective user
165
+ while File.stat(current_dir).writable?
166
+ # check for minvee.json
167
+ if File.exist?(current_dir + '/minvee.json')
168
+ return current_dir
169
+ end
170
+
171
+ # go to parent directory
172
+ current_dir = File.expand_path('..', current_dir)
173
+ end
174
+
175
+ # didn't find working copy
176
+ return nil
177
+ end
178
+ #
179
+ # find_working_copy_dir
180
+ #---------------------------------------------------------------------------
181
+
182
+
183
+ #---------------------------------------------------------------------------
184
+ # find_working_copy
185
+ #
186
+ def self.find_working_copy(opts={})
187
+ # $tm.hrm
188
+
189
+ # get working copy directory
190
+ root = find_working_copy_dir(opts)
191
+ root or return nil
192
+
193
+ # instantiate working copy object
194
+ wc = Minvee::Client::WorkingCopy.new(root, opts)
195
+
196
+ # return
197
+ return wc
198
+ end
199
+ #
200
+ # find_working_copy
201
+ #---------------------------------------------------------------------------
202
+ end
203
+ #
204
+ # Minvee::Client
205
+ #===============================================================================
206
+
207
+
208
+ #===============================================================================
209
+ # Minvee::Utils
210
+ #
211
+ module Minvee::Utils
212
+ #---------------------------------------------------------------------------
213
+ # rand_pk
214
+ #
215
+ @@rnd_chars = 'a'.upto('z').to_a + '0'.upto('9').to_a
216
+ @@rnd_chars_max = @@rnd_chars.length - 1
217
+
218
+ def self.rand_pk()
219
+ # intialize rv
220
+ rv = ''
221
+
222
+ # build return string
223
+ 10.times do
224
+ rv += @@rnd_chars[rand 0 .. @@rnd_chars_max]
225
+ end
226
+
227
+ # freeze return value
228
+ rv.freeze
229
+
230
+ # return
231
+ return rv
232
+ end
233
+ #
234
+ # rand_pk
235
+ #---------------------------------------------------------------------------
236
+
237
+
238
+ #---------------------------------------------------------------------------
239
+ # atomic_write
240
+ #
241
+ def self.atomic_write(tgt_path)
242
+ # $tm.hrm
243
+
244
+ # get temp path
245
+ tmp_path = Minvee::Utils::TempPath.new(Minvee::Client.tmp_dir)
246
+
247
+ # ensure the tmp path gets deleted
248
+ begin
249
+ # provide write handle
250
+ File.open(tmp_path, 'w') do |tmp_file|
251
+ yield(tmp_file)
252
+ end
253
+
254
+ # move the temp file to the permanent location
255
+ FileUtils.mv(tmp_path, tgt_path)
256
+ ensure
257
+ # delete the tmp path
258
+ tmp_path.close
259
+ end
260
+ end
261
+ #
262
+ # atomic_write
263
+ #---------------------------------------------------------------------------
264
+ end
265
+ #
266
+ # Minvee::Utils
267
+ #===============================================================================
268
+
269
+
270
+ #===============================================================================
271
+ # Minvee::Client::Message
272
+ #
273
+ class Minvee::Client::Message
274
+ attr_reader :languages
275
+ attr_reader :params
276
+ attr_reader :path
277
+
278
+ #---------------------------------------------------------------------------
279
+ # initialize
280
+ #
281
+ def initialize(p_path)
282
+ # initialize params
283
+ @params = {}
284
+
285
+ # initialize languages
286
+ @languages = ['general']
287
+
288
+ # clone path
289
+ @path = p_path.clone
290
+ end
291
+ #
292
+ # initialize
293
+ #---------------------------------------------------------------------------
294
+
295
+
296
+ #---------------------------------------------------------------------------
297
+ # src
298
+ #
299
+ def src
300
+ # intialize search
301
+ search = @path.clone
302
+
303
+ # initialize rv
304
+ rv = Minvee::Client::Message::Sources.srcs['general']
305
+
306
+ # get message source
307
+ while search.length > 0
308
+ rv = rv[search.shift()]
309
+ end
310
+
311
+ # return
312
+ return rv
313
+ end
314
+ #
315
+ # src
316
+ #---------------------------------------------------------------------------
317
+
318
+
319
+ #---------------------------------------------------------------------------
320
+ # expand
321
+ #
322
+ def expand
323
+ # $tm.hrm
324
+
325
+ # initialize return value
326
+ rv = []
327
+
328
+ # split src
329
+ tokens = src.split(/(\[\[[a-z0-9\-\_]+?\]\])/imu)
330
+ tokens = tokens.grep(/\S/imu)
331
+
332
+ # build return value
333
+ tokens.each do |token|
334
+ if token.match(/\A\[\[[a-z0-9\-\_]+?\]\]\z/imu)
335
+ token.sub!(/\A\[\[\s*/imu, '')
336
+ token.sub!(/\s*\]\]\z/imu, '')
337
+
338
+ if @params.has_key?(token)
339
+ rv.push @params[token].to_s
340
+ end
341
+ else
342
+ rv.push token
343
+ end
344
+ end
345
+
346
+ # return
347
+ return rv.join('')
348
+ end
349
+ #
350
+ # expand
351
+ #---------------------------------------------------------------------------
352
+ end
353
+ #
354
+ # Minvee::Client::Message
355
+ #===============================================================================
356
+
357
+
358
+ #===============================================================================
359
+ # Minvee::Client::Message::Sources
360
+ #
361
+ module Minvee::Client::Message::Sources
362
+ @@srcs = {}
363
+ def self.srcs; return @@srcs; end;
364
+
365
+ # general
366
+ @@srcs['general'] = {
367
+ 'test' => {
368
+ 'a' => 'test message a',
369
+ 'b' => 'test [[[[my-param]] b',
370
+ },
371
+
372
+ 'questions' => {
373
+ 'yes' => {
374
+ 'letter' => 'y',
375
+ 'word' => 'yes',
376
+ },
377
+ 'no' => {
378
+ 'letter' => 'n',
379
+ 'word' => 'no',
380
+ },
381
+ }
382
+ }
383
+ end
384
+ #
385
+ # Minvee::Client::Message::Sources
386
+ #===============================================================================
387
+
388
+
389
+ #===============================================================================
390
+ # Minvee::Client::WorkingCopy
391
+ #
392
+ class Minvee::Client::WorkingCopy
393
+ attr_accessor :root
394
+ attr_reader :settings
395
+
396
+
397
+ #---------------------------------------------------------------------------
398
+ # initialize
399
+ #
400
+ def initialize(p_root, opts={})
401
+ # $tm.hrm
402
+
403
+ # p_root must be defined
404
+ if p_root.nil?
405
+ raise 'root-not-defined: did not get a defined root for working copy'
406
+ end
407
+
408
+ # hold on to root
409
+ @root = p_root.to_s.dup
410
+
411
+ # normalize and freeze root
412
+ @root.sub!(/\/+\z/imu, '')
413
+ @root.freeze
414
+
415
+ # if directory doesn't exist
416
+ if not File.exist?(@root)
417
+ raise 'root-not-exists: do not find a directory at ' + @root
418
+ end
419
+
420
+ # if settings file doesn't exist
421
+ if not File.exist?(self.settings_path)
422
+ raise 'no-settings-file: do not find minvee.json at ' + settings_path
423
+ end
424
+
425
+ # slurp in file
426
+ # KLUDGE: File or JSON will throw errors at this point if there are any.
427
+ # We'll just let those bubble up to the script that is calling this
428
+ # method instead of catching the exceptions here.
429
+ @settings = JSON.parse(File.read(self.settings_path))
430
+
431
+ # check settings
432
+ check_settings(opts)
433
+ end
434
+ #
435
+ # initialize
436
+ #---------------------------------------------------------------------------
437
+
438
+
439
+ #---------------------------------------------------------------------------
440
+ # zip
441
+ #
442
+ def zip(opts={})
443
+ return Minvee::Client.zip(@root, opts)
444
+ end
445
+ #
446
+ # zip
447
+ #---------------------------------------------------------------------------
448
+
449
+
450
+ #---------------------------------------------------------------------------
451
+ # settings_path
452
+ #
453
+ def settings_path
454
+ return @root + '/minvee.json'
455
+ end
456
+ #
457
+ # settings_path
458
+ #---------------------------------------------------------------------------
459
+
460
+
461
+ #---------------------------------------------------------------------------
462
+ # save_settings
463
+ #
464
+ def save_settings
465
+ Minvee::Utils.atomic_write(self.settings_path) do |file|
466
+ file.print JSON.pretty_generate(@settings)
467
+ end
468
+ end
469
+ #
470
+ # save_settings
471
+ #---------------------------------------------------------------------------
472
+
473
+
474
+ # private from here on
475
+ private
476
+
477
+
478
+ #---------------------------------------------------------------------------
479
+ # check_settings
480
+ # settings must have a project element, which is a hash, and that hash
481
+ # must have a url.
482
+ #
483
+ def check_settings(opts={})
484
+ # $tm.hrm
485
+
486
+ # default options
487
+ opts = {'check_url'=>true}.merge(opts)
488
+
489
+ # must be hash
490
+ if not @settings.is_a?(Hash)
491
+ raise "settings-not-hash: the settings for #{@root} are not a hash"
492
+ end
493
+
494
+ # convenience
495
+ project = @settings['project']
496
+
497
+ # must have project element
498
+ if not project
499
+ raise "settings-no-project: there are no project settings for #{@root}"
500
+ end
501
+
502
+ # project element must be a hash
503
+ if not project.is_a?(Hash)
504
+ raise "settings-project-not-hash: the project element for #{@root} is not a hash"
505
+ end
506
+
507
+ # must have project url
508
+ if not project['url']
509
+ raise "settings-project-no-url: the settings for #{@root} do not have a project url"
510
+ end
511
+
512
+ # must be an HTTPS url
513
+ # TODO: We'll probably need to expand what types of URLs are allowed.
514
+ # For example, we should probably allow http on localhost.
515
+ if opts['check_url']
516
+ if not URI(project['url']).is_a?(URI::HTTPS)
517
+ raise "settings-project-invalid-url: the project url for #{@root} is not an HTTPS url: " + project['url']
518
+ end
519
+ end
520
+ end
521
+ #
522
+ # check_settings
523
+ #---------------------------------------------------------------------------
524
+ end
525
+ #
526
+ # Minvee::Client::WorkingCopy
527
+ #===============================================================================
528
+
529
+
530
+ #===============================================================================
531
+ # Minvee::Utils::TempPath
532
+ #
533
+ class Minvee::Utils::TempPath
534
+ attr_reader :path
535
+ attr_accessor :auto_delete
536
+
537
+ #---------------------------------------------------------------------------
538
+ # initialize
539
+ #
540
+ def initialize(base, opts={})
541
+ # default options
542
+ opts = {'auto_delete'=>true}.merge(opts)
543
+
544
+ # if explicit path sent
545
+ if opts['explicit']
546
+ @path = base
547
+
548
+ # else build random path
549
+ else
550
+ # if path is a directory, add /
551
+ if File.directory?(base)
552
+ base = base.sub(/\/*\z/imu, '/')
553
+ end
554
+
555
+ # initialize path
556
+ @path = base + Minvee::Utils.rand_pk
557
+
558
+ # add suffix
559
+ if opts['suffix']
560
+ @path += '.' + opts['suffix']
561
+ end
562
+
563
+ # add ext if sent
564
+ if opts['ext']
565
+ ext = opts['ext'].dup
566
+ ext.sub!(/\A\.*/imu, '.')
567
+ @path += ext
568
+ end
569
+ end
570
+
571
+ # set finalizer for this object
572
+ ObjectSpace.define_finalizer(self, proc { self.class.finalize(self.object_id) })
573
+
574
+ # set auto delete
575
+ @auto_delete = opts['auto_delete']
576
+ end
577
+ #
578
+ # initialize
579
+ #---------------------------------------------------------------------------
580
+
581
+
582
+ #---------------------------------------------------------------------------
583
+ # to_s. and to_str
584
+ #
585
+ def to_str
586
+ return @path
587
+ end
588
+
589
+ def to_s
590
+ return @path
591
+ end
592
+
593
+ def +(str)
594
+ return @path.to_s + str
595
+ end
596
+ #
597
+ # to_s. and to_str
598
+ #---------------------------------------------------------------------------
599
+
600
+
601
+ #---------------------------------------------------------------------------
602
+ # close
603
+ #
604
+ def close
605
+ # if file exists, delete it
606
+ if self.auto_delete
607
+ if File.directory?(self.path)
608
+ FileUtils.rm_rf(self.path)
609
+ elsif File.exist?(self.path)
610
+ File.delete(self.path)
611
+ end
612
+ end
613
+ end
614
+ #
615
+ # close
616
+ #---------------------------------------------------------------------------
617
+
618
+
619
+ #---------------------------------------------------------------------------
620
+ # finalize method
621
+ #
622
+ def self.finalize(id)
623
+ # $tm.hrm
624
+
625
+ # get object from id
626
+ myself = ObjectSpace._id2ref(id)
627
+
628
+ # close
629
+ myself.close()
630
+ end
631
+ #
632
+ # finalize method
633
+ #---------------------------------------------------------------------------
634
+ end
635
+ #
636
+ # Minvee::Utils::TempPath
637
+ #===============================================================================
638
+
639
+
640
+ #===============================================================================
641
+ # Minvee::Utils::TempDir
642
+ #
643
+ class Minvee::Utils::TempDir < Minvee::Utils::TempPath
644
+ #---------------------------------------------------------------------------
645
+ # initialize
646
+ #
647
+ def initialize(base, opts={})
648
+ # call super method
649
+ super(base, opts)
650
+
651
+ # create directory
652
+ FileUtils::mkdir_p(@path)
653
+ end
654
+ #
655
+ # initialize
656
+ #---------------------------------------------------------------------------
657
+ end
658
+ #
659
+ # Minvee::Utils::TempDir
660
+ #===============================================================================
661
+
662
+
663
+ #===============================================================================
664
+ # Minvee::Client::Questions
665
+ #
666
+ module Minvee::Client::Questions
667
+ #---------------------------------------------------------------------------
668
+ # ask
669
+ #
670
+ def self.ask(type, opts)
671
+ # $tm.hrm
672
+
673
+ # select
674
+ if type == 'select'
675
+ return self.select(opts)
676
+
677
+ # short
678
+ elsif type == 'short'
679
+ return self.short(opts)
680
+
681
+ # boolean
682
+ elsif type == 'boolean'
683
+ return self.boolean(opts)
684
+
685
+ # else unknown
686
+ else
687
+ raise 'unknown question type: ' + type
688
+ end
689
+ end
690
+ #
691
+ # ask
692
+ #---------------------------------------------------------------------------
693
+
694
+
695
+ #---------------------------------------------------------------------------
696
+ # select
697
+ #
698
+ def self.select(opts)
699
+ # $tm.hrm
700
+
701
+ # initialize choice
702
+ choice = ''
703
+
704
+ # initialize choices hash
705
+ choices = {}
706
+
707
+ # initialize return hash
708
+ rv = {}
709
+
710
+ # normalized choices
711
+ opts['choices'].each do |k, v|
712
+ choices[k.downcase] = v
713
+ end
714
+
715
+ # prompt user until they make a choice
716
+ while not choices[choice]
717
+ # empty line
718
+ puts
719
+
720
+ # output question
721
+ puts opts['question']
722
+
723
+ # output choices
724
+ choices.each do |k, v|
725
+ puts k + ': ' + v['text']
726
+ end
727
+
728
+ # empty line
729
+ puts
730
+
731
+ # prompt
732
+ print opts['prompt'] + ': '
733
+
734
+ # get input from user
735
+ choice = gets
736
+ choice.sub!(/\s+\z/imu, '')
737
+ choice.sub!(/\A\s+/imu, '')
738
+ choice.downcase!
739
+ end
740
+
741
+ # store choice
742
+ rv['choice'] = choice
743
+
744
+ # if "other"
745
+ if choices[choice]['other']
746
+ while not rv['other']
747
+ # get other choice
748
+ print choices[choice]['other'] + ': '
749
+ response = gets
750
+
751
+ # normalize response
752
+ response.sub!(/\s+\z/imu, '')
753
+ response.sub!(/\A\s+/imu, '')
754
+
755
+ # if response has content, we're done with this loop
756
+ if response.length > 0
757
+ rv['other'] = response
758
+ end
759
+ end
760
+ end
761
+
762
+ # return
763
+ return rv
764
+ end
765
+ #
766
+ # select
767
+ #---------------------------------------------------------------------------
768
+
769
+
770
+ #---------------------------------------------------------------------------
771
+ # short
772
+ #
773
+ def self.short(opts)
774
+ # $tm.hrm
775
+
776
+ # initialize return hash
777
+ rv = {}
778
+
779
+ # loop until we get an answer
780
+ while not rv['response']
781
+ # get other choice
782
+ print opts['prompt'] + ': '
783
+ response = gets
784
+
785
+ # normalize response
786
+ response.sub!(/\s+\z/imu, '')
787
+ response.sub!(/\A\s+/imu, '')
788
+
789
+ # if response has content, or response is not required, we're done
790
+ if (response.length > 0) or (not opts['required'])
791
+ rv['response'] = response
792
+ end
793
+ end
794
+
795
+ # return
796
+ return rv
797
+ end
798
+ #
799
+ # short
800
+ #---------------------------------------------------------------------------
801
+
802
+
803
+ #---------------------------------------------------------------------------
804
+ # boolean
805
+ #
806
+ def self.boolean(opts)
807
+ # $tm.hrm
808
+
809
+ # initialize return hash
810
+ rv = {}
811
+
812
+ # output prompt
813
+ puts opts['prompt']
814
+
815
+ # boolean options
816
+ bools = {
817
+ Minvee::Client::Message.new(%w{questions yes letter}).expand => true,
818
+ Minvee::Client::Message.new(%w{questions no letter}).expand => false,
819
+ }
820
+
821
+ # initialize choices
822
+ choices = []
823
+
824
+ # yes
825
+ choices.push 'y'
826
+ choices.push 'n'
827
+
828
+ # default
829
+ if opts.has_key?('default')
830
+ has_default = true
831
+
832
+ if opts['default']
833
+ choices[0].upcase!
834
+ else
835
+ choices[1].upcase!
836
+ end
837
+ end
838
+
839
+ # ask until we get an answer
840
+ while rv['response'].nil?
841
+ # prompt
842
+ print choices.join('/'), ': '
843
+
844
+ # get response
845
+ response = gets
846
+
847
+ # normalize response
848
+ response.sub!(/\s+\z/imu, '')
849
+ response.sub!(/\A\s+/imu, '')
850
+ response.downcase!()
851
+
852
+ # if anything left in the string, get the first character
853
+ if response.length > 0
854
+ response = response[0]
855
+ end
856
+
857
+ # if we got an acceptable answer
858
+ if bools.has_key?(response)
859
+ rv['response'] = bools[response]
860
+ elsif has_default and (response == '')
861
+ rv['response'] = opts['default']
862
+ end
863
+ end
864
+
865
+ # return
866
+ return rv
867
+ end
868
+ #
869
+ # boolean
870
+ #---------------------------------------------------------------------------
871
+ end
872
+ #
873
+ # Minvee::Client::Questions
874
+ #===============================================================================
875
+
876
+
877
+ #===============================================================================
878
+ # run if the script was not loaded by another script
879
+ #
880
+ if caller().length <= 0
881
+ Minvee::Client.process_command_line ARGV
882
+ end
883
+ #
884
+ # run if the script was not loaded by another script
885
+ #===============================================================================