mongodb_logger 0.4.2 → 0.5.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.
Files changed (94) hide show
  1. data/.gitignore +2 -1
  2. data/.rvmrc +1 -1
  3. data/.travis.yml +5 -4
  4. data/Appraisals +9 -0
  5. data/CHANGELOG.md +20 -3
  6. data/README.md +58 -35
  7. data/Rakefile +33 -96
  8. data/app/assets/javascripts/analytics.js.coffee +5 -4
  9. data/app/assets/javascripts/logs.js.coffee +11 -11
  10. data/app/assets/javascripts/mongodb_logger.js +2 -1
  11. data/app/assets/javascripts/vendors/jquery-1.9.1.min.js +5 -0
  12. data/app/assets/javascripts/vendors/jquery.pjax.min.js +15 -13
  13. data/app/assets/javascripts/vendors/mustache.min.js +10 -0
  14. data/app/assets/stylesheets/layout.css +20 -15
  15. data/app/assets/stylesheets/mongodb_logger.css +0 -3
  16. data/bin/mongodb_logger_web +1 -2
  17. data/features/rails.feature +20 -8
  18. data/features/step_definitions/mongodb_logger_web_steps.rb +2 -3
  19. data/features/step_definitions/rails_application_steps.rb +84 -58
  20. data/features/step_definitions/rake_steps.rb +10 -0
  21. data/features/support/aruba.rb +5 -0
  22. data/features/support/env.rb +11 -5
  23. data/features/support/rails.rb +55 -67
  24. data/gemfiles/rails31.gemfile +8 -0
  25. data/gemfiles/rails32.gemfile +8 -0
  26. data/lib/mongodb_logger.rb +10 -3
  27. data/lib/mongodb_logger/adapters/base.rb +5 -1
  28. data/lib/mongodb_logger/adapters/mongo.rb +17 -13
  29. data/lib/mongodb_logger/adapters/moped.rb +17 -11
  30. data/lib/mongodb_logger/capistrano.rb +8 -0
  31. data/lib/mongodb_logger/logger.rb +13 -26
  32. data/lib/mongodb_logger/rack_middleware.rb +24 -0
  33. data/lib/mongodb_logger/railtie.rb +4 -4
  34. data/lib/mongodb_logger/server.rb +43 -68
  35. data/lib/mongodb_logger/server/helpers.rb +5 -0
  36. data/lib/mongodb_logger/server/{content_for.rb → helpers/content_for.rb} +0 -0
  37. data/lib/mongodb_logger/server/helpers/mustache_helpers.rb +66 -0
  38. data/lib/mongodb_logger/server/{partials.rb → helpers/partials.rb} +0 -0
  39. data/lib/mongodb_logger/server/{sprokets.rb → helpers/sprokets.rb} +4 -4
  40. data/lib/mongodb_logger/server/{view_helpers.rb → helpers/view_helpers.rb} +22 -38
  41. data/lib/mongodb_logger/server/model.rb +4 -0
  42. data/lib/mongodb_logger/server/model/additional_filter.rb +15 -23
  43. data/lib/mongodb_logger/server/model/analytic.rb +24 -32
  44. data/lib/mongodb_logger/server/model/base.rb +21 -0
  45. data/lib/mongodb_logger/server/model/filter.rb +12 -20
  46. data/lib/mongodb_logger/server/mustache/logs/info.rb +15 -0
  47. data/lib/mongodb_logger/server/templates/logs/info.mustache +25 -0
  48. data/lib/mongodb_logger/server/views/layout.erb +28 -26
  49. data/lib/mongodb_logger/server/views/shared/_log.erb +1 -1
  50. data/lib/mongodb_logger/server/views/shared/_tabs.erb +2 -2
  51. data/lib/mongodb_logger/server/views/shared/layout/_mustache.erb +6 -0
  52. data/lib/mongodb_logger/server/views/show_log.erb +2 -2
  53. data/lib/mongodb_logger/server_config.rb +4 -4
  54. data/lib/mongodb_logger/utils/migrate.rb +50 -0
  55. data/lib/mongodb_logger/utils/progressbar.rb +79 -0
  56. data/lib/mongodb_logger/version.rb +1 -1
  57. data/mongodb_logger.gemspec +15 -8
  58. data/{test/config/samples → spec/factories/config}/database.yml +0 -0
  59. data/{test/config/samples → spec/factories/config}/database_no_file_logging.yml +0 -0
  60. data/{test/config/samples → spec/factories/config}/database_replica_set.yml +0 -0
  61. data/{test/config/samples → spec/factories/config}/database_with_auth.yml +0 -0
  62. data/spec/factories/config/database_with_capsize.yml +9 -0
  63. data/{test/config/samples → spec/factories/config}/database_with_collection.yml +0 -0
  64. data/{test/config/samples → spec/factories/config}/database_with_url.yml +0 -0
  65. data/{test/config/samples → spec/factories/config}/mongodb_logger.yml +0 -0
  66. data/{test/config/samples → spec/factories/config}/mongoid.yml +0 -0
  67. data/{test/config/samples → spec/factories/config}/server_config.yml +0 -0
  68. data/spec/javascripts/support/jasmine.yml +1 -5
  69. data/spec/mongodb_logger_spec.rb +257 -0
  70. data/spec/rails_spec/controllers/tests_controller_spec_rails.rb +128 -0
  71. data/spec/rails_spec/spec_helper_rails.rb +13 -0
  72. data/spec/spec_helper.rb +17 -0
  73. data/{test/test_helper.rb → spec/support/mongodb_logger_helper.rb} +44 -63
  74. data/spec/support/mongodb_logger_macros.rb +22 -0
  75. data/spec/support/rails.rb +40 -0
  76. data/spec/utils/migrate_spec.rb +32 -0
  77. data/{lib/tasks → tasks}/mongodb_logger.rake +14 -6
  78. metadata +198 -78
  79. data/SUPPORTED_RAILS_VERSIONS +0 -16
  80. data/TESTING.md +0 -24
  81. data/app/assets/javascripts/vendors/jquery-1.8.3.min.js +0 -2
  82. data/features/support/terminal.rb +0 -95
  83. data/lib/mongodb_logger/server/views/shared/_log_info.erb +0 -27
  84. data/test/Gemfile_tests +0 -9
  85. data/test/active_record.rb +0 -13
  86. data/test/log/.gitkeep +0 -0
  87. data/test/rails.rb +0 -22
  88. data/test/rails/app/controllers/order_controller.rb +0 -23
  89. data/test/rails/test/functional/order_controller_test.rb +0 -116
  90. data/test/rails/test/test_helper.rb +0 -10
  91. data/test/shoulda_macros/log_macros.rb +0 -13
  92. data/test/test.sh +0 -7
  93. data/test/unit/mongodb_logger_replica_test.rb +0 -56
  94. data/test/unit/mongodb_logger_test.rb +0 -307
@@ -15,7 +15,7 @@ module MongodbLogger
15
15
  # Looks for configuration files in this order
16
16
  CONFIGURATION_FILES = ["mongodb_logger.yml", "mongoid.yml", "database.yml"]
17
17
  LOG_LEVEL_SYM = [:debug, :info, :warn, :error, :fatal, :unknown]
18
-
18
+
19
19
  ADAPTERS = [
20
20
  ["mongo", Adapers::Mongo],
21
21
  ["moped", Adapers::Moped]
@@ -84,11 +84,11 @@ module MongodbLogger
84
84
  rescue
85
85
  begin
86
86
  # try to nice serialize record
87
- nice_serialize @mongo_record
87
+ record_serializer @mongo_record, true
88
88
  @insert_block.call
89
89
  rescue
90
- # do extra work to inpect (and flatten)
91
- force_serialize @mongo_record
90
+ # do extra work to inspect (and flatten)
91
+ record_serializer @mongo_record, false
92
92
  @insert_block.call rescue nil
93
93
  end
94
94
  end
@@ -137,7 +137,7 @@ module MongodbLogger
137
137
  end
138
138
  config
139
139
  end
140
-
140
+
141
141
  def find_adapter
142
142
  ADAPTERS.each do |(library, adapter)|
143
143
  begin
@@ -171,15 +171,12 @@ module MongodbLogger
171
171
  end
172
172
 
173
173
  # try to serialyze data by each key and found invalid object
174
- def nice_serialize(rec)
175
- if msgs = rec[:messages]
176
- msgs.each do |i, j|
177
- msgs[i] = nice_serialize_object(j)
178
- end
179
- end
180
- if pms = rec[:params]
181
- pms.each do |i, j|
182
- pms[i] = nice_serialize_object(j)
174
+ def record_serializer(rec, nice = true)
175
+ [:messages, :params].each do |key|
176
+ if msgs = rec[key]
177
+ msgs.each do |i, j|
178
+ msgs[i] = (true == nice ? nice_serialize_object(j) : j.inspect)
179
+ end
183
180
  end
184
181
  end
185
182
  end
@@ -190,10 +187,10 @@ module MongodbLogger
190
187
  data
191
188
  when Hash
192
189
  hvalues = Hash.new
193
- data.each{|k,v| hvalues[k] = nice_serialize_object(v) }
190
+ data.each{ |k,v| hvalues[k] = nice_serialize_object(v) }
194
191
  hvalues
195
192
  when Array
196
- data.map{|v| nice_serialize_object(v) }
193
+ data.map{ |v| nice_serialize_object(v) }
197
194
  when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile # uploaded files
198
195
  hvalues = {
199
196
  original_filename: data.original_filename,
@@ -204,15 +201,5 @@ module MongodbLogger
204
201
  end
205
202
  end
206
203
 
207
- # force the data in the db by inspecting each top level array and hash element
208
- # this will flatten other hashes and arrays
209
- def force_serialize(rec)
210
- if msgs = rec[:messages]
211
- msgs.each { |i, j| msgs[i] = j.inspect }
212
- end
213
- if pms = rec[:params]
214
- pms.each { |i, j| pms[i] = j.inspect }
215
- end
216
- end
217
204
  end
218
205
  end
@@ -0,0 +1,24 @@
1
+ # Rack middleware for mounted rack app (e.g Grape)
2
+ module MongodbLogger
3
+ class RackMiddleware
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ request = ::Rack::Request.new env
10
+ path = request.path.split('/')
11
+ log_attrs = {:method => request.request_method,
12
+ :action => path[2..-1].join('/'),
13
+ :controller => path[1],
14
+ :path => request.path,
15
+ :url => request.url,
16
+ :params => request.params,
17
+ :ip => env['REMOTE_ADDR'] }
18
+
19
+ Rails.logger.mongoize(log_attrs) do
20
+ return @app.call(env)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -2,15 +2,15 @@ require 'mongodb_logger/initializer_mixin'
2
2
  module MongodbLogger
3
3
  class Railtie < Rails::Railtie
4
4
  include MongodbLogger::InitializerMixin
5
-
5
+
6
6
  initializer :initialize_mongodb_logger, :before => :initialize_logger do
7
7
  app_config = Rails.application.config
8
8
  Rails.logger = config.logger = create_logger(app_config)
9
9
  end
10
-
10
+
11
11
  rake_tasks do
12
- load "tasks/mongodb_logger.rake"
12
+ load File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "tasks", "mongodb_logger.rake"))
13
13
  end
14
-
14
+
15
15
  end
16
16
  end
@@ -1,69 +1,52 @@
1
1
  require 'sinatra/base'
2
2
  require 'erubis'
3
- require 'json'
3
+ require 'multi_json'
4
4
  require 'active_support'
5
+ require 'mustache/sinatra'
5
6
 
6
- require 'mongodb_logger/server/view_helpers'
7
- require 'mongodb_logger/server/partials'
8
- require 'mongodb_logger/server/content_for'
9
- require 'mongodb_logger/server/sprokets'
10
-
11
- require 'mongodb_logger/server/model/additional_filter'
12
- require 'mongodb_logger/server/model/filter'
13
- require 'mongodb_logger/server/model/analytic'
14
-
7
+ require 'mongodb_logger/server/helpers'
8
+ require 'mongodb_logger/server/model'
15
9
  require 'mongodb_logger/server_config'
16
10
 
17
- if defined? Encoding
18
- Encoding.default_external = Encoding::UTF_8
19
- end
11
+ Encoding.default_external = Encoding::UTF_8 if defined?(Encoding)
20
12
 
21
13
  module MongodbLogger
22
14
  class Server < Sinatra::Base
15
+ module Views; end
16
+
17
+ register Mustache::Sinatra
23
18
  helpers Sinatra::ViewHelpers
24
19
  helpers Sinatra::Partials
25
20
  helpers Sinatra::ContentFor
26
-
21
+
27
22
  dir = File.dirname(File.expand_path(__FILE__))
28
23
 
29
24
  set :views, "#{dir}/server/views"
30
25
  #set :environment, :production
31
26
  set :static, true
27
+ set :mustache, {
28
+ templates: "#{dir}/server/templates",
29
+ views: "#{dir}/server/mustache"
30
+ }
32
31
 
33
32
  helpers do
34
33
  include Rack::Utils
35
- alias_method :h, :escape_html
36
- # pipeline
37
34
  include AssetHelpers
38
-
39
- def current_page
40
- url_path request.path_info.sub('/','')
41
- end
42
-
43
- def class_if_current(path = '')
44
- 'class="active"' if current_page[0, path.size] == path
45
- end
46
-
47
- def url_path(*path_parts)
48
- [ path_prefix, path_parts ].join("/").squeeze('/')
49
- end
35
+ include MustacheHelpers
36
+ alias_method :h, :escape_html
50
37
  alias_method :u, :url_path
51
-
52
- def path_prefix
53
- request.env['SCRIPT_NAME']
54
- end
55
-
56
38
  end
57
-
39
+
58
40
  before do
59
41
  begin
42
+ @main_dir = dir
60
43
  @mongo_adapter = (ServerConfig.mongo_adapter ? ServerConfig.mongo_adapter : Rails.logger.mongo_adapter)
61
44
  @collection_stats = @mongo_adapter.collection_stats
62
45
  rescue => e
63
46
  erb :error, {:layout => false}, :error => "Can't connect to MongoDB!"
64
47
  return false
65
48
  end
66
-
49
+
67
50
  cache_control :private, :must_revalidate, :max_age => 0
68
51
  end
69
52
 
@@ -71,7 +54,7 @@ module MongodbLogger
71
54
  begin
72
55
  erb page.to_sym, {:layout => layout}
73
56
  rescue => e
74
- erb :error, {:layout => false}, :error => "Error in view. Debug: #{e.inspect}"
57
+ erb :error, { :layout => false }, :error => "Error in view. Debug: #{e.inspect}"
75
58
  end
76
59
  end
77
60
 
@@ -80,55 +63,47 @@ module MongodbLogger
80
63
  redirect url_path(:overview)
81
64
  end
82
65
 
83
- %w( overview ).each do |page|
84
- get "/#{page}/?" do
85
- @filter = ServerModel::Filter.new(params[:filter])
86
- @logs = @mongo_adapter.filter_by_conditions(@filter)
87
- show page, !request.xhr?
88
- end
66
+ get "/overview/?" do
67
+ @filter = ServerModel::Filter.new(params[:filter])
68
+ @logs = @mongo_adapter.filter_by_conditions(@filter)
69
+ show :overview, !request.xhr?
89
70
  end
90
-
71
+
72
+ # log info
73
+ get "/log/:id" do
74
+ @log = @mongo_adapter.find_by_id(params[:id])
75
+ show :show_log, !request.xhr?
76
+ end
77
+
91
78
  get "/tail_logs/?:log_last_id?" do
92
79
  @info = @mongo_adapter.tail_log_from_params(params)
93
80
  @info.merge!(
94
- :content => @info[:logs].map{|log| partial(:"shared/log", :object => log) }.join("\n"),
81
+ :content => @info[:logs].map{ |log| partial(:"shared/log", :object => log) }.join("\n"),
95
82
  :collection_stats => partial(:"shared/collection_stats", :object => @collection_stats)
96
83
  )
97
- content_type :json
98
- @info.to_json
84
+ content_type :json
85
+ MultiJson.dump(@info)
99
86
  end
100
-
87
+
101
88
  get "/changed_filter/:type" do
102
89
  type_id = ServerModel::AdditionalFilter.get_type_index params[:type]
103
90
  conditions = ServerModel::AdditionalFilter::VAR_TYPE_CONDITIONS[type_id]
104
91
  values = ServerModel::AdditionalFilter::VAR_TYPE_VALUES[type_id]
105
-
92
+
106
93
  content_type :json
107
- {
94
+ MultiJson.dump({
108
95
  :type_id => type_id,
109
96
  :conditions => conditions,
110
97
  :values => values
111
- }.to_json
98
+ })
112
99
  end
113
-
114
- # log info
115
- get "/log/:id" do
116
- @log = @mongo_adapter.find_by_id(params[:id])
117
- show :show_log, !request.xhr?
118
- end
119
-
120
- # log info right
121
- get "/log_info/:id" do
122
- @log = @mongo_adapter.find_by_id(params[:id])
123
- partial(:"shared/log_info", :object => @log)
124
- end
125
-
100
+
126
101
  get "/add_filter/?" do
127
102
  @filter = ServerModel::Filter.new(nil)
128
103
  @filter_more = ServerModel::AdditionalFilter.new(nil, @filter)
129
104
  partial(:"shared/dynamic_filter", :object => @filter_more)
130
105
  end
131
-
106
+
132
107
  # analytics
133
108
  %w( analytics ).each do |page|
134
109
  get "/#{page}/?" do
@@ -138,13 +113,13 @@ module MongodbLogger
138
113
  post "/#{page}/?" do
139
114
  @analytic = ServerModel::Analytic.new(@mongo_adapter, params[:analytic])
140
115
  content_type :json
141
- @analytic.get_data.to_json
116
+ MultiJson.dump(@analytic.get_data)
142
117
  end
143
118
  end
144
-
119
+
145
120
  error do
146
- erb :error, {:layout => false}, :error => 'Sorry there was a nasty error. Maybe no connection to MongoDB. Debug: ' + env['sinatra.error'].inspect + '<br />' + env.inspect
121
+ erb :error, { :layout => false }, :error => 'Sorry there was a nasty error. Maybe no connection to MongoDB. Debug: ' + env['sinatra.error'].inspect + '<br />' + env.inspect
147
122
  end
148
-
123
+
149
124
  end
150
125
  end
@@ -0,0 +1,5 @@
1
+ require 'mongodb_logger/server/helpers/view_helpers'
2
+ require 'mongodb_logger/server/helpers/partials'
3
+ require 'mongodb_logger/server/helpers/content_for'
4
+ require 'mongodb_logger/server/helpers/sprokets'
5
+ require 'mongodb_logger/server/helpers/mustache_helpers'
@@ -0,0 +1,66 @@
1
+ require 'multi_json'
2
+ module MongodbLogger
3
+ module MustacheHelpers
4
+ include Rack::Utils
5
+
6
+ def url_path(*path_parts)
7
+ [ path_prefix, path_parts ].join("/").squeeze('/')
8
+ end
9
+
10
+ def path_prefix
11
+ request.env['SCRIPT_NAME']
12
+ end
13
+
14
+ def current_page
15
+ url_path request.path_info.sub('/','')
16
+ end
17
+
18
+ def class_if_current(path = '')
19
+ 'class="active"' if current_page[0, path.size] == path
20
+ end
21
+
22
+ def string_from_log_message(message)
23
+ message.is_a?(Array) ? message.join("\n") : message.to_s
24
+ end
25
+
26
+ def pretty_hash(hash)
27
+ begin
28
+ Marshal::dump(hash)
29
+ h(hash.to_yaml).gsub(" ", "&nbsp; ")
30
+ rescue Exception => e # errors from Marshal or YAML
31
+ # Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback
32
+ h(hash.inspect)
33
+ end
34
+ end
35
+
36
+ def log_data(log)
37
+ main_msg = "No message"
38
+ if log['messages'] && log['messages']['info']
39
+ main_msg = string_from_log_message(log['messages']['info']).truncate(300, :separator => ' ')
40
+ end
41
+ if log['is_exception'] && log['messages'] && log['messages']['error']
42
+ main_msg = string_from_log_message(log['messages']['error']).truncate(300, :separator => ' ')
43
+ end
44
+ # return value
45
+ {
46
+ '_id' => log['_id'].to_s,
47
+ 'web_url' => url_path("log/#{log['_id']}"),
48
+ 'main_msg' => main_msg,
49
+ 'is_exception_class' => (log['is_exception'] ? 'failure' : 'success'),
50
+ 'method' => log['method'],
51
+ 'url' => log['url'],
52
+ 'request_time' => log['request_time'],
53
+ 'ip' => log['ip'],
54
+ 'params' => pretty_hash(log['params'])
55
+ }
56
+ end
57
+
58
+ def log_data_json(log)
59
+ MultiJson.dump(log_data(log))
60
+ end
61
+ end
62
+
63
+ class MustacheHelpersObj
64
+ extend MustacheHelpers
65
+ end
66
+ end
@@ -3,7 +3,7 @@ require 'rack/utils'
3
3
  require 'sprockets'
4
4
 
5
5
  module MongodbLogger
6
-
6
+
7
7
  module AssetHelpers
8
8
  def asset_path(source)
9
9
  "#{request.env['SCRIPT_NAME']}/assets/#{Assets.instance.find_asset(source).digest_path}" unless Assets.instance.find_asset(source).nil?
@@ -16,11 +16,11 @@ module MongodbLogger
16
16
  end
17
17
  end
18
18
  end
19
-
19
+
20
20
  class Assets < Sprockets::Environment
21
21
  class << self
22
22
  def instance(root = nil)
23
- assets_path = File.expand_path('../../../../app/assets', __FILE__)
23
+ assets_path = File.expand_path('../../../../../app/assets', __FILE__)
24
24
  @instance ||= new(assets_path)
25
25
  end
26
26
  end
@@ -36,5 +36,5 @@ module MongodbLogger
36
36
  end
37
37
  end
38
38
  end
39
-
39
+
40
40
  end
@@ -1,24 +1,10 @@
1
1
  # view helpers
2
2
  module Sinatra::ViewHelpers
3
-
4
- def pretty_hash(hash)
5
- begin
6
- Marshal::dump(hash)
7
- h(hash.to_yaml).gsub(" ", "&nbsp; ")
8
- rescue Exception => e # errors from Marshal or YAML
9
- # Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback
10
- h(object.inspect)
11
- end
12
- end
13
-
3
+
14
4
  def percent_of_userd_memory(collection_stats)
15
5
  ((collection_stats[:size] / collection_stats[:storageSize]) * 100).round
16
6
  end
17
-
18
- def string_from_log_message(message)
19
- message.is_a?(Array) ? message.join("\n") : message.to_s
20
- end
21
-
7
+
22
8
  def meta_informations(log)
23
9
  meta_data = Hash.new
24
10
  log.each do |key, val|
@@ -28,32 +14,30 @@ module Sinatra::ViewHelpers
28
14
  end
29
15
  meta_data
30
16
  end
31
-
32
- # TODO: improve this
17
+
18
+ STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb].freeze
33
19
  def number_to_human_size(number, precision = 2)
34
20
  number = begin
35
21
  Float(number)
36
22
  rescue ArgumentError, TypeError
37
23
  return number
38
24
  end
39
- case
40
- when number.to_i == 1 then
41
- "1 Byte"
42
- when number < 1024 then
43
- "%d Bytes" % number
44
- when number < 1048576 then
45
- "%.#{precision}f KB" % (number / 1024)
46
- when number < 1073741824 then
47
- "%.#{precision}f MB" % (number / 1048576)
48
- when number < 1099511627776 then
49
- "%.#{precision}f GB" % (number / 1073741824)
50
- else
51
- "%.#{precision}f TB" % (number / 1099511627776)
52
- end.sub(/([0-9]\.\d*?)0+ /, '\1 ' ).sub(/\. /,' ')
25
+ number = begin
26
+ Float(number)
27
+ rescue ArgumentError, TypeError
28
+ return number
29
+ end
30
+ base = 1024
31
+ max_exp = STORAGE_UNITS.size - 1
32
+ exponent = (Math.log(number) / Math.log(base)).to_i # Convert to base
33
+ exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
34
+ number /= base ** exponent
35
+ unit_key = STORAGE_UNITS[exponent]
36
+ ("%.#{precision}f #{unit_key.to_s.upcase}" % number).sub(/([0-9]\.\d*?)0+ /, '\1 ' ).sub(/\. /,' ')
53
37
  rescue
54
38
  nil
55
39
  end
56
-
40
+
57
41
  def text_field_tag(object, name, options = {})
58
42
  value = ""
59
43
  value = options.delete(:value) if options[:value]
@@ -64,7 +48,7 @@ module Sinatra::ViewHelpers
64
48
  end
65
49
  "<input type='text' name='#{object.form_name}[#{name.to_s}]' value='#{value}' #{attr.join(" ")} />"
66
50
  end
67
-
51
+
68
52
  def submit_tag(name, value, options = {})
69
53
  attr = []
70
54
  options.each do |key, val|
@@ -72,7 +56,7 @@ module Sinatra::ViewHelpers
72
56
  end
73
57
  "<input type='submit' name='#{name.to_s}' value='#{value}' #{attr.join(" ")} />"
74
58
  end
75
-
59
+
76
60
  def check_box_tag(object, name, options = {})
77
61
  value = nil
78
62
  value = options.delete(:value) if options[:value]
@@ -83,7 +67,7 @@ module Sinatra::ViewHelpers
83
67
  end
84
68
  "<input id='#{object.form_name}_#{name.to_s}' type='checkbox' name='#{object.form_name}[#{name.to_s}]' #{'checked="checked"' if value} value='1' #{attr.join(" ")} />"
85
69
  end
86
-
70
+
87
71
  def label_tag(object, name, label, options = {})
88
72
  attr = []
89
73
  options.each do |key, val|
@@ -91,7 +75,7 @@ module Sinatra::ViewHelpers
91
75
  end
92
76
  "<label for='#{object.form_name}_#{name.to_s}' #{attr.join(" ")}>#{label}</label>"
93
77
  end
94
-
78
+
95
79
  def select_tag(object, name, options_array, options = {})
96
80
  value = nil
97
81
  value = options.delete(:value) if options[:value]
@@ -113,5 +97,5 @@ module Sinatra::ViewHelpers
113
97
  select_tag << "</select>"
114
98
  select_tag.join("\n")
115
99
  end
116
-
100
+
117
101
  end