blufin-lib 1.7.6 → 1.8.0

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.
@@ -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