sbf-data_objects 0.10.17

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 (66) hide show
  1. checksums.yaml +7 -0
  2. data/ChangeLog.markdown +115 -0
  3. data/LICENSE +20 -0
  4. data/README.markdown +18 -0
  5. data/Rakefile +20 -0
  6. data/lib/data_objects/byte_array.rb +6 -0
  7. data/lib/data_objects/command.rb +79 -0
  8. data/lib/data_objects/connection.rb +95 -0
  9. data/lib/data_objects/error/connection_error.rb +4 -0
  10. data/lib/data_objects/error/data_error.rb +4 -0
  11. data/lib/data_objects/error/integrity_error.rb +4 -0
  12. data/lib/data_objects/error/sql_error.rb +17 -0
  13. data/lib/data_objects/error/syntax_error.rb +4 -0
  14. data/lib/data_objects/error/transaction_error.rb +4 -0
  15. data/lib/data_objects/error.rb +4 -0
  16. data/lib/data_objects/extension.rb +9 -0
  17. data/lib/data_objects/logger.rb +247 -0
  18. data/lib/data_objects/pooling.rb +243 -0
  19. data/lib/data_objects/quoting.rb +99 -0
  20. data/lib/data_objects/reader.rb +45 -0
  21. data/lib/data_objects/result.rb +21 -0
  22. data/lib/data_objects/spec/lib/pending_helpers.rb +13 -0
  23. data/lib/data_objects/spec/lib/ssl.rb +19 -0
  24. data/lib/data_objects/spec/setup.rb +5 -0
  25. data/lib/data_objects/spec/shared/command_spec.rb +201 -0
  26. data/lib/data_objects/spec/shared/connection_spec.rb +148 -0
  27. data/lib/data_objects/spec/shared/encoding_spec.rb +161 -0
  28. data/lib/data_objects/spec/shared/error/sql_error_spec.rb +23 -0
  29. data/lib/data_objects/spec/shared/quoting_spec.rb +0 -0
  30. data/lib/data_objects/spec/shared/reader_spec.rb +180 -0
  31. data/lib/data_objects/spec/shared/result_spec.rb +67 -0
  32. data/lib/data_objects/spec/shared/typecast/array_spec.rb +29 -0
  33. data/lib/data_objects/spec/shared/typecast/bigdecimal_spec.rb +112 -0
  34. data/lib/data_objects/spec/shared/typecast/boolean_spec.rb +133 -0
  35. data/lib/data_objects/spec/shared/typecast/byte_array_spec.rb +76 -0
  36. data/lib/data_objects/spec/shared/typecast/class_spec.rb +53 -0
  37. data/lib/data_objects/spec/shared/typecast/date_spec.rb +114 -0
  38. data/lib/data_objects/spec/shared/typecast/datetime_spec.rb +140 -0
  39. data/lib/data_objects/spec/shared/typecast/float_spec.rb +115 -0
  40. data/lib/data_objects/spec/shared/typecast/integer_spec.rb +92 -0
  41. data/lib/data_objects/spec/shared/typecast/ipaddr_spec.rb +0 -0
  42. data/lib/data_objects/spec/shared/typecast/nil_spec.rb +107 -0
  43. data/lib/data_objects/spec/shared/typecast/other_spec.rb +41 -0
  44. data/lib/data_objects/spec/shared/typecast/range_spec.rb +29 -0
  45. data/lib/data_objects/spec/shared/typecast/string_spec.rb +130 -0
  46. data/lib/data_objects/spec/shared/typecast/time_spec.rb +111 -0
  47. data/lib/data_objects/transaction.rb +111 -0
  48. data/lib/data_objects/uri.rb +109 -0
  49. data/lib/data_objects/utilities.rb +18 -0
  50. data/lib/data_objects/version.rb +3 -0
  51. data/lib/data_objects.rb +20 -0
  52. data/spec/command_spec.rb +24 -0
  53. data/spec/connection_spec.rb +31 -0
  54. data/spec/do_mock.rb +29 -0
  55. data/spec/do_mock2.rb +29 -0
  56. data/spec/pooling_spec.rb +153 -0
  57. data/spec/reader_spec.rb +19 -0
  58. data/spec/result_spec.rb +21 -0
  59. data/spec/spec_helper.rb +18 -0
  60. data/spec/transaction_spec.rb +37 -0
  61. data/spec/uri_spec.rb +23 -0
  62. data/tasks/release.rake +14 -0
  63. data/tasks/spec.rake +10 -0
  64. data/tasks/yard.rake +9 -0
  65. data/tasks/yardstick.rake +19 -0
  66. metadata +122 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 244576946e544f5a11ca625f783d1a5536e77bef7437041fdb11d6abe793094f
4
+ data.tar.gz: 8583773433efe291633740bd2499c404d81cb6f444705e9f45d947ee709697c6
5
+ SHA512:
6
+ metadata.gz: 7113134178dc9b9675801b387a91508864e2002a27a2d8c19de95edeebb82baa7b46a804a480a9738b2dd803724ec9f7d21224fcd028c718e7983ce9423ffb6a
7
+ data.tar.gz: 873820bd19ce5202a58c8ad2a0174de99ae1e04e23ce803b1c30a794a6e1c813fa601c823440c2094f0cebb07b2bb6dd5e01a94e46e2c236f2e53715f01ce4c8
@@ -0,0 +1,115 @@
1
+ ## 0.10.17 2016-01-24
2
+
3
+ * Enable CI with Ruby 2.3
4
+ * Improve utf8mb4 support in do\_mysql
5
+ * Fix memory leak in do\_mysql and do\_postgres
6
+ * Support for MySQL 5.7 in do\_mysql
7
+ * Fix bug with DateTime and wrong timezone offsets
8
+ * Fix compilation on JRuby 9000
9
+
10
+ ## 0.10.16 2015-05-17
11
+
12
+ * Fix compile issue with do\_postgres on stock OS X Ruby
13
+
14
+ ## 0.10.15 2015-02-15
15
+
16
+ * Ruby 2.2 support
17
+ * Double after free fix in do\_postgres
18
+ * utf8mb4 support on do\_mysql
19
+
20
+ * Windows support on 2.1.x and 2.2.x
21
+ ## 0.10.14 2014-02-13
22
+
23
+ * Don't do DNS lookup in transaction loading
24
+
25
+ ## 0.10.13 2013-05-27
26
+
27
+ * Create binaries for Ruby 2.0 on Windows
28
+ * Fix segfault in do\_postgres
29
+ * Fix compilation of do\_oracle for Ruby 2.0
30
+
31
+ ## 0.10.12 2013-01-21
32
+
33
+ * Fix JRuby driver loading for newer jdbc-\* gems
34
+ * Compatibility change for anscient MySQL versions
35
+
36
+ ## 0.10.11 2012-12-29
37
+
38
+ * Rename C symbols to prevent name collitions
39
+
40
+ ## 0.10.10 2012-10-11
41
+
42
+ * JRuby performance improvements
43
+ * Reconnect added on JRuby
44
+ * do\_sqlite3 C driver supports busy\_timeout
45
+
46
+ ## 0.10.9 2012-08-13
47
+
48
+ * Don't try to escape queries when no binding parameters are given
49
+
50
+ ## 0.10.8 2012-02-10
51
+
52
+ * Ruby 1.9.3 compatibility on Windows
53
+ * Don't display password in URI
54
+
55
+ ## 0.10.7 2011-10-13
56
+
57
+ * Ruby 1.9.3 compatibility
58
+
59
+ ## 0.10.6 2011-05-22
60
+
61
+ Bugfixes
62
+ * Fix an issue on some platforms when multiple DO drivers are loaded
63
+
64
+ ## 0.10.5 2011-05-03
65
+
66
+ Bugfixes
67
+ * Fix an issue with DateTime (do\_sqlite3)
68
+
69
+ ## 0.10.4 2011-04-28
70
+
71
+ New features
72
+ * Add save point to transactions (all)
73
+ * JRuby 1.9 mode support (encodings etc.)
74
+
75
+ Bugfixes
76
+ * Fix segfault when no tuples are returned from a non select statement (do\_postgres)
77
+ * Fix bug when using nested transactions in concurrent scenarios (all)
78
+ * Use column aliases instead of names (jruby)
79
+ * DST calculation fixes (all)
80
+ * Attempt to add better support for ancient MySQL versions (do\_mysql)
81
+ * Fix handling sub second precision for Time objects (do\_postgres)
82
+
83
+ Other
84
+ * Refactor to DRY up the adapters (all)
85
+ * Many style fixes
86
+ * Switch back to RSpec
87
+
88
+ ## 0.10.3 2011-01-30
89
+ * Reworked transactions
90
+ * Fix a DST bug that could cause datetimes in the wrong timezone
91
+
92
+ ## 0.10.2 2010-05-19
93
+ * Support for Encoding.default_internal
94
+ * Rework logging to adding a callback is possible
95
+
96
+ ## 0.10.1 2010-01-08
97
+
98
+ * Removal of Extlib dependency: Pooling and Utilities code moved to DataObjects.
99
+ * Switch to Jeweler for Gem building tasks (this change may be temporary).
100
+ * Switch to using Bacon for running specs: This should make specs friendlier to
101
+ new Ruby implementations that are not yet 100% MRI-compatible, and in turn,
102
+ pave the road for our own IronRuby and MacRuby support.
103
+ * Make DataObjects::Reader Enumerable.
104
+
105
+ ## 0.10.0 2009-09-15
106
+
107
+ * No Changes since 0.9.11
108
+
109
+ ## 0.9.11 2009-01-19
110
+ * Fixes
111
+ * Use Extlib `Object.full_const_get` instead of custom code
112
+ * Remove Field as it was unused
113
+
114
+ ## 0.9.9 2008-11-27
115
+ * No Changes since 0.9.8
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 - 2011 Yehuda Katz, Dirkjan Bussink
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,18 @@
1
+ # data_objects
2
+
3
+ * <http://dataobjects.info>
4
+
5
+ ## Description
6
+
7
+ A unified Ruby API for popular databases.
8
+
9
+ ## License
10
+
11
+ Licensed under the MIT license. Please see the {file:LICENSE} for more information.
12
+
13
+ ## Contact
14
+
15
+ **IRC**: **Join us on IRC in #datamapper on irc.freenode.net!**<br/>
16
+ **Git**: <http://github.com/firesring/datamapper-do><br/>
17
+ **Author**: Dirkjan Bussink<br/>
18
+ **License**: MIT License
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ require 'pathname'
2
+ require 'rubygems'
3
+ require 'bundler'
4
+ require 'rubygems/package_task'
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ require 'rake'
8
+ require 'rake/clean'
9
+
10
+ ROOT = Pathname(__FILE__).dirname.expand_path
11
+
12
+ # rubocop:disable Style/StringConcatenation
13
+ require ROOT + 'lib/data_objects/version'
14
+ # rubocop:enable Style/StringConcatenation
15
+
16
+ SUDO = 'sudo' unless ENV['SUDOLESS']
17
+
18
+ CLEAN.include(%w(pkg/ **/*.rbc))
19
+
20
+ FileList['tasks/**/*.rake'].each { |task| import task }
@@ -0,0 +1,6 @@
1
+ # This class has exists to represent binary data. This is mainly
2
+ # used by DataObjects. Binary data sometimes needs to be quoted differently
3
+ # than regular string data (even if the string is just plain ASCII).
4
+ module Extlib
5
+ class ByteArray < ::String; end
6
+ end
@@ -0,0 +1,79 @@
1
+ module DataObjects
2
+ # Abstract base class for adapter-specific Command subclasses
3
+ class Command
4
+ # The Connection on which the command will be run
5
+ attr_reader :connection
6
+
7
+ # Create a new Command object on the specified connection
8
+ def initialize(connection, text)
9
+ raise ArgumentError, '+connection+ must be a DataObjects::Connection' unless connection.is_a?(DataObjects::Connection)
10
+
11
+ @connection = connection
12
+ @text = text
13
+ end
14
+
15
+ # Execute this command and return no dataset
16
+ def execute_non_query(*_args)
17
+ raise NotImplementedError
18
+ end
19
+
20
+ # Execute this command and return a DataObjects::Reader for a dataset
21
+ def execute_reader(*_args)
22
+ raise NotImplementedError
23
+ end
24
+
25
+ # Assign an array of types for the columns to be returned by this command
26
+ def set_types(_column_types)
27
+ raise NotImplementedError
28
+ end
29
+
30
+ # Display the command text
31
+ def to_s
32
+ @text
33
+ end
34
+
35
+ # Escape a string of SQL with a set of arguments.
36
+ # The first argument is assumed to be the SQL to escape,
37
+ # the remaining arguments (if any) are assumed to be
38
+ # values to escape and interpolate.
39
+ #
40
+ # ==== Examples
41
+ # escape_sql("SELECT * FROM zoos")
42
+ # # => "SELECT * FROM zoos"
43
+ #
44
+ # escape_sql("SELECT * FROM zoos WHERE name = ?", "Dallas")
45
+ # # => "SELECT * FROM zoos WHERE name = `Dallas`"
46
+ #
47
+ # escape_sql("SELECT * FROM zoos WHERE name = ? AND acreage > ?", "Dallas", 40)
48
+ # # => "SELECT * FROM zoos WHERE name = `Dallas` AND acreage > 40"
49
+ #
50
+ # ==== Warning
51
+ # This method is meant mostly for adapters that don't support
52
+ # bind-parameters.
53
+ private def escape_sql(args)
54
+ return @text if args.empty?
55
+
56
+ sql = @text.dup
57
+ vars = args.dup
58
+
59
+ replacements = 0
60
+ mismatch = false
61
+
62
+ sql.gsub!(/'[^']*'|"[^"]*"|`[^`]*`|\?/) do |x|
63
+ next x unless x == '?'
64
+
65
+ replacements += 1
66
+ if vars.empty?
67
+ mismatch = true
68
+ else
69
+ var = vars.shift
70
+ connection.quote_value(var)
71
+ end
72
+ end
73
+
74
+ raise ArgumentError, "Binding mismatch: #{args.size} for #{replacements}" if !vars.empty? || mismatch
75
+
76
+ sql
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,95 @@
1
+
2
+ module DataObjects
3
+ # An abstract connection to a DataObjects resource. The physical connection may be broken and re-established from time to time.
4
+ class Connection
5
+ include Logging
6
+
7
+ # Make a connection to the database using the DataObjects::URI given.
8
+ # Note that the physical connection may be delayed until the first command is issued, so success here doesn't necessarily mean you can connect.
9
+ def self.new(uri_s)
10
+ uri = DataObjects::URI.parse(uri_s)
11
+
12
+ driver = uri.scheme
13
+ conn_uri = uri
14
+
15
+ # Exceptions to how a driver class is determined for a given URI
16
+ driver_class = driver.capitalize
17
+
18
+ clazz = DataObjects.const_get(driver_class)::Connection
19
+ unless clazz.method_defined? :close
20
+ clazz.class_eval do
21
+ include Pooling
22
+ alias_method :close, :release
23
+ end
24
+ end
25
+ clazz.new(conn_uri)
26
+ end
27
+
28
+ # Ensure that all Connection subclasses handle pooling and logging uniformly.
29
+ # See also DataObjects::Pooling and DataObjects::Logger
30
+ def self.inherited(target)
31
+ target.class_eval do
32
+ # Allocate a Connection object from the pool, creating one if necessary. This method is active in Connection subclasses only.
33
+ def self.new(*args)
34
+ instance = allocate
35
+ instance.send(:initialize, *args)
36
+ instance
37
+ end
38
+
39
+ include Quoting
40
+ end
41
+
42
+ return unless (driver_module_name = target.name.split('::')[-2])
43
+
44
+ driver_module = DataObjects.const_get(driver_module_name)
45
+ driver_module.class_eval <<-EOS, __FILE__, __LINE__ + 1
46
+ def self.logger
47
+ @logger
48
+ end
49
+
50
+ def self.logger=(logger)
51
+ @logger = logger
52
+ end
53
+ EOS
54
+
55
+ driver_module.logger = DataObjects::Logger.new(nil, :off)
56
+ end
57
+
58
+ #####################################################
59
+ # Standard API Definition
60
+ #####################################################
61
+
62
+ # Show the URI for this connection, without
63
+ # the password the connection was setup with
64
+ def to_s
65
+ @uri.to_s
66
+ end
67
+
68
+ def initialize(_uri) # :nodoc:
69
+ raise NotImplementedError
70
+ end
71
+
72
+ def dispose # :nodoc:
73
+ raise NotImplementedError
74
+ end
75
+
76
+ # Create a Command object of the right subclass using the given text
77
+ def create_command(text)
78
+ self.class.concrete_command.new(self, text)
79
+ end
80
+
81
+ def extension
82
+ driver_namespace.const_get('Extension').new(self)
83
+ end
84
+
85
+ private def driver_namespace
86
+ constant_name = self.class.name&.split('::')
87
+ DataObjects.const_get(constant_name[-2])
88
+ end
89
+
90
+ def self.concrete_command
91
+ constant_name = name&.split('::')
92
+ @concrete_command ||= DataObjects.const_get(constant_name[-2]).const_get('Command')
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,4 @@
1
+ module DataObjects
2
+ class ConnectionError < SQLError
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module DataObjects
2
+ class DataError < SQLError
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module DataObjects
2
+ class IntegrityError < SQLError
3
+ end
4
+ end
@@ -0,0 +1,17 @@
1
+ module DataObjects
2
+ class SQLError < Error
3
+ attr_reader :message, :code, :sqlstate, :query, :uri
4
+
5
+ def initialize(message, code = nil, sqlstate = nil, query = nil, uri = nil)
6
+ @message = message
7
+ @code = code
8
+ @sqlstate = sqlstate
9
+ @query = query
10
+ @uri = uri
11
+ end
12
+
13
+ def to_s
14
+ "#{message} (code: #{code}, sql state: #{sqlstate}, query: #{query}, uri: #{uri})"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,4 @@
1
+ module DataObjects
2
+ class SyntaxError < SQLError
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module DataObjects
2
+ class TransactionError < SQLError
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module DataObjects
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -0,0 +1,9 @@
1
+ module DataObjects
2
+ class Extension
3
+ attr_reader :connection
4
+
5
+ def initialize(connection)
6
+ @connection = connection
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,247 @@
1
+ require 'time' # httpdate
2
+
3
+ module DataObjects
4
+ module Logging
5
+ def log(message)
6
+ logger = driver_namespace.logger
7
+ return unless logger.level <= DataObjects::Logger::LEVELS[:debug]
8
+
9
+ message = format('(%.6f) %s', message.duration / 1_000_000.0, message.query)
10
+ logger.debug message
11
+ end
12
+ end
13
+
14
+ class << self
15
+ # The global logger for DataObjects
16
+ attr_accessor :logger
17
+ end
18
+
19
+ # ==== Public DataObjects Logger API
20
+ #
21
+ # Logger taken from Merb :)
22
+ #
23
+ # To replace an existing logger with a new one:
24
+ # DataObjects::Logger.set_log(log{String, IO},level{Symbol, String})
25
+ #
26
+ # Available logging levels are
27
+ # DataObjects::Logger::{ Fatal, Error, Warn, Info, Debug }
28
+ #
29
+ # Logging via:
30
+ # DataObjects.logger.fatal(message<String>)
31
+ # DataObjects.logger.error(message<String>)
32
+ # DataObjects.logger.warn(message<String>)
33
+ # DataObjects.logger.info(message<String>)
34
+ # DataObjects.logger.debug(message<String>)
35
+ #
36
+ # Flush the buffer to
37
+ # DataObjects.logger.flush
38
+ #
39
+ # Remove the current log object
40
+ # DataObjects.logger.close
41
+ #
42
+ # ==== Private DataObjects Logger API
43
+ #
44
+ # To initialize the logger you create a new object, proxies to set_log.
45
+ # DataObjects::Logger.new(log{String, IO},level{Symbol, String})
46
+ #
47
+ # Logger will not create the file until something is actually logged
48
+ # This avoids file creation on DataObjects init when it creates the
49
+ # default logger.
50
+ class Logger
51
+ # Use asynchronous I/O?
52
+ attr_accessor :aio
53
+ # delimiter to use between message sections
54
+ attr_accessor :delimiter
55
+ # a symbol representing the log level from {:off, :fatal, :error, :warn, :info, :debug}
56
+ attr_reader :level
57
+ # Direct access to the buffer
58
+ attr_reader :buffer
59
+ # The name of the log file
60
+ attr_reader :log
61
+
62
+ Message = Struct.new(:query, :start, :duration)
63
+
64
+ #
65
+ # Ruby (standard) logger levels:
66
+ # off: absolutely nothing
67
+ # fatal: an unhandleable error that results in a program crash
68
+ # error: a handleable error condition
69
+ # warn: a warning
70
+ # info: generic (useful) information about system operation
71
+ # debug: low-level information for developers
72
+ #
73
+ # DataObjects::Logger::LEVELS[:off, :fatal, :error, :warn, :info, :debug]
74
+ LEVELS =
75
+ {
76
+ off: 99_999,
77
+ fatal: 7,
78
+ error: 6,
79
+ warn: 4,
80
+ info: 3,
81
+ debug: 0
82
+ }.freeze
83
+
84
+ # Set the log level (use the level symbols as documented)
85
+ def level=(new_level)
86
+ @level = LEVELS[new_level.to_sym]
87
+ reset_methods(:close)
88
+ end
89
+
90
+ # The idea here is that instead of performing an 'if' conditional check on
91
+ # each logging we do it once when the log object is setup
92
+ private def set_write_method
93
+ @log.instance_eval do
94
+ # Determine if asynchronous IO can be used
95
+ def aio?
96
+ @aio = !RUBY_PLATFORM.match(/java|mswin/) &&
97
+ @log != $stdout &&
98
+ @log.respond_to?(:write_nonblock)
99
+ end
100
+
101
+ # Define the write method based on if aio an be used
102
+ undef write_method if defined? write_method
103
+ if aio?
104
+ alias :write_method :write_nonblock
105
+ else
106
+ alias :write_method :write
107
+ end
108
+ end
109
+ end
110
+
111
+ private def initialize_log(log)
112
+ close if @log # be sure that we don't leave open files laying around.
113
+ @log = log || 'log/dm.log'
114
+ end
115
+
116
+ private def reset_methods(o_or_c)
117
+ if o_or_c == :open
118
+ alias internal_push push_opened
119
+ elsif o_or_c == :close
120
+ alias internal_push push_closed
121
+ end
122
+ end
123
+
124
+ private def push_opened(string)
125
+ message = Time.now.httpdate
126
+ message << delimiter
127
+ message << string
128
+ message << "\n" unless message[-1] == "\n"
129
+ @buffer << message
130
+ flush # Force a flush for now until we figure out where we want to use the buffering.
131
+ end
132
+
133
+ private def push_closed(string)
134
+ unless @log.respond_to?(:write)
135
+ log = Pathname(@log)
136
+ log.dirname.mkpath
137
+ @log = log.open('a')
138
+ @log.sync = true
139
+ end
140
+ set_write_method
141
+ reset_methods(:open)
142
+ push(string)
143
+ end
144
+
145
+ alias internal_push push_closed
146
+
147
+ private def prep_msg(message, level)
148
+ level << delimiter << message
149
+ end
150
+
151
+ # To initialize the logger you create a new object, proxies to set_log.
152
+ # DataObjects::Logger.new(log{String, IO},level{Symbol, String})
153
+ #
154
+ # @param * [Mixed]
155
+ # log<IO,String> either an IO object or a name of a logfile.
156
+ # log_level<String> the message string to be logged
157
+ # delimiter<String> delimiter to use between message sections
158
+ # log_creation<Boolean> log that the file is being created
159
+ public def initialize(*args)
160
+ set_log(*args)
161
+ end
162
+
163
+ # To replace an existing logger with a new one:
164
+ # DataObjects::Logger.set_log(log{String, IO},level{Symbol, String})
165
+ #
166
+ #
167
+ # @param log<IO,String> either an IO object or a name of a logfile.
168
+ # @param log_level<Symbol> a symbol representing the log level from
169
+ # {:off, :fatal, :error, :warn, :info, :debug}
170
+ # @param delimiter<String> delimiter to use between message sections
171
+ # @param log_creation<Boolean> log that the file is being created
172
+ public def set_log(log, log_level = :off, delimiter = ' ~ ', log_creation = false)
173
+ delimiter ||= ' ~ '
174
+
175
+ self.level = if log_level && LEVELS[log_level.to_sym]
176
+ log_level.to_sym
177
+ else
178
+ :debug
179
+ end
180
+
181
+ @buffer = []
182
+ @delimiter = delimiter
183
+
184
+ initialize_log(log)
185
+
186
+ DataObjects.logger = self
187
+
188
+ info('Logfile created') if log_creation
189
+ end
190
+
191
+ # Flush the entire buffer to the log object.
192
+ # DataObjects.logger.flush
193
+ #
194
+ public def flush
195
+ return unless @buffer.size.positive?
196
+
197
+ @log.write_method(@buffer.slice!(0..-1).join)
198
+ end
199
+
200
+ # Close and remove the current log object.
201
+ # DataObjects.logger.close
202
+ #
203
+ public def close
204
+ flush
205
+ @log.close if @log.respond_to?(:close)
206
+ @log = nil
207
+ end
208
+
209
+ # Appends a string and log level to logger's buffer.
210
+
211
+ #
212
+ # Note that the string is discarded if the string's log level less than the
213
+ # logger's log level.
214
+ #
215
+ # Note that if the logger is aio capable then the logger will use
216
+ # non-blocking asynchronous writes.
217
+ #
218
+ # Is this old or does the method receive mixed params? level<Fixnum> the logging level as an integer
219
+ # @param string<String> the message string to be logged
220
+ public def push(string)
221
+ internal_push(string)
222
+ end
223
+ alias << push
224
+
225
+ # Generate the following logging methods for DataObjects.logger as described
226
+ # in the API:
227
+ # :fatal, :error, :warn, :info, :debug
228
+ # :off only gets an off? method
229
+ LEVELS.each_pair do |name, number|
230
+ unless name.to_sym == :off
231
+ class_eval <<-EOS, __FILE__, __LINE__ + 1
232
+ # DOC
233
+ def #{name}(message)
234
+ self.<<( prep_msg(message, "#{name}") ) if #{name}?
235
+ end
236
+ EOS
237
+ end
238
+
239
+ class_eval <<-EOS, __FILE__, __LINE__ + 1
240
+ # DOC
241
+ def #{name}?
242
+ #{number} >= level
243
+ end
244
+ EOS
245
+ end
246
+ end
247
+ end