logfmt 0.0.10 → 0.1.0.beta.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5d357083dac3dea75a9cd937ac2ba24b8cfff5c11f0656749a084cc16fb6cd4a
4
- data.tar.gz: 97aad097b305ded6c753de8c233cd2317c12dcb9834fec52047ea799d842f8ff
3
+ metadata.gz: d8f2e9e3f3d271e1cad7120cb1fedc6a839050004a51ccd72a9e0ef019b242e6
4
+ data.tar.gz: 01035abc746fce52ac36c672bec2640a3c4d436f300b244d13b4e936b655030b
5
5
  SHA512:
6
- metadata.gz: 8a140be6a60d67ca2f7eb8373689e7f246e7ffadae3fba22da5820992b38df1410a5faaabc0e37ab6e318434db0ba4973b4844e74e86f3e60ef9df19aff574c8
7
- data.tar.gz: 9df321f5d270af2e288a6de8e2391d9a05159e680ae3bfa669751a92bd0e155e30d6fc86c55f246896c20d5d274e45b93878a5f365ebb0c652c94b52d53379a2
6
+ metadata.gz: bb92737ee195acf40cfae4ae48b73895165568690984448e75d9bd4c3a3131f570721ebf0182778c959dc312182f6ebc9bcc0a6a26a877f09e24faa16f2377ff
7
+ data.tar.gz: 3df2ec4510aad12b18cbe6d1f2da2ffc7b8e3114bb3c66a4b74ae9d04965a5ba766a90db1b7510104295e0c133d36b2bff7190ac80bbbdfac85d770a4b1bb98d
data/CHANGELOG.md CHANGED
@@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
5
5
 
6
6
  ## \[Unreleased\]
7
7
 
8
+ ## [0.1.0.beta.1] 2022-10-21
9
+ ### Added
10
+ - Add `Logfmt::Logger` and `Logfmt::TaggedLogger`.
11
+ The later is distributed as its own gem, `logfmt-tagged_logger`, but lives in this repo.
12
+
8
13
  ## [0.0.10] 2022-04-30
9
14
  ### Changed
10
15
  - Autoload the `Logfmt::Parser` when it's used, in preparation for the coming `Logfmt::Logger` and friends.
data/Gemfile CHANGED
@@ -1,3 +1,4 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gemspec
3
+ gemspec name: "logfmt"
4
+ gemspec name: "logfmt-tagged_logger"
data/README.md CHANGED
@@ -1,6 +1,58 @@
1
1
  # Logfmt
2
2
 
3
- Parse log lines in the logfmt style:
3
+ Write and parse structured log lines in the [logfmt style][logfmt-blog].
4
+
5
+ ## Installation
6
+
7
+ Add this line to your Gemfile:
8
+
9
+ ```ruby
10
+ gem "logfmt"
11
+ ```
12
+
13
+ And then install:
14
+
15
+ ```bash
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself:
20
+
21
+ ```bash
22
+ $ gem install logfmt
23
+ ```
24
+
25
+ ### Versioning
26
+
27
+ This project adheres to [Semantic Versioning][semver].
28
+
29
+ ## Usage
30
+
31
+ `Logfmt` is composed to two parts: writing structured log lines in the `logfmt` style, and parsing `logfmt`-style log lines.
32
+
33
+ While writing and parsing `logfmt` are related, we've found that it's common to only need to do one or there other in a single application.
34
+ To support that usage, `Logfmt` leverages Ruby's `autoload` to lazily load the `Logfmt::Parser` or `Logfmt::Logger` (and associated code) into memory.
35
+ In the general case that looks something like:
36
+
37
+ ```ruby
38
+ require "logfmt"
39
+
40
+ Logfmt # This constant was already loaded, but neither Logfmt::Parser
41
+ # nor Logfmt::Logger constants are loaded. Yet.
42
+
43
+ Logfmt.parse("…")
44
+ # OR
45
+ Logfmt::Parser.parse("…")
46
+
47
+ # Either of the above will load the Logfmt::Parser constant.
48
+ # Similarly you can autoload the Logfmt::Logger via
49
+
50
+ Logfmt::Logger.new
51
+ ```
52
+
53
+ If you want to eagerly load the logger or parser, you can do that by requiring them directly
54
+
55
+ ### Parsing log lines
4
56
 
5
57
  ```ruby
6
58
  require "logfmt/parser"
@@ -8,3 +60,95 @@ require "logfmt/parser"
8
60
  Logfmt::Parser.parse('foo=bar a=14 baz="hello kitty" cool%story=bro f %^asdf')
9
61
  #=> {"foo"=>"bar", "a"=>14, "baz"=>"hello kitty", "cool%story"=>"bro", "f"=>true, "%^asdf"=>true}
10
62
  ```
63
+
64
+ ### Writing log lines
65
+
66
+ The `Logfmt::Logger` is built on the stdlib `::Logger` and adheres to its API.
67
+ The primary difference is that `Logfmt::Logger` defaults to a `logfmt`-style formatter.
68
+ Specifically, a `Logfmt::Logger::KeyValueFormatter`, which results in log lines something like this:
69
+
70
+ ```ruby
71
+ require "logfmt/logger"
72
+
73
+ logger = Logfmt::Logger.new($stdout)
74
+
75
+ logger.info(foo: "bar", a: 14, "baz" => "hello kitty", "cool%story" => "bro", f: true, "%^asdf" => true)
76
+ #=> time=2022-04-20T23:30:54.647403Z severity=INFO foo=bar a=14 baz="hello kitty" cool%story=bro f %^asdf
77
+
78
+ logger.debug("MADE IT HERE!")
79
+ #=> time=2022-04-20T23:33:44.912595Z severity=DEBUG msg="MADE IT HERE!"
80
+ ```
81
+
82
+ #### Tagged log lines
83
+
84
+ The `logfmt-tagged_logger` gem adds support for Rails-style [tagged logging][tagged-logger].
85
+ This gem adds a `Logfmt::TaggedLogger` which is built on `ActiveSupport::TaggedLogger`, but emits the tags in logfmt-style, as key/value pairs.
86
+ For example
87
+
88
+ ```ruby
89
+ logger = Logfmt::TaggedLogger.new($stdout)
90
+
91
+ logger.tagged(source: "api") do
92
+ logger.info(foo: "bar")
93
+ end
94
+
95
+ #=> time=2022-04-20T23:33:44.912595Z severity=info source=api foo=bar"
96
+ ```
97
+
98
+ You can also pass "bare" tags and they'll be collected and emitted under the `tags` key.
99
+
100
+ ```ruby
101
+ logger = Logfmt::TaggedLogger.new($stdout)
102
+
103
+ logger.tagged("API", "1.2.3.4") do
104
+ logger.info(foo: "bar")
105
+ end
106
+
107
+ #=> time=2022-04-20T23:33:44.912595Z severity=info tags="[API] [1.2.3.4]" foo=bar"
108
+ ```
109
+
110
+ It's likely more helpful and useful to use meaningful key/values for your tags, rather than bare tags.
111
+
112
+ #### Expected key/value transformations
113
+
114
+ When writing a log line with the `Logfmt::Logger::KeyValueFormatter` the keys and/or values will be transformed thusly:
115
+
116
+ * "Bare messages" (those with no key given when invoking the logger) will be wrapped in the `msg` key.
117
+
118
+ ```ruby
119
+ logger.info("here")
120
+ #=> time=2022-04-20T23:33:49.912997Z severity=INFO msg=here
121
+ ```
122
+
123
+ * Values, including bare messages, containing white space or control characters (spaces, tabs, newlines, emoji, etc…) will be wrapped in double quotes (`""`) and fully escaped.
124
+
125
+ ```ruby
126
+ logger.info("👻 Boo!")
127
+ #=> time=2022-04-20T23:33:35.912595Z severity=INFO msg="\u{1F47B} Boo!"
128
+
129
+ logger.info(number: 42, with_quotes: %{These "are" 'quotes', OK?})
130
+ #=> time=2022-04-20T23:33:36.412183Z severity=INFO number=42 with_quotes="These \"are\" 'quotes', OK?"
131
+ ```
132
+
133
+ * Floating point values are truncated to three digits.
134
+
135
+ * Time values are formatted as ISO8601 strings, with six digits sub-second precision.
136
+
137
+ * A value that is an Array is wrapped in square brackets, and then the above rules applied to each Array value.
138
+ This works well for arrays of simple values - like numbers, symbols, or simple strings.
139
+ But complex data structures will result in human mind-breaking escape sequences.
140
+ So don't do that.
141
+ Keep values simple.
142
+
143
+ ```ruby
144
+ logger.info(an_array: [1, "two", :three])
145
+ #=> time=2022-04-20T23:33:36.412183Z severity=INFO an_array="[1, two, three]"
146
+ ```
147
+
148
+ **NOTE**: it is **not** expected that log lines generated by `Logfmt` can be round-tripped by parsing the log line with `Logfmt`.
149
+ Specifically, this applies to Unicode and some control characters, as well as bare messages which will be wrapped in the `msg` key when writing.
150
+ Additionally, symbol keys will be parsed back into string keys.
151
+
152
+ [logfmt-blog]: https://brandur.org/logfmt "Structured log lines with key/value pairs"
153
+ [semver]: https://semver.org/spec/v2.0.0.html "Semantic Versioning 2.0.0"
154
+ [tagged-logger]: https://guides.rubyonrails.org/debugging_rails_applications.html#tagged-logging "Tagged Logging"
data/Rakefile CHANGED
@@ -1,8 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/gem_tasks"
3
+ require "bundler/gem_helper"
4
4
  require "rspec/core/rake_task"
5
5
 
6
+ desc "Run all specs"
6
7
  RSpec::Core::RakeTask.new(:spec)
7
8
 
9
+ namespace "logfmt" do
10
+ Bundler::GemHelper.install_tasks name: "logfmt"
11
+ end
12
+
13
+ # Inspired by how dotenv/dotenv-rails handles mulitple Gems in a single repo
14
+ class LogFmtTaggedLoggerGemHelper < Bundler::GemHelper
15
+ def guard_already_tagged
16
+ # noop
17
+ end
18
+
19
+ def tag_version
20
+ # noop
21
+ end
22
+ end
23
+
24
+ namespace "logfmt-tagged_logger" do
25
+ LogFmtTaggedLoggerGemHelper.install_tasks name: "logfmt-tagged_logger"
26
+ end
27
+
28
+ desc "Build logfmt and logfmt-tagged_logger into the pkg directory"
29
+ task build: ["logfmt:build", "logfmt-tagged_logger:build"]
30
+
31
+ desc "Build and install logfmt and logfmt-tagged_logger into system gems"
32
+ task install: ["logfmt:install", "logfmt-tagged_logger:install"]
33
+
34
+ desc "Create tag, build, and push logfmt and logfmt-tagged_logger to rubygems.org"
35
+ task release: ["logfmt:release", "logfmt-tagged_logger:release"]
36
+
8
37
  task default: :spec
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../logfmt"
4
+ require "logger"
5
+ require "time"
6
+
7
+ module Logfmt
8
+ class Logger < ::Logger
9
+ def initialize(*args, **kwargs)
10
+ super
11
+ @formatter ||= KeyValueFormatter.new
12
+ end
13
+
14
+ class KeyValueFormatter < ::Logger::Formatter
15
+ def call(severity, timestamp, progname, msg)
16
+ %(time=#{format_datetime(timestamp)} severity=#{severity.ljust(5)}#{format_progname(progname)} #{format_message(msg)}\n)
17
+ end
18
+
19
+ private
20
+
21
+ def format_datetime(time)
22
+ time.utc.iso8601(6)
23
+ end
24
+
25
+ def format_message(msg)
26
+ return unless msg
27
+
28
+ if msg.respond_to?(:to_hash)
29
+ pairs = msg.to_hash.map { |k, v| format_pair(k, v) }
30
+ pairs.compact.join(" ")
31
+ else
32
+ format_pair("msg", msg)
33
+ end
34
+ end
35
+
36
+ def format_pair(key, value)
37
+ return nil if value.nil?
38
+
39
+ # Return a bare key when the value is a `TrueClass`
40
+ return key if value == true
41
+
42
+ "#{key}=#{format_value(value)}"
43
+ end
44
+
45
+ def format_progname(progname)
46
+ return nil unless progname
47
+
48
+ # Format this pair like any other to ensure quoting, escaping, etc…,
49
+ # But we also need a leading space so we can interpolate the resulting
50
+ # key/value pair into our log line.
51
+ " #{format_pair(" progname", progname)}"
52
+ end
53
+
54
+ def format_value(value)
55
+ if value.is_a?(Float)
56
+ format("%.3f", value)
57
+ elsif value.is_a?(Time)
58
+ format_datetime(value)
59
+ elsif value.respond_to?(:to_ary)
60
+ format_value(
61
+ "[#{Array(value).map { |v| format_value(v) }.join(", ")}]"
62
+ )
63
+ else
64
+ # Interpolating due to a weird/subtle behaviour possible in #to_s.
65
+ # Namely, it's possible it doesn't actually return a String:
66
+ # https://github.com/ruby/spec/blob/3affe1e54fcd11918a242ad5d4a7ba895ee30c4c/language/string_spec.rb#L130-L141
67
+ value = "#{value}" # rubocop:disable Style/RedundantInterpolation
68
+ value = value.dump if value.match?(/[[:space:]]|[[:cntrl:]]/) # wrap in quotes and escape control characters
69
+ value
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Logfmt
4
- VERSION = "0.0.10"
4
+ VERSION = "0.1.0.beta.1"
5
5
  end
data/lib/logfmt.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require "logfmt/version"
4
4
 
5
5
  module Logfmt
6
+ autoload(:Logger, "logfmt/logger")
6
7
  autoload(:Parser, "logfmt/parser")
7
8
 
8
9
  def self.parse(line)
data/logfmt.gemspec ADDED
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/logfmt/version"
4
+ require "English"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "logfmt"
8
+ spec.version = Logfmt::VERSION
9
+ spec.authors = ["Timothée Peignier"]
10
+ spec.email = ["timothee.peignier@tryphon.org"]
11
+
12
+ spec.summary = "Write and parse logfmt messages."
13
+ spec.description = "Write and parse log lines in the logfmt style."
14
+ spec.homepage = "https://github.com/cyberdelia/logfmt-ruby"
15
+ spec.license = "MIT"
16
+ spec.required_ruby_version = ">= 2.5.0"
17
+
18
+ spec.metadata = {
19
+ "bug_tracker_uri" => "#{spec.homepage}/issues",
20
+ "changelog_uri" => "#{spec.homepage}/blog/master/CHANGELOG.md",
21
+ "documentation_uri" => spec.homepage,
22
+ "source_code_uri" => spec.homepage
23
+ }
24
+
25
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
+ `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
27
+ .reject { |f|
28
+ (f == __FILE__) ||
29
+ f.match?(%r{\A(?:(?:bin|spec|features)/|\.(?:git|github))}) ||
30
+ f.match?(/tagged_logger/)
31
+ }
32
+ end
33
+ spec.bindir = "bin"
34
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
35
+ spec.require_paths = ["lib"]
36
+
37
+ spec.add_development_dependency "pry-byebug", "~> 3.9"
38
+ spec.add_development_dependency "rake", "~> 13.0"
39
+ spec.add_development_dependency "rspec", "~> 3.0"
40
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logfmt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.10
4
+ version: 0.1.0.beta.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Timothée Peignier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-30 00:00:00.000000000 Z
11
+ date: 2022-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry-byebug
@@ -52,7 +52,7 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
- description: Parse log lines in the logfmt style.
55
+ description: Write and parse log lines in the logfmt style.
56
56
  email:
57
57
  - timothee.peignier@tryphon.org
58
58
  executables: []
@@ -65,13 +65,11 @@ files:
65
65
  - README.md
66
66
  - Rakefile
67
67
  - bench.rb
68
- - bin/bundle
69
- - bin/console
70
- - bin/rake
71
- - bin/rspec
72
68
  - lib/logfmt.rb
69
+ - lib/logfmt/logger.rb
73
70
  - lib/logfmt/parser.rb
74
71
  - lib/logfmt/version.rb
72
+ - logfmt.gemspec
75
73
  homepage: https://github.com/cyberdelia/logfmt-ruby
76
74
  licenses:
77
75
  - MIT
@@ -88,15 +86,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
88
86
  requirements:
89
87
  - - ">="
90
88
  - !ruby/object:Gem::Version
91
- version: 2.4.0
89
+ version: 2.5.0
92
90
  required_rubygems_version: !ruby/object:Gem::Requirement
93
91
  requirements:
94
- - - ">="
92
+ - - ">"
95
93
  - !ruby/object:Gem::Version
96
- version: '0'
94
+ version: 1.3.1
97
95
  requirements: []
98
96
  rubygems_version: 3.3.7
99
97
  signing_key:
100
98
  specification_version: 4
101
- summary: Parse logfmt messages.
99
+ summary: Write and parse logfmt messages.
102
100
  test_files: []
data/bin/bundle DELETED
@@ -1,114 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- #
5
- # This file was generated by Bundler.
6
- #
7
- # The application 'bundle' is installed as part of a gem, and
8
- # this file is here to facilitate running it.
9
- #
10
-
11
- require "rubygems"
12
-
13
- m = Module.new do
14
- module_function
15
-
16
- def invoked_as_script?
17
- File.expand_path($0) == File.expand_path(__FILE__)
18
- end
19
-
20
- def env_var_version
21
- ENV["BUNDLER_VERSION"]
22
- end
23
-
24
- def cli_arg_version
25
- return unless invoked_as_script? # don't want to hijack other binstubs
26
- return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
27
- bundler_version = nil
28
- update_index = nil
29
- ARGV.each_with_index do |a, i|
30
- if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
31
- bundler_version = a
32
- end
33
- next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
34
- bundler_version = $1
35
- update_index = i
36
- end
37
- bundler_version
38
- end
39
-
40
- def gemfile
41
- gemfile = ENV["BUNDLE_GEMFILE"]
42
- return gemfile if gemfile && !gemfile.empty?
43
-
44
- File.expand_path("../../Gemfile", __FILE__)
45
- end
46
-
47
- def lockfile
48
- lockfile =
49
- case File.basename(gemfile)
50
- when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
51
- else "#{gemfile}.lock"
52
- end
53
- File.expand_path(lockfile)
54
- end
55
-
56
- def lockfile_version
57
- return unless File.file?(lockfile)
58
- lockfile_contents = File.read(lockfile)
59
- return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
60
- Regexp.last_match(1)
61
- end
62
-
63
- def bundler_requirement
64
- @bundler_requirement ||=
65
- env_var_version || cli_arg_version ||
66
- bundler_requirement_for(lockfile_version)
67
- end
68
-
69
- def bundler_requirement_for(version)
70
- return "#{Gem::Requirement.default}.a" unless version
71
-
72
- bundler_gem_version = Gem::Version.new(version)
73
-
74
- requirement = bundler_gem_version.approximate_recommendation
75
-
76
- return requirement unless Gem.rubygems_version < Gem::Version.new("2.7.0")
77
-
78
- requirement += ".a" if bundler_gem_version.prerelease?
79
-
80
- requirement
81
- end
82
-
83
- def load_bundler!
84
- ENV["BUNDLE_GEMFILE"] ||= gemfile
85
-
86
- activate_bundler
87
- end
88
-
89
- def activate_bundler
90
- gem_error = activation_error_handling do
91
- gem "bundler", bundler_requirement
92
- end
93
- return if gem_error.nil?
94
- require_error = activation_error_handling do
95
- require "bundler/version"
96
- end
97
- return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
98
- warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
99
- exit 42
100
- end
101
-
102
- def activation_error_handling
103
- yield
104
- nil
105
- rescue StandardError, LoadError => e
106
- e
107
- end
108
- end
109
-
110
- m.load_bundler!
111
-
112
- if m.invoked_as_script?
113
- load Gem.bin_path("bundler", "bundle")
114
- end
data/bin/console DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require "bundler/setup"
5
- require "logfmt"
6
-
7
- require "pry"
8
- Pry.start
data/bin/rake DELETED
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- #
5
- # This file was generated by Bundler.
6
- #
7
- # The application 'rake' is installed as part of a gem, and
8
- # this file is here to facilitate running it.
9
- #
10
-
11
- require "pathname"
12
- ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
- Pathname.new(__FILE__).realpath)
14
-
15
- bundle_binstub = File.expand_path("../bundle", __FILE__)
16
-
17
- if File.file?(bundle_binstub)
18
- if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
- load(bundle_binstub)
20
- else
21
- abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
- Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
- end
24
- end
25
-
26
- require "rubygems"
27
- require "bundler/setup"
28
-
29
- load Gem.bin_path("rake", "rake")
data/bin/rspec DELETED
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- #
5
- # This file was generated by Bundler.
6
- #
7
- # The application 'rspec' is installed as part of a gem, and
8
- # this file is here to facilitate running it.
9
- #
10
-
11
- require "pathname"
12
- ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
- Pathname.new(__FILE__).realpath)
14
-
15
- bundle_binstub = File.expand_path("../bundle", __FILE__)
16
-
17
- if File.file?(bundle_binstub)
18
- if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
- load(bundle_binstub)
20
- else
21
- abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
- Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
- end
24
- end
25
-
26
- require "rubygems"
27
- require "bundler/setup"
28
-
29
- load Gem.bin_path("rspec-core", "rspec")