Skip to content

Commit

Permalink
Add JsonCommenter
Browse files Browse the repository at this point in the history
The JsonCommenter will generate JSON comments instead of a simple comma
seperated string.
  • Loading branch information
jfrancoist committed Apr 3, 2020
1 parent 3abc911 commit ef9d118
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 4 deletions.
21 changes: 20 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Install
gem install active_record-comments
```

Usage
Usage (Default commenter)
=====

```ruby
Expand All @@ -26,6 +26,25 @@ result = ActiveRecord::Comments.comment("account cleanup") do
end
```

Usage (Json commenter)
=====

```ruby
require "active_record/comments"

ActiveRecord::Comments.configure do |config|
config.enable_json_comment = true
end

# => SELECT ... /* {"user":"123"} */
result = ActiveRecord::Comments.comment(user: "123") { User.where("x like y").count }

# => SELECT ... /* {"account":"1","service":"commenter"} */
result = ActiveRecord::Comments.comment(account: "1") do
ActiveRecord::Comments.comment(service: "commenter") { User.where("x like y").count }
end
```

Author
======
[Michael Grosser](https://grosser.it)<br/>
Expand Down
7 changes: 6 additions & 1 deletion lib/active_record/comments.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require "active_record/comments/configuration"
require "active_record/comments/execute_with_comments"
require "active_record/comments/json_commenter"
require "active_record/comments/simple_commenter"
require "active_record/comments/version"
require "active_record"
Expand All @@ -18,7 +19,11 @@ def with_comment_sql(sql)
private

def commenter
simple_commenter
configuration.enable_json_comment ? json_commenter : simple_commenter
end

def json_commenter
@json_commenter ||= JsonCommenter.new
end

def simple_commenter
Expand Down
4 changes: 2 additions & 2 deletions lib/active_record/comments/configuration.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module ActiveRecord
module Comments
Configuration = Struct.new
Configuration = Struct.new(:enable_json_comment)

class << self
def configure
Expand All @@ -10,7 +10,7 @@ def configure
private

def configuration
@configuration ||= Configuration.new
@configuration ||= Configuration.new(false)
end
end
end
Expand Down
35 changes: 35 additions & 0 deletions lib/active_record/comments/json_commenter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
require "json"

module ActiveRecord
module Comments
class JsonCommenter
def comment(comment)
return yield unless comment.is_a?(Hash)

begin
orig = current_comments.dup
current_comments.merge!(comment)
yield
ensure
current_comments.replace(orig)
end
end

def with_comment_sql(sql)
return sql unless comment = current_comment

"#{sql} /* #{comment} */"
end

private

def current_comments
Thread.current[:ar_json_comment] ||= {}
end

def current_comment
current_comments.to_json if current_comments.present?
end
end
end
end
89 changes: 89 additions & 0 deletions spec/active_record/comments/json_commenter_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
require "spec_helper"

describe ActiveRecord::Comments::JsonCommenter do
subject(:commenter) { ActiveRecord::Comments::JsonCommenter.new }

describe "#current_comment" do
it "is empty when not a hash" do
commenter.comment("xxx") {}
expect(commenter.send(:current_comment)).to eq(nil)
end

it "is filled when called with hash" do
result = nil
commenter.comment(foo: "bar") do
result = commenter.send(:current_comment)
end
expect(result).to eq('{"foo":"bar"}')
end

it "concatenates to json when multiple comments" do
result = nil
commenter.comment(foo: "bar") do
commenter.comment(hello: "world") do
result = commenter.send(:current_comment)
end
end
expect(result).to eq('{"foo":"bar","hello":"world"}')
end

it "merges existing hash key" do
result = nil
commenter.comment(foo: "bar") do
commenter.comment(foo: "world") do
result = commenter.send(:current_comment)
end
end
expect(result).to eq('{"foo":"world"}')
end

it "removes comment when its block ends" do
result = nil
commenter.comment(foo: "bar") do
commenter.comment(hello: "world") {}
result = commenter.send(:current_comment)
end
expect(result).to eq('{"foo":"bar"}')
end

it "does not removes comment when its block ends and the key already exist" do
result = nil
commenter.comment(foo: "bar") do
commenter.comment(foo: "world") {}
result = commenter.send(:current_comment)
end
expect(result).to eq('{"foo":"bar"}')
end
end

describe "#comment" do
it "returns results" do
expect(commenter.comment(foo: "bar") { 1 }).to eq(1)
end
end

describe "#with_comment_sql" do
it "returns sql with single k/v pair comment" do
result = nil
commenter.comment(foo: "bar") do
result = commenter.with_comment_sql("SELECT * FROM User")
end
expect(result).to eq('SELECT * FROM User /* {"foo":"bar"} */')
end

it "returns sql with multiple k/v pair comment" do
result = nil
commenter.comment({foo: "bar", hello: "world"}) do
commenter.comment(hello: "foo") do
result = commenter.with_comment_sql("SELECT * FROM User")
end
end
expect(result).to eq('SELECT * FROM User /* {"foo":"bar","hello":"foo"} */')
end

it "returns sql without comments" do
result = commenter.with_comment_sql("SELECT * FROM User")
expect(result).to eq("SELECT * FROM User")
end
end
end
58 changes: 58 additions & 0 deletions spec/active_record/comments_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,62 @@ def capture_sql
end
end
end

context "using json commenter" do
before do
ActiveRecord::Comments.configure do |config|
config.enable_json_comment = true
end
end

describe "finding" do
it "is not there when not called" do
ActiveRecord::Comments.comment("xxx") {}
sql = capture_sql { User.where(id: 1).to_a }
expect(sql).to eq('SELECT * FROM "users" WHERE "users"."id" = ?')
end

it "is there when called" do
sql = nil
ActiveRecord::Comments.comment(foo: "bar") do
sql = capture_sql { User.where(id: 1).to_a }
end
expect(sql).to eq('SELECT * FROM "users" WHERE "users"."id" = ? /* {"foo":"bar"} */')
end

it "is thread safe" do
res = []
[{foo:"bar"}, {hello:"world"}].map do |comment|
Thread.new do
res << ActiveRecord::Comments.comment(comment) do
sleep 0.1 # make sure they both enter this block together
sql = capture_sql { User.where(id: 1).to_a }
end
end
end.each(&:join)

expect(res.sort.first).to match(/"foo":"bar"/)
expect(res.sort.first).not_to match(/"hello":"world"/)

expect(res.sort.last).to match(/"hello":"world"/)
expect(res.sort.last).not_to match(/"foo":"bar"/)
end
end

describe "counting" do
it "is not there when not called" do
ActiveRecord::Comments.comment(foo: "bar") {}
sql = capture_sql { User.where(id: 1).count }
expect(sql).to eq('SELECT COUNT(*) FROM "users" WHERE "users"."id" = ?')
end

it "is there when called" do
sql = nil
ActiveRecord::Comments.comment(foo: "bar") do
sql = capture_sql { User.where(id: 1).count }
end
expect(sql).to eq('SELECT COUNT(*) FROM "users" WHERE "users"."id" = ? /* {"foo":"bar"} */')
end
end
end
end

0 comments on commit ef9d118

Please sign in to comment.