blazer 3.0.0 → 3.3.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.
@@ -56,7 +56,7 @@
56
56
  <p v-if="more" class="text-muted">Loading...</p>
57
57
  </div>
58
58
 
59
- <script>
59
+ <%= javascript_tag nonce: true do %>
60
60
  <%= blazer_js_var "dashboards", @dashboards %>
61
61
  <%= blazer_js_var "queries", @queries %>
62
62
  <%= blazer_js_var "more", @more %>
@@ -166,4 +166,4 @@
166
166
  })
167
167
  app.config.compilerOptions.whitespace = "preserve"
168
168
  app.mount("#queries")
169
- </script>
169
+ <% end %>
@@ -4,7 +4,7 @@
4
4
  <% if @only_chart %>
5
5
  <p class="text-muted">Select variables</p>
6
6
  <% else %>
7
- <div class="alert alert-info">Can’t preview queries with variables...yet!</div>
7
+ <div class="alert alert-info">Can’t preview queries with variables</div>
8
8
  <% end %>
9
9
  <% elsif @cohort_analysis %>
10
10
  <% if @cohort_error %>
@@ -46,7 +46,7 @@
46
46
  <% chart_id = SecureRandom.hex %>
47
47
  <% column_types = @result.column_types %>
48
48
  <% chart_type = @result.chart_type %>
49
- <% chart_options = {id: chart_id} %>
49
+ <% chart_options = {id: chart_id, thousands: t("number.format.delimiter"), decimal: t("number.format.separator")} %>
50
50
  <% if ["line", "line2"].include?(chart_type) %>
51
51
  <% chart_options.merge!(min: nil) %>
52
52
  <% end %>
@@ -75,21 +75,21 @@
75
75
  <% if @markers.any? %>
76
76
  <% map_id = SecureRandom.hex %>
77
77
  <%= content_tag :div, nil, id: map_id, style: "height: #{@only_chart ? 300 : 500}px;" %>
78
- <script>
78
+ <%= javascript_tag nonce: true do %>
79
79
  <%= blazer_js_var "mapboxAccessToken", Blazer.mapbox_access_token %>
80
80
  <%= blazer_js_var "markers", @markers %>
81
81
  <%= blazer_js_var "mapId", map_id %>
82
82
  new Mapkick.Map(mapId, markers, {accessToken: mapboxAccessToken, tooltips: {hover: false, html: true}});
83
- </script>
83
+ <% end %>
84
84
  <% elsif @geojson.any? %>
85
85
  <% map_id = SecureRandom.hex %>
86
86
  <%= content_tag :div, nil, id: map_id, style: "height: #{@only_chart ? 300 : 500}px;" %>
87
- <script>
87
+ <%= javascript_tag nonce: true do %>
88
88
  <%= blazer_js_var "mapboxAccessToken", Blazer.mapbox_access_token %>
89
89
  <%= blazer_js_var "geojson", @geojson %>
90
90
  <%= blazer_js_var "mapId", map_id %>
91
91
  new Mapkick.AreaMap(mapId, geojson, {accessToken: mapboxAccessToken, tooltips: {hover: false, html: true}});
92
- </script>
92
+ <% end %>
93
93
  <% elsif chart_type == "line" %>
94
94
  <% chart_data = @columns[1..-1].each_with_index.map{ |k, i| {name: blazer_series_name(k), data: @rows.map{ |r| [r[0], r[i + 1]] }, library: series_library[i]} } %>
95
95
  <%= line_chart chart_data, **chart_options %>
@@ -165,7 +165,7 @@
165
165
  <% end %>
166
166
  <% end %>
167
167
 
168
- <% if v2 = (@smart_values[k] || {})[v.nil? ? v : v.to_s] %>
168
+ <% if (v2 = @smart_values.dig(k, v&.to_s)) %>
169
169
  <div class="text-muted"><%= v2 %></div>
170
170
  <% end %>
171
171
  </td>
@@ -13,7 +13,7 @@
13
13
  <thead>
14
14
  <tr>
15
15
  <th colspan="2">
16
- <% if table[:schema] != "public" %><%= table[:schema] %>.<% end %><%= table[:table] %>
16
+ <% if table[:schema] && table[:schema] != "public" %><%= table[:schema] %>.<% end %><%= table[:table] %>
17
17
  </th>
18
18
  </tr>
19
19
  </thead>
@@ -28,7 +28,7 @@
28
28
  </table>
29
29
  <% end %>
30
30
 
31
- <script>
31
+ <%= javascript_tag nonce: true do %>
32
32
  $("#search").on("keyup", function() {
33
33
  var value = $(this).val().toLowerCase()
34
34
  $(".schema-table").filter(function() {
@@ -52,4 +52,4 @@
52
52
  $(this).toggle(found)
53
53
  })
54
54
  }).focus()
55
- </script>
55
+ <% end %>
@@ -46,7 +46,7 @@
46
46
  <p class="text-muted">Loading...</p>
47
47
  </div>
48
48
 
49
- <script>
49
+ <%= javascript_tag nonce: true do %>
50
50
  function showRun(data) {
51
51
  $("#results").html(data)
52
52
  $("#results table").stupidtable(stupidtableCustomSettings).stickyTableHeaders({fixedOffset: 60})
@@ -59,14 +59,14 @@
59
59
  <%= blazer_js_var "data", @run_data %>
60
60
 
61
61
  runQuery(data, showRun, showError)
62
- </script>
62
+ <% end %>
63
63
  <% end %>
64
64
 
65
- <script>
65
+ <%= javascript_tag nonce: true do %>
66
66
  // do not highlight really long queries
67
67
  // this can lead to performance issues
68
68
  var code = $("#code code")
69
69
  if (code.text().length < 10000) {
70
70
  hljs.highlightElement(code.get(0))
71
71
  }
72
- </script>
72
+ <% end %>
@@ -45,11 +45,11 @@
45
45
  </tbody>
46
46
  </table>
47
47
 
48
- <script>
48
+ <%= javascript_tag nonce: true do %>
49
49
  $("#search").on("keyup", function() {
50
50
  var value = $(this).val().toLowerCase()
51
51
  $("#uploads tbody tr").filter( function() {
52
52
  $(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
53
53
  })
54
54
  }).focus()
55
- </script>
55
+ <% end %>
@@ -5,16 +5,16 @@
5
5
 
6
6
  <meta charset="utf-8" />
7
7
  <%= favicon_link_tag "blazer/favicon.png" %>
8
- <% if defined?(Propshaft::Railtie) %>
8
+ <% if defined?(Propshaft::Railtie) && Rails.application.assets.is_a?(Propshaft::Assembly) %>
9
9
  <%= stylesheet_link_tag "blazer/bootstrap-propshaft", "blazer/bootstrap", "blazer/selectize", "blazer/github", "blazer/daterangepicker", "blazer/application" %>
10
- <%= javascript_include_tag "blazer/jquery", "blazer/rails-ujs", "blazer/stupidtable", "blazer/stupidtable-custom-settings", "blazer/jquery.stickytableheaders", "blazer/selectize", "blazer/highlight.min", "blazer/moment", "blazer/moment-timezone-with-data", "blazer/daterangepicker", "blazer/chart.umd", "blazer/chartjs-adapter-date-fns.bundle", "blazer/chartkick", "blazer/mapkick.bundle", "blazer/ace/ace", "blazer/ace/ext-language_tools", "blazer/ace/theme-twilight", "blazer/ace/mode-sql", "blazer/ace/snippets/text", "blazer/ace/snippets/sql", "blazer/Sortable", "blazer/bootstrap", "blazer/vue.global.prod", "blazer/routes", "blazer/queries", "blazer/fuzzysearch", "blazer/application" %>
10
+ <%= javascript_include_tag "blazer/jquery", "blazer/rails-ujs", "blazer/stupidtable", "blazer/stupidtable-custom-settings", "blazer/jquery.stickytableheaders", "blazer/selectize", "blazer/highlight.min", "blazer/moment", "blazer/moment-timezone-with-data", "blazer/daterangepicker", "blazer/chart.umd", "blazer/chartjs-adapter-date-fns.bundle", "blazer/chartkick", "blazer/mapkick.bundle", "blazer/ace/ace", "blazer/ace/ext-language_tools", "blazer/ace/theme-twilight", "blazer/ace/mode-sql", "blazer/ace/snippets/text", "blazer/ace/snippets/sql", "blazer/Sortable", "blazer/bootstrap", "blazer/vue.global.prod", "blazer/routes", "blazer/queries", "blazer/fuzzysearch", "blazer/application", nonce: true %>
11
11
  <% else %>
12
12
  <%= stylesheet_link_tag "blazer/application" %>
13
- <%= javascript_include_tag "blazer/application" %>
13
+ <%= javascript_include_tag "blazer/application", nonce: true %>
14
14
  <% end %>
15
- <script>
15
+ <%= javascript_tag nonce: true do %>
16
16
  <%= blazer_js_var "rootPath", root_path %>
17
- </script>
17
+ <% end %>
18
18
  <%= csrf_meta_tags %>
19
19
  </head>
20
20
  <body>
@@ -33,7 +33,7 @@ module Blazer
33
33
  # use token so we fetch cached results after query is run
34
34
  client_request_token: request_token,
35
35
  query_execution_context: {
36
- database: database,
36
+ database: database
37
37
  }
38
38
  }
39
39
 
@@ -45,7 +45,7 @@ module Blazer
45
45
  port: uri.port,
46
46
  username: uri.user,
47
47
  password: uri.password,
48
- database: uri.path.sub(/\A\//, ""),
48
+ database: uri.path.delete_prefix("/"),
49
49
  mode: uri.scheme.to_sym
50
50
  )
51
51
  end
@@ -7,14 +7,20 @@ module Blazer
7
7
  error = nil
8
8
 
9
9
  begin
10
- result = session.query("#{statement} /*#{comment}*/", bind_params)
11
- columns = result.columns.map(&:to_s)
12
- rows = []
13
- result.each do |row|
14
- rows << columns.map do |c|
15
- v = row.send(c)
16
- v = v.properties if v.respond_to?(:properties)
17
- v
10
+ if bolt?
11
+ result = session.run("#{statement} /*#{comment}*/", bind_params).to_a
12
+ columns = result.any? ? result.first.keys.map(&:to_s) : []
13
+ rows = result.map(&:values)
14
+ else
15
+ result = session.query("#{statement} /*#{comment}*/", bind_params)
16
+ columns = result.columns.map(&:to_s)
17
+ rows = []
18
+ result.each do |row|
19
+ rows << columns.map do |c|
20
+ v = row.send(c)
21
+ v = v.properties if v.respond_to?(:properties)
22
+ v
23
+ end
18
24
  end
19
25
  end
20
26
  rescue => e
@@ -26,8 +32,13 @@ module Blazer
26
32
  end
27
33
 
28
34
  def tables
29
- result = session.query("CALL db.labels()")
30
- result.rows.map(&:first)
35
+ if bolt?
36
+ result = session.run("CALL db.labels()").to_a
37
+ result.map { |r| r.values.first }
38
+ else
39
+ result = session.query("CALL db.labels()")
40
+ result.rows.map(&:first)
41
+ end
31
42
  end
32
43
 
33
44
  def preview_statement
@@ -52,10 +63,27 @@ module Blazer
52
63
 
53
64
  def session
54
65
  @session ||= begin
55
- require "neo4j/core/cypher_session/adaptors/http"
56
- http_adaptor = Neo4j::Core::CypherSession::Adaptors::HTTP.new(settings["url"])
57
- Neo4j::Core::CypherSession.new(http_adaptor)
66
+ if bolt?
67
+ uri = URI.parse(settings["url"])
68
+ auth = Neo4j::Driver::AuthTokens.basic(uri.user, uri.password)
69
+ database = uri.path.delete_prefix("/")
70
+ uri.user = nil
71
+ uri.password = nil
72
+ uri.path = ""
73
+ Neo4j::Driver::GraphDatabase.driver(uri, auth).session(database: database)
74
+ else
75
+ require "neo4j/core/cypher_session/adaptors/http"
76
+ http_adaptor = Neo4j::Core::CypherSession::Adaptors::HTTP.new(settings["url"])
77
+ Neo4j::Core::CypherSession.new(http_adaptor)
78
+ end
79
+ end
80
+ end
81
+
82
+ def bolt?
83
+ if !defined?(@bolt)
84
+ @bolt = settings["url"].start_with?("bolt")
58
85
  end
86
+ @bolt
59
87
  end
60
88
  end
61
89
  end
@@ -39,10 +39,11 @@ module Blazer
39
39
  def client
40
40
  @client ||= begin
41
41
  uri = URI.parse(settings["url"])
42
- query = uri.query ? CGI::parse(uri.query) : {}
43
- Presto::Client.new(
42
+ query = uri.query ? CGI.parse(uri.query) : {}
43
+ cls = uri.scheme == "trino" ? Trino::Client : Presto::Client
44
+ cls.new(
44
45
  server: "#{uri.host}:#{uri.port}",
45
- catalog: uri.path.to_s.sub(/\A\//, ""),
46
+ catalog: uri.path.to_s.delete_prefix("/"),
46
47
  schema: query["schema"] || "public",
47
48
  user: uri.user,
48
49
  http_debug: false
@@ -21,16 +21,42 @@ module Blazer
21
21
  error = nil
22
22
 
23
23
  begin
24
- in_transaction do
24
+ types = []
25
+ in_transaction do |connection|
25
26
  set_timeout(data_source.timeout) if data_source.timeout
26
-
27
27
  binds = bind_params.map { |v| ActiveRecord::Relation::QueryAttribute.new(nil, v, ActiveRecord::Type::Value.new) }
28
- result = connection_model.connection.select_all("#{statement} /*#{comment}*/", nil, binds)
29
- columns = result.columns
30
- result.rows.each do |untyped_row|
31
- rows << (result.column_types.empty? ? untyped_row : columns.each_with_index.map { |c, i| untyped_row[i] && result.column_types[c] ? result.column_types[c].send(:cast_value, untyped_row[i]) : untyped_row[i] })
28
+ if sqlite?
29
+ type_map = connection.send(:type_map)
30
+ connection.raw_connection.prepare("#{statement} /*#{comment}*/") do |stmt|
31
+ stmt.bind_params(connection.send(:type_casted_binds, binds))
32
+ columns = stmt.columns
33
+ rows = stmt.to_a
34
+ types = stmt.types.map { |t| type_map.lookup(t) }
35
+ end
36
+ else
37
+ result = connection.select_all("#{statement} /*#{comment}*/", nil, binds)
38
+ columns = result.columns
39
+ rows = result.rows
40
+ if result.column_types.any?
41
+ types = columns.size.times.map { |i| result.column_types[i] }
42
+ end
32
43
  end
33
44
  end
45
+
46
+ # cast values
47
+ if types.any?
48
+ rows =
49
+ rows.map do |row|
50
+ row.map.with_index do |v, i|
51
+ v && (t = types[i]) ? t.send(:cast_value, v) : v
52
+ end
53
+ end
54
+ end
55
+
56
+ # fix for non-ASCII column names and charts
57
+ if adapter_name == "Trilogy"
58
+ columns = columns.map { |k| k.dup.force_encoding(Encoding::UTF_8) }
59
+ end
34
60
  rescue => e
35
61
  error = e.message.sub(/.+ERROR: /, "")
36
62
  error = Blazer::TIMEOUT_MESSAGE if Blazer::TIMEOUT_ERRORS.any? { |e| error.include?(e) }
@@ -45,7 +71,12 @@ module Blazer
45
71
  end
46
72
 
47
73
  def tables
48
- sql = add_schemas("SELECT table_schema, table_name FROM information_schema.tables")
74
+ sql =
75
+ if sqlite?
76
+ "SELECT NULL, name FROM sqlite_master WHERE type IN ('table', 'view') ORDER BY name"
77
+ else
78
+ add_schemas("SELECT table_schema, table_name FROM information_schema.tables")
79
+ end
49
80
  result = data_source.run_statement(sql, refresh_cache: true)
50
81
  if postgresql? || redshift? || snowflake?
51
82
  result.rows.sort_by { |r| [r[0] == default_schema ? "" : r[0], r[1]] }.map do |row|
@@ -69,7 +100,12 @@ module Blazer
69
100
  end
70
101
 
71
102
  def schema
72
- sql = add_schemas("SELECT table_schema, table_name, column_name, data_type, ordinal_position FROM information_schema.columns")
103
+ sql =
104
+ if sqlite?
105
+ "SELECT NULL, t.name, c.name, c.type, c.cid FROM sqlite_master t INNER JOIN pragma_table_info(t.name) c WHERE t.type IN ('table', 'view')"
106
+ else
107
+ add_schemas("SELECT table_schema, table_name, column_name, data_type, ordinal_position FROM information_schema.columns")
108
+ end
73
109
  result = data_source.run_statement(sql)
74
110
  result.rows.group_by { |r| [r[0], r[1]] }.map { |k, vs| {schema: k[0], table: k[1], columns: vs.sort_by { |v| v[2] }.map { |v| {name: v[2], data_type: v[3]} }} }.sort_by { |t| [t[:schema] == default_schema ? "" : t[:schema], t[:table]] }
75
111
  end
@@ -135,7 +171,7 @@ module Blazer
135
171
  def cohort_analysis_statement(statement, period:, days:)
136
172
  raise "Cohort analysis not supported" unless supports_cohort_analysis?
137
173
 
138
- cohort_column = statement =~ /\bcohort_time\b/ ? "cohort_time" : "conversion_time"
174
+ cohort_column = statement.match?(/\bcohort_time\b/) ? "cohort_time" : "conversion_time"
139
175
  tzname = Blazer.time_zone.tzinfo.name
140
176
 
141
177
  if mysql?
@@ -194,10 +230,10 @@ module Blazer
194
230
 
195
231
  # Redshift adapter silently ignores binds
196
232
  def parameter_binding
197
- if postgresql? && (ActiveRecord::VERSION::STRING.to_f >= 6.1 || prepared_statements?)
198
- # Active Record < 6.1 silently ignores binds with Postgres when prepared statements are disabled
233
+ if postgresql?
199
234
  :numeric
200
- elsif sqlite?
235
+ elsif sqlite? && prepared_statements?
236
+ # Active Record silently ignores binds with SQLite when prepared statements are disabled
201
237
  :numeric
202
238
  elsif mysql? && prepared_statements?
203
239
  # Active Record silently ignores binds with MySQL when prepared statements are disabled
@@ -259,6 +295,8 @@ module Blazer
259
295
  "public"
260
296
  elsif sqlserver?
261
297
  "dbo"
298
+ elsif sqlite?
299
+ nil
262
300
  elsif connection_model.respond_to?(:connection_db_config)
263
301
  connection_model.connection_db_config.database
264
302
  else
@@ -287,8 +325,7 @@ module Blazer
287
325
  if postgresql? || redshift?
288
326
  execute("SET #{use_transaction? ? "LOCAL " : ""}statement_timeout = #{timeout.to_i * 1000}")
289
327
  elsif mysql?
290
- # use send as this method is private in Rails 4.2
291
- mariadb = connection_model.connection.send(:mariadb?) rescue false
328
+ mariadb = connection_model.connection.mariadb? rescue false
292
329
  if mariadb
293
330
  execute("SET max_statement_time = #{timeout.to_i * 1000}")
294
331
  else
@@ -304,14 +341,14 @@ module Blazer
304
341
  end
305
342
 
306
343
  def in_transaction
307
- connection_model.connection_pool.with_connection do
344
+ connection_model.connection_pool.with_connection do |connection|
308
345
  if use_transaction?
309
346
  connection_model.transaction do
310
- yield
347
+ yield connection
311
348
  raise ActiveRecord::Rollback
312
349
  end
313
350
  else
314
- yield
351
+ yield connection
315
352
  end
316
353
  end
317
354
  end
@@ -15,3 +15,4 @@ Blazer.register_adapter "soda", Blazer::Adapters::SodaAdapter
15
15
  Blazer.register_adapter "spark", Blazer::Adapters::SparkAdapter
16
16
  Blazer.register_adapter "sql", Blazer::Adapters::SqlAdapter
17
17
  Blazer.register_adapter "snowflake", Blazer::Adapters::SnowflakeAdapter
18
+ Blazer.register_adapter "trino", Blazer::Adapters::PrestoAdapter
@@ -107,7 +107,7 @@ module Blazer
107
107
  end
108
108
 
109
109
  unless result
110
- comment = "blazer"
110
+ comment = "blazer".dup
111
111
  if options[:user].respond_to?(:id)
112
112
  comment << ",user_id:#{options[:user].id}"
113
113
  end
@@ -256,7 +256,7 @@ module Blazer
256
256
  def detect_adapter
257
257
  scheme = settings["url"].to_s.split("://").first
258
258
  case scheme
259
- when "presto", "cassandra", "ignite"
259
+ when "presto", "trino", "cassandra", "ignite"
260
260
  scheme
261
261
  else
262
262
  "sql"
data/lib/blazer/engine.rb CHANGED
@@ -3,22 +3,24 @@ module Blazer
3
3
  isolate_namespace Blazer
4
4
 
5
5
  initializer "blazer" do |app|
6
- if defined?(Sprockets) && Sprockets::VERSION.to_i >= 4
7
- app.config.assets.precompile += [
8
- "blazer/application.js",
9
- "blazer/application.css",
10
- "blazer/glyphicons-halflings-regular.eot",
11
- "blazer/glyphicons-halflings-regular.svg",
12
- "blazer/glyphicons-halflings-regular.ttf",
13
- "blazer/glyphicons-halflings-regular.woff",
14
- "blazer/glyphicons-halflings-regular.woff2",
15
- "blazer/favicon.png"
16
- ]
17
- else
18
- # use a proc instead of a string
19
- app.config.assets.precompile << proc { |path| path =~ /\Ablazer\/application\.(js|css)\z/ }
20
- app.config.assets.precompile << proc { |path| path =~ /\Ablazer\/.+\.(eot|svg|ttf|woff|woff2)\z/ }
21
- app.config.assets.precompile << proc { |path| path == "blazer/favicon.png" }
6
+ if app.config.respond_to?(:assets) && defined?(Sprockets)
7
+ if Sprockets::VERSION.to_i >= 4
8
+ app.config.assets.precompile += [
9
+ "blazer/application.js",
10
+ "blazer/application.css",
11
+ "blazer/glyphicons-halflings-regular.eot",
12
+ "blazer/glyphicons-halflings-regular.svg",
13
+ "blazer/glyphicons-halflings-regular.ttf",
14
+ "blazer/glyphicons-halflings-regular.woff",
15
+ "blazer/glyphicons-halflings-regular.woff2",
16
+ "blazer/favicon.png"
17
+ ]
18
+ else
19
+ # use a proc instead of a string
20
+ app.config.assets.precompile << proc { |path| path =~ /\Ablazer\/application\.(js|css)\z/ }
21
+ app.config.assets.precompile << proc { |path| path =~ /\Ablazer\/.+\.(eot|svg|ttf|woff|woff2)\z/ }
22
+ app.config.assets.precompile << proc { |path| path == "blazer/favicon.png" }
23
+ end
22
24
  end
23
25
 
24
26
  Blazer.time_zone ||= Blazer.settings["time_zone"] || Time.zone
data/lib/blazer/result.rb CHANGED
@@ -5,7 +5,7 @@ module Blazer
5
5
 
6
6
  def initialize(data_source, columns, rows, error, cached_at, just_cached)
7
7
  @data_source = data_source
8
- @columns = columns
8
+ @columns = columns.dup
9
9
  @rows = rows
10
10
  @error = error
11
11
  @cached_at = cached_at
@@ -131,7 +131,7 @@ module Blazer
131
131
 
132
132
  if chart_type == "line"
133
133
  columns[1..-1].each_with_index.each do |k, i|
134
- series << {name: k, data: rows.map{ |r| [r[0], r[i + 1]] }}
134
+ series << {name: k, data: rows.map { |r| [r[0], r[i + 1]] }}
135
135
  end
136
136
  else
137
137
  rows.group_by { |r| v = r[1]; (smart_values[columns[1]] || {})[v.to_s] || v }.each_with_index.map do |(name, v), i|
@@ -13,7 +13,8 @@ module Blazer
13
13
  audit = Blazer::Audit.new(statement: audit_statement)
14
14
  audit.query = query
15
15
  audit.data_source = data_source.id
16
- audit.user = options[:user]
16
+ # only set user if present to avoid error with Rails 7.1 when no user model
17
+ audit.user = options[:user] unless options[:user].nil?
17
18
  audit.save!
18
19
  end
19
20
 
@@ -33,9 +33,12 @@ module Blazer
33
33
  end
34
34
 
35
35
  unless value.is_a?(ActiveSupport::TimeWithZone)
36
- if value =~ /\A\d+\z/
37
- value = value.to_i
38
- elsif value =~ /\A\d+\.\d+\z/
36
+ if value.match?(/\A\d+\z/)
37
+ # check no leading zeros (when not zero)
38
+ if value == value.to_i.to_s
39
+ value = value.to_i
40
+ end
41
+ elsif value.match?(/\A\d+\.\d+\z/)
39
42
  value = value.to_f
40
43
  end
41
44
  end
@@ -1,3 +1,3 @@
1
1
  module Blazer
2
- VERSION = "3.0.0"
2
+ VERSION = "3.3.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blazer
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2023-09-20 00:00:00.000000000 Z
10
+ date: 2025-04-13 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: railties
@@ -16,28 +15,28 @@ dependencies:
16
15
  requirements:
17
16
  - - ">="
18
17
  - !ruby/object:Gem::Version
19
- version: '6.1'
18
+ version: '7.1'
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - ">="
25
24
  - !ruby/object:Gem::Version
26
- version: '6.1'
25
+ version: '7.1'
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: activerecord
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - ">="
32
31
  - !ruby/object:Gem::Version
33
- version: '6.1'
32
+ version: '7.1'
34
33
  type: :runtime
35
34
  prerelease: false
36
35
  version_requirements: !ruby/object:Gem::Requirement
37
36
  requirements:
38
37
  - - ">="
39
38
  - !ruby/object:Gem::Version
40
- version: '6.1'
39
+ version: '7.1'
41
40
  - !ruby/object:Gem::Dependency
42
41
  name: chartkick
43
42
  requirement: !ruby/object:Gem::Requirement
@@ -66,7 +65,20 @@ dependencies:
66
65
  - - ">="
67
66
  - !ruby/object:Gem::Version
68
67
  version: '0.4'
69
- description:
68
+ - !ruby/object:Gem::Dependency
69
+ name: csv
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
70
82
  email: andrew@ankane.org
71
83
  executables: []
72
84
  extensions: []
@@ -222,7 +234,6 @@ homepage: https://github.com/ankane/blazer
222
234
  licenses:
223
235
  - MIT
224
236
  metadata: {}
225
- post_install_message:
226
237
  rdoc_options: []
227
238
  require_paths:
228
239
  - lib
@@ -230,15 +241,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
230
241
  requirements:
231
242
  - - ">="
232
243
  - !ruby/object:Gem::Version
233
- version: '3'
244
+ version: '3.2'
234
245
  required_rubygems_version: !ruby/object:Gem::Requirement
235
246
  requirements:
236
247
  - - ">="
237
248
  - !ruby/object:Gem::Version
238
249
  version: '0'
239
250
  requirements: []
240
- rubygems_version: 3.4.10
241
- signing_key:
251
+ rubygems_version: 3.6.2
242
252
  specification_version: 4
243
253
  summary: Explore your data with SQL. Easily create charts and dashboards, and share
244
254
  them with your team.