blufin-lib 1.7.6 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -67,11 +67,30 @@ module Blufin
67
67
  flags_set = 0
68
68
  possible_flags.each { |possible_flag|
69
69
  raise RuntimeError, "@opts does not contain flag: #{possible_flag}" unless opts.has_key?(possible_flag)
70
- flags_set += 1 if opts[possible_flag]
70
+ flags_set += 1 if opts[possible_flag] && !opts[possible_flag].nil?
71
71
  }
72
72
  Blufin::Terminal::error("Cannot set #{Blufin::Terminal::format_highlight('more than one')} of the following flags: \x1B[38;5;172m#{possible_flags.join(', ')}\x1B[0m") if flags_set > 1
73
73
  end
74
74
 
75
+ # Prevents code from running twice.
76
+ # @return boolean
77
+ def self.code_already_ran(tmp_key, timeout = 1000)
78
+ t = Time.new
79
+ epoch = t.strftime('%s%3N')
80
+ epoch_previous = nil
81
+ tmp_file = "/tmp/#{tmp_key}-last-run-epoch.txt"
82
+ if Blufin::Files::file_exists(tmp_file)
83
+ Blufin::Files::read_file(tmp_file).each do |line|
84
+ epoch_previous = line
85
+ break
86
+ end
87
+ end
88
+ Blufin::Files::write_file(tmp_file, [epoch])
89
+ return if epoch_previous.nil?
90
+ diff = (epoch.to_i - epoch_previous.to_i).abs
91
+ diff < timeout
92
+ end
93
+
75
94
  private
76
95
 
77
96
  # Does exactly what it says on the Tin.
@@ -97,6 +97,22 @@ module Blufin
97
97
  rs
98
98
  end
99
99
 
100
+ # Strips all newline character(s) -- IE: "abc\n" -> "abc"
101
+ # Be careful, if multiple newlines are in string, they will all get stripped and you might end up with weird output.
102
+ # @return string
103
+ def self.strip_newline(string)
104
+ raise RuntimeError, "Expected String, instead got: #{string.class}" unless string.is_a?(String)
105
+ string.gsub(/[\r\n]+/m, '').strip
106
+ end
107
+
108
+ # Removes all colors from a string.
109
+ # https://stackoverflow.com/questions/16032726/removing-color-decorations-from-strings-before-writing-them-to-logfile
110
+ # @return string
111
+ def self.strip_ansi_colors(string)
112
+ raise RuntimeError, "Expected String, instead got: #{string.class}" unless string.is_a?(String)
113
+ string.gsub(/\e\[([;\d]+)?m/, '').strip
114
+ end
115
+
100
116
  private
101
117
 
102
118
  # Internal string validation.
@@ -22,6 +22,7 @@ module Blufin
22
22
  MSG_PROGRESS = 'progress'
23
23
  MSG_CUSTOM = 'custom'
24
24
  MSG_CUSTOM_AUTO_PAD = 'custom_auto_pad'
25
+ ERR_OUTPUT = '/tmp/execute-output'
25
26
 
26
27
  # Runs a series of commands in the terminal.
27
28
  # @return void
@@ -77,30 +78,39 @@ module Blufin
77
78
  # If capture: is false, returns TRUE:FALSE whether command was successful or not.
78
79
  # If capture: is true, returns raw output of command.
79
80
  # See: https://github.com/piotrmurach/tty-spinner/blob/master/lib/tty/spinner/formats.rb (for spinner options).
80
- # @return void
81
- def self.execute(command, path = nil, capture: false, verbose: true, text: nil)
81
+ # @return idx-1 => Command was successful (exit code 0) will output TRUE if capture: FALSE or otherwise STDOUT will be returned, IE: 'On branch master'
82
+ # @return idx-2 => STDERR (if any), IE: anything written to /tmp/execute-output
83
+ def self.execute(command, path = nil, capture: false, verbose: true, text: nil, error_output: ERR_OUTPUT, display_error: true, ignore_error: false)
82
84
  text = text.is_a?(String) ? text : command
83
85
  t1 = Time.now
84
86
  spinner = TTY::Spinner.new("[:spinner] \x1B[38;5;208m#{text}#{!path.nil? ? " \x1B[38;5;246m\xe2\x86\x92 \x1B[38;5;240m#{path}" : nil}\x1B[0m", format: :dots) if verbose
85
87
  spinner.auto_spin if verbose
86
88
  path = File.expand_path('~/') if path.nil?
89
+ path = File.expand_path(path) unless path.nil?
90
+ `echo '' > #{ERR_OUTPUT}`
87
91
  if capture
88
- res = `cd #{path} && #{command}`
92
+ res = `cd #{path} && #{command} 2>#{error_output}`
89
93
  else
90
- res = system("cd #{path} && #{command} > /tmp/execute-output")
94
+ res = system("cd #{path} && #{command} 1>/dev/null 2>#{error_output}")
91
95
  end
92
96
  t2 = Time.now
93
97
  delta = "#{'%.3f' % (t2 - t1).abs}s"
94
- if res || capture
98
+ error = `cat #{ERR_OUTPUT}`.to_s.strip
99
+ if (res && error.length == 0) || ignore_error
95
100
  spinner.success("\x1B[38;5;246m\xe2\x86\x92 \x1B[38;5;46mComplete \x1B[38;5;240m(#{delta})\x1B[0m\x1B[0m") if verbose
101
+ res = true unless capture
96
102
  else
97
103
  spinner.error("\x1B[38;5;246m\xe2\x86\x92 \x1B[38;5;196mFailed (#{delta})\x1B[0m") if verbose
98
104
  # If there is an error, output it (even if verbose == false).
99
- puts "\x1B[38;5;240m"
100
- system('cat /tmp/execute-output')
101
- puts "\x1B[0m"
105
+ if error.length > 0 && display_error
106
+ puts "\x1B[38;5;240m"
107
+ system("cat #{ERR_OUTPUT}")
108
+ puts "\x1B[0m"
109
+ end
110
+ res = false unless capture
102
111
  end
103
- res
112
+ error = nil if error.nil? || error.strip == ''
113
+ return res, error
104
114
  end
105
115
 
106
116
  # Same as above but with a proc/lambda instead of a terminal command.
@@ -124,11 +134,9 @@ module Blufin
124
134
  # If CUSTOM, title must be 11 characters to match existing scheme.
125
135
  # @return void
126
136
  def self.output(message = 'No message', type = MSG_INFO, title = nil, bg_color = nil)
127
-
128
137
  raise RuntimeError, "'title' cannot be nil." if title.nil? && [MSG_CUSTOM, MSG_CUSTOM_AUTO_PAD].include?(type)
129
138
  raise RuntimeError, "'bg_color' cannot be nil." if bg_color.nil? && [MSG_CUSTOM, MSG_CUSTOM_AUTO_PAD].include?(type)
130
139
  raise RuntimeError, "'title' cannot be more that 9 characerts when MSG_CUSTOM_AUTO_PAD is the message type." if type == MSG_CUSTOM_AUTO_PAD && title.length > 9
131
-
132
140
  case type
133
141
  when MSG_INFO
134
142
  puts "\x1B[38;5;231m\x1B[48;5;22m Executing \x1B[0m \x1B[0m\xe2\x86\x92\x1B[0m \x1B[0m#{message}\x1B[0m\n"
@@ -151,8 +159,7 @@ module Blufin
151
159
  when MSG_CUSTOM_AUTO_PAD
152
160
  puts "\x1B[38;5;231m#{''.rjust((9 - title.length), ' ')}\x1B[48;5;#{bg_color}m #{title} \x1B[0m \x1B[0m\xe2\x86\x92\x1B[0m \x1B[0m#{message}\x1B[0m\n"
153
161
  else
154
- puts "'#{type}' is not a valid Terminal::output() type.\n"
155
- exit
162
+ raise RuntimeError, "Invalid Terminal::output type: #{type}"
156
163
  end
157
164
  end
158
165
 
@@ -170,7 +177,7 @@ module Blufin
170
177
  puts if preceding_blank_line
171
178
  puts " \x1B[38;5;231m\x1B[48;5;124m #{error_text} \x1B[0m #{title.nil? ? '' : "\xe2\x86\x92 "}#{title}\n"
172
179
  parse_messages(message)
173
- exit if exit_script
180
+ exit 1 if exit_script
174
181
  end
175
182
 
176
183
  # Displays automatic message.
@@ -607,7 +614,10 @@ module Blufin
607
614
  type = type.downcase
608
615
  types = {
609
616
  'yml' => Rouge::Lexers::YAML.new,
610
- 'json' => Rouge::Lexers::JSON.new
617
+ 'json' => Rouge::Lexers::JSON.new,
618
+ 'js' => Rouge::Lexers::Javascript.new,
619
+ 'java' => Rouge::Lexers::Javascript.new,
620
+ 'sass' => Rouge::Lexers::Sass.new
611
621
  }
612
622
  raise RuntimeError, "Lexer not defined for type: #{type}" unless types.has_key?(type)
613
623
  repeat = ' ' * indent
@@ -0,0 +1,15 @@
1
+ module Blufin
2
+
3
+ class Update
4
+
5
+ # Updates ruby-gem.
6
+ # @return void
7
+ def self.run(gem_name)
8
+
9
+ raise RuntimeError, "Update #{gem_name}. Not yet implemented!"
10
+
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,43 @@
1
+ module Blufin
2
+
3
+ class Yml
4
+
5
+ # Validate and initialize a YML file.
6
+ # @return Hash
7
+ def self.read_file(content_file, schema_file)
8
+ content_file = File.expand_path(content_file)
9
+ schema_file = File.expand_path(schema_file)
10
+ # Check file(s) exist.
11
+ [content_file, schema_file].each { |file| Blufin::Terminal::error("File not found: #{schema_file}") unless Blufin::Files::file_exists(file) }
12
+ # Parse the schema file.
13
+ begin
14
+ schema_file_parsed = YAML.load_file(schema_file)
15
+ rescue => e
16
+ Blufin::Terminal::error("Failed to parse schema file: #{Blufin::Terminal::format_directory(schema_file)}", e.message)
17
+ end
18
+ # Initialize the validator.
19
+ validator = Kwalify::Validator.new(schema_file_parsed)
20
+ # Validate the content file.
21
+ begin
22
+ document = YAML.load_file(content_file)
23
+ # noinspection RubyArgCount
24
+ errors = validator.validate(document)
25
+ rescue => e
26
+ Blufin::Terminal::error("Failed to parse content file: #{Blufin::Terminal::format_directory(content_file)}", e.message)
27
+ end
28
+ # If errors occurred, display and bomb-out.
29
+ if errors && !errors.empty?
30
+ errors_output = []
31
+ errors.each { |e|
32
+ errors_output << "[#{e.path}] #{e.message}"
33
+ }
34
+ Blufin::Terminal::error("File had errors: #{Blufin::Terminal::format_directory(content_file)}", errors_output)
35
+ exit
36
+ end
37
+ # Otherwise, return valid YML.
38
+ document
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,25 @@
1
+ module Blufin
2
+
3
+ class GenerateBase
4
+
5
+ OUTPUT_GENERATE = 'generated'
6
+ OUTPUT_SKIP = 'exists'
7
+
8
+ # Standardized output for code-generation.
9
+ # @return void
10
+ def self.output(message, type)
11
+
12
+ case type
13
+ when OUTPUT_GENERATE
14
+ puts "\x1B[38;5;231m\x1B[48;5;55m Generated \x1B[0m \xe2\x86\x92 \x1B[38;5;182m#{message}\x1B[0m"
15
+ when OUTPUT_SKIP
16
+ puts " \x1B[38;5;246m\x1B[48;5;234m Skipping \x1B[0m \xe2\x86\x92 \x1B[38;5;240m#{message}\x1B[0m"
17
+ else
18
+ raise "Unrecognized output type: #{type}"
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,165 @@
1
+ module Blufin
2
+
3
+ class GenerateUiRoutes
4
+
5
+ SCHEMA_FILE = "#{Blufin::Base::get_base_path}#{Blufin::Base::OPT_PATH}/schema/routes.yml"
6
+ HOME_TERM = 'dashboard'
7
+
8
+ # Generates routes in the UI.
9
+ # @return void
10
+ def self.run(project)
11
+ raise RuntimeError, "Expected project type to be: #{Blufin::Projects::TYPE_UI}, instead got: #{project[Blufin::Projects::TYPE]}" unless project[Blufin::Projects::TYPE] == Blufin::Projects::TYPE_UI
12
+ @errors = []
13
+ @warnings = []
14
+ @files = []
15
+ @files_to_create = []
16
+ begin
17
+ @output = <<TEMPLATE
18
+ const routes = [
19
+ {
20
+ TEMPLATE
21
+ project_path = Blufin::Projects::get_project_path(project[Blufin::Projects::PROJECT_ID], true)
22
+ routes_file = "#{project_path}/#{project[Blufin::Projects::UI][Blufin::Projects::ROUTES_FILE]}"
23
+ routes_yml = Blufin::Yml::read_file(routes_file, SCHEMA_FILE)
24
+ target_file = "#{project_path}/src/#{Blufin::Strings::remove_surrounding_slashes(routes_yml['RoutesFile'])}"
25
+ layout = routes_yml['Layout']
26
+ pages_root = routes_yml['PagesRoot']
27
+ pages_not_found = routes_yml['Pages404']
28
+ path_pages_ignore = []
29
+ path_to_pages = "#{project_path}/src/#{pages_root}"
30
+ # Add ignore path(s) -- if exists.
31
+ routes_yml['Ignore'].each { |ignore_path| path_pages_ignore << "#{Blufin::Strings::remove_surrounding_slashes(ignore_path)}" } if routes_yml.has_key?('Ignore')
32
+ routes = routes_yml['Routes']
33
+ routes.each_with_index do |route, idx|
34
+ comma = route.has_key?('Children') ? ',' : nil
35
+ parent_path = route['Parent']['Path']
36
+ @output += <<TEMPLATE
37
+ path: '/#{route['Parent'].has_key?('RootPath') && route['Parent']['RootPath'] ? nil : parent_path}',
38
+ sectionName: '#{route['Parent']['Name']}',
39
+ sectionIcon: '#{route['Parent']['Icon']}',
40
+ component: () => import('./../#{layout}')#{comma}
41
+ TEMPLATE
42
+ file = parent_path == HOME_TERM ? "#{pages_root}/#{parent_path}/#{HOME_TERM}.vue" : "#{pages_root}/#{parent_path}/#{parent_path}-#{HOME_TERM}.vue"
43
+ prefix = parent_path == HOME_TERM ? 'dashboard/' : nil
44
+ process_file(file, project_path)
45
+ if route.has_key?('Children')
46
+ @output += <<TEMPLATE
47
+ children: [
48
+ {
49
+ path: '',
50
+ component: () => import('./../#{file}')
51
+ },
52
+ TEMPLATE
53
+ children = route['Children']
54
+ children.each_with_index do |child, idx|
55
+ comma = idx == children.length - 1 ? nil : ','
56
+ file = "#{pages_root}/#{parent_path}/#{child['Path']}.vue"
57
+ disabled = child.has_key?('Disabled') && child['Disabled'] ? "\n disabled: true," : nil
58
+ process_file(file, project_path)
59
+ @output += <<TEMPLATE
60
+ {
61
+ path: '#{prefix}#{child['Path']}',
62
+ name: '#{child['Name']}',#{disabled}
63
+ component: () => import('./../#{file}')
64
+ }#{comma}
65
+ TEMPLATE
66
+ end
67
+ @output += " ]\n"
68
+ end
69
+
70
+ if idx == routes.length - 1
71
+ @output += <<TEMPLATE
72
+ }
73
+ TEMPLATE
74
+ else
75
+ @output += <<TEMPLATE
76
+ },
77
+ {
78
+ TEMPLATE
79
+ end
80
+ end
81
+
82
+ @output += <<TEMPLATE
83
+ ];
84
+
85
+ if (process.env.MODE !== 'ssr') {
86
+ routes.push({
87
+ path: '*',
88
+ component: () => import('./../#{pages_not_found}')
89
+ });
90
+ }
91
+
92
+ export default routes;
93
+ TEMPLATE
94
+ rescue => e
95
+ Blufin::Terminal::print_exception(e)
96
+ end
97
+
98
+ # Write the output to the file.
99
+ Blufin::Files::write_file(target_file, Blufin::Arrays::convert_string_to_line_array(@output))
100
+
101
+ # Output message.
102
+ Blufin::Terminal::info('Generating route file(s):') if @files_to_create.any?
103
+
104
+ # Write all the blank route files.
105
+ @files_to_create.each do |ftc|
106
+ if Blufin::Files::file_exists(ftc)
107
+ Blufin::GenerateBase::output(ftc, Blufin::GenerateBase::OUTPUT_SKIP)
108
+ else
109
+ Blufin::Files::write_file(ftc, Blufin::Arrays::convert_string_to_line_array(get_blank_file_content))
110
+ Blufin::GenerateBase::output(ftc, Blufin::GenerateBase::OUTPUT_GENERATE)
111
+ end
112
+ end
113
+
114
+ puts if @files_to_create.any?
115
+
116
+ # Check for rogue files.
117
+ Blufin::Files::get_files_in_dir(path_to_pages).each do |file|
118
+ next if file =~ /#{path_to_pages}\/(#{path_pages_ignore.join('|')})/
119
+ unless @files_to_create.include?(file)
120
+ @warnings << "Found rogue file: #{Blufin::Terminal::format_invalid(file)}"
121
+ end
122
+ end
123
+
124
+ return @errors, @warnings
125
+
126
+ end
127
+
128
+ private
129
+
130
+ # Ensures that we're not defining a path twice.
131
+ # Creates file if not exists
132
+ # @return void
133
+ def self.process_file(file, project_path)
134
+ Blufin::Terminal::error("Duplicate file: #{Blufin::Terminal::format_invalid(file)}", 'Your configuration would produce a duplicate file. Please fix.') if @files.include?(file)
135
+ @files << file
136
+ @files_to_create << "#{project_path}/src/#{file}"
137
+ end
138
+
139
+ # Returns the contents of a blank file.
140
+ # @return string
141
+ def self.get_blank_file_content
142
+ <<TEMPLATE
143
+ <template>
144
+ <div>
145
+ <base-title h="5" :nudge-right="30" :nudge-up="10">{{ $route.name }}</base-title>
146
+ </div>
147
+ </template>
148
+
149
+ <script type="text/ecmascript-6">
150
+ export default {
151
+ data() {
152
+ return {
153
+ test: this.$route.path
154
+ };
155
+ }
156
+ };
157
+ </script>
158
+
159
+ <style scoped type="text/scss" lang="scss"></style>
160
+ TEMPLATE
161
+ end
162
+
163
+ end
164
+
165
+ end
@@ -1 +1 @@
1
- BLUFIN_LIB_VERSION = '1.7.6'
1
+ BLUFIN_LIB_VERSION = '1.8.0'
@@ -56,7 +56,15 @@ mapping:
56
56
  Remote:
57
57
  required: yes
58
58
  ProjectRoot:
59
- required: yes
59
+ required: no
60
+ Upstream:
61
+ type: seq
62
+ sequence:
63
+ - type: str
64
+ Downstream:
65
+ type: seq
66
+ sequence:
67
+ - type: str
60
68
  Run:
61
69
  type: map
62
70
  mapping:
@@ -137,4 +145,9 @@ mapping:
137
145
  type: seq
138
146
  required: yes
139
147
  sequence:
140
- - type: str
148
+ - type: str
149
+ UI:
150
+ type: map
151
+ mapping:
152
+ RoutesFile:
153
+ required: yes
@@ -0,0 +1,44 @@
1
+ type: map
2
+ mapping:
3
+ Layout:
4
+ required: true
5
+ PagesRoot:
6
+ required: true
7
+ Pages404:
8
+ required: true
9
+ RoutesFile:
10
+ required: true
11
+ Ignore:
12
+ type: seq
13
+ sequence:
14
+ - type: str
15
+ Routes:
16
+ type: seq
17
+ required: true
18
+ sequence:
19
+ - type: map
20
+ mapping:
21
+ Parent:
22
+ type: map
23
+ required: true
24
+ mapping:
25
+ Path:
26
+ required: true
27
+ Name:
28
+ required: true
29
+ Icon:
30
+ required: true
31
+ RootPath:
32
+ type: bool
33
+ Children:
34
+ type: seq
35
+ sequence:
36
+ - type: map
37
+ required: true
38
+ mapping:
39
+ Path:
40
+ required: true
41
+ Name:
42
+ required: true
43
+ Disabled:
44
+ type: bool