Skip to content

Commit

Permalink
Merge pull request #200 from indirect/thread-safety
Browse files Browse the repository at this point in the history
thread safety
  • Loading branch information
indirect authored Dec 4, 2023
2 parents ef45ca1 + 0426fee commit 2fe9123
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 139 deletions.
124 changes: 65 additions & 59 deletions README.rdoc → README.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,42 @@
= Rails 7 Footnotes
# Rails 7 Footnotes

Rails footnotes displays footnotes in your application for easy debugging, such as sessions,
request parameters, cookies, filter chain, routes, queries, etc.

Even more, it contains links to open files directly in your editor including
your backtrace lines.

== Installation
### Installation

Add to your `Gemfile`:
1. Add to your `Gemfile` with `bundle add rails-footnotes`
2. Run `bundle install`
3. Generate the initializer with `bin/rails generate rails_footnotes:install`

gem "rails-footnotes", "~> 5.0"

Run `bundle install`, then the generator:
This will create an initializer with default config and some examples.

rails generate rails_footnotes:install
### Hooks

This will create an initializer with default config and some examples.
You can run blocks before and after footnotes are evaluated.

=== Hooks
```
Footnotes.setup do |config|
config.before do |controller, filter|
filter.notes = (controller.class.name =~ /Message/ && controller.action_name == 'index' ? [:assigns] : [])
end
Footnotes.setup do |config|
config.before {|controller, filter| filter.notes = controller.class.name =~ /Message/ && \
controller.action_name == 'index' ? [:assigns] : []}
config.before {|controller, filter| filter.notes |= [:params] if controller.class.name =~ /Profile/ && \
controller.action_name == 'edit' }
end
config.before do |controller, filter|
filter.notes |= [:params] if controller.class.name =~ /Profile/ && controller.action_name == 'edit'
end
end
```

=== Editor links
### Editor links

Textmate, MacVim and Sublime Text 3 are compatible.
By default, files are linked to open in TextMate, but you can use any editor with a URL scheme. Here are some examples for other editors:

*MacVim*
**MacVim**

In the rails-footnotes initializer do :
In `config/initializers/rails-footnotes.rb` do:

f.prefix = 'mvim://open?url=file://%s&line=%d&column=%d'

Expand All @@ -44,26 +47,26 @@ the second %d is replaced by the column number.
Take note that the order in which the file name (%s), line number (%d) and column number (%d) appears is important.
We assume that they appear in that order. "foo://line=%d&file=%s" (%d precedes %s) would throw out an error.

*Sublime* *Text* *3*
**Sublime Text 3**

Install {subl}[https://github.com/dhoulb/subl], then use:
Install [subl](https://github.com/dhoulb/subl), then use:

f.prefix = 'subl://open?url=file://%s&line=%d&column=%d'

*Use* *with* *Vagrant* (*and* *other* *virtual* *machines*)
**Use with Docker, Vagrant, or other virtual machines**

If you're running your app in Vagrant, you'll find that the edit links won't work because the paths point to the Vagrant directory not your native directory. To solve this, you can use a lambda for the prefix and modify the pathname accordingly.
If you're running your app in a container or VM, you'll find that the edit links won't work because the paths point to the VM directory and not your native directory. To solve this, you can use a lambda for the prefix and modify the pathname accordingly.

For example,

f.prefix = ->(*args) do
filename = args[0].sub '/vagrant', '/Users/jamie/projects/myproject'
filename = args[0].sub '/docker', '/Users/name/projects/myproject'
"subl://open?url=file://#{filename}&line=#{args[1]}&column=#{args[2]}"
end

replaces the vm directory /vagrant with OS X directory where the code is being edited.
replaces the VM directory /docker with the macOS directory containing the source code.

*Footnotes* *Display* *Options*
**Footnotes Display Options**

By default, footnotes are appended at the end of the page with default stylesheet. If you want
to change their position, you can define a div with id "footnotes_holder" or define your own stylesheet
Expand All @@ -90,7 +93,7 @@ Finally, you can control which notes you want to show. The default are:

Setting <tt>f.notes = []</tt> will show none of the available notes, although the supporting CSS and JavaScript will still be included. To completely disable all rails-footnotes content on a page, include <tt>params[:footnotes] = 'false'</tt> in the request.

== Creating your own notes
### Creating your own notes

Creating your notes to integrate with Footnotes is easy.

Expand All @@ -100,49 +103,52 @@ Creating your notes to integrate with Footnotes is easy.

For example, to create a note that shows info about the user logged in your application you just have to do:

module Footnotes
module Notes
class CurrentUserNote < AbstractNote
# This method always receives a controller
#
def initialize(controller)
@current_user = controller.instance_variable_get("@current_user")
end

# Returns the title that represents this note.
#
def title
"Current user: #{@current_user.name}"
end

# This Note is only valid if we actually found an user
# If it's not valid, it won't be displayed
#
def valid?
@current_user
end

# The fieldset content
#
def content
escape(@current_user.inspect)
module Footnotes
module Notes
class CurrentUserNote < AbstractNote
# This method always receives a controller
#
def initialize(controller)
@current_user = controller.instance_variable_get("@current_user")
end

# Returns the title that represents this note.
#
def title
"Current user: #{@current_user.name}"
end

# This Note is only valid if we actually found an user
# If it's not valid, it won't be displayed
#
def valid?
@current_user
end

# The fieldset content
#
def content
escape(@current_user.inspect)
end
end
end
end
end

Then put in your environment, add in your initializer:

f.notes += [:current_user]
f.notes += [:current_user]

== Footnote position
### Footnote position

By default the notes will be showed at the bottom of your page (appended just before </body>).
By default the notes will be showed at the bottom of your page (appended just before `</body>`).
If you'd like the footnote, to be at a different place (perhaps for aesthetical reasons) you can edit one of your views and add:
<div id="footnotes_holder"></div>

<div id="footnotes_holder"></div>

at an appropriate place, your notes will now appear inside div#footnotes_holder

== Bugs and Feedback
### Bugs and Feedback

If you discover any bugs, please open an issue.
If you just want to give some positive feedback or drop a line, that's fine too!

27 changes: 11 additions & 16 deletions lib/rails-footnotes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,9 @@
require 'rails-footnotes/extension'

module Footnotes
mattr_accessor :before_hooks
@@before_hooks = []

mattr_accessor :after_hooks
@@after_hooks = []

mattr_accessor :enabled
@@enabled = false
thread_mattr_accessor :before_hooks, default: []
thread_mattr_accessor :after_hooks, default: []
thread_mattr_accessor :enabled, default: false

class << self
delegate :notes, :to => Filter
Expand All @@ -40,22 +35,22 @@ class << self
end

def self.before(&block)
@@before_hooks << block
before_hooks << block
end

def self.after(&block)
@@after_hooks << block
after_hooks << block
end

def self.enabled?(controller)
if @@enabled.is_a? Proc
if @@enabled.arity == 1
@@enabled.call(controller)
if enabled.is_a? Proc
if enabled.arity == 1
enabled.call(controller)
else
@@enabled.call
enabled.call
end
else
!!@@enabled
!!enabled
end
end

Expand All @@ -68,4 +63,4 @@ def self.setup
ActionController::Base.send(:include, Footnotes::RailsFootnotesExtension)
end

load Rails.root.join('.rails_footnotes') if Rails.root && Rails.root.join('.rails_footnotes').exist?
load Rails.root.join('.rails_footnotes') if Rails.root&.join('.rails_footnotes')&.exist?
69 changes: 39 additions & 30 deletions lib/rails-footnotes/filter.rb
Original file line number Diff line number Diff line change
@@ -1,28 +1,37 @@
module Footnotes
class Filter
@@no_style = false
@@multiple_notes = false
@@klasses = []
@@lock_top_right = false
@@font_size = '11px'
@@default_limit = 25

# Default link prefix is textmate
@@prefix = 'txmt://open?url=file://%s&amp;line=%d&amp;column=%d'

# Edit notes
@@notes = [ :controller, :view, :layout, :partials, :stylesheets, :javascripts ]
# Show notes
@@notes += [ :assigns, :session, :cookies, :params, :filters, :routes, :env, :queries, :log]

# :default_limit => Default limit for ActiveRecord:Relation in assigns note
# :font_size => CSS font-size property
# :lock_top_right => Lock a btn to toggle notes to the top right of the browser
# :multiple_notes => Set to true if you want to open several notes at the same time
# :no_style => If you don't want the style to be appended to your pages
# :notes => Class variable that holds the notes to be processed
# :prefix => Prefix appended to FootnotesLinks
# :multiple_notes => Set to true if you want to open several notes at the same time
# :lock_top_right => Lock a btn to toggle notes to the top right of the browser
# :font_size => CSS font-size property
# :default_limit => Default limit for ActiveRecord:Relation in assigns note
cattr_accessor :no_style, :notes, :prefix, :multiple_notes, :lock_top_right, :font_size, :default_limit
# :notes => Class variable that holds the notes to be processed
thread_cattr_accessor :default_limit, default: 25
thread_cattr_accessor :font_size, default: '11px'
thread_cattr_accessor :lock_top_right, default: false
thread_cattr_accessor :multiple_notes, default: false
thread_cattr_accessor :no_style, default: false
thread_cattr_accessor :prefix, default: 'txmt://open?url=file://%s&amp;line=%d&amp;column=%d'
thread_cattr_accessor :notes, default: [
:assigns,
:controller,
:cookies,
:env,
:filters,
:javascripts,
:layout,
:log,
:params,
:partials,
:queries,
:routes,
:session,
:stylesheets,
:view
]

class << self
include Footnotes::EachWithRescue
Expand All @@ -35,7 +44,7 @@ def start!(controller)
self.each_with_rescue(Footnotes.before_hooks) {|hook| hook.call(controller, self)}

@@klasses = []
self.each_with_rescue(@@notes.flatten) do |note|
self.each_with_rescue(notes.flatten) do |note|
klass = "Footnotes::Notes::#{note.to_s.camelize}Note".constantize
klass.start!(controller) if klass.respond_to?(:start!)
@@klasses << klass
Expand All @@ -44,17 +53,17 @@ def start!(controller)

# If none argument is sent, simply return the prefix.
# Otherwise, replace the args in the prefix.
#
alias_method :read_prefix, :prefix
def prefix(*args)
if args.empty?
@@prefix
read_prefix
else
args.map! { |arg| arg.to_s.split("/").map{|s| ERB::Util.url_encode(s) }.join("/") }

if @@prefix.respond_to? :call
@@prefix.call *args
if read_prefix.respond_to? :call
read_prefix.call(*args)
else
format(@@prefix, *args)
format(read_prefix, *args)
end
end
end
Expand Down Expand Up @@ -97,7 +106,7 @@ def valid?

def add_footnotes_without_validation!
initialize_notes!
insert_styles unless @@no_style
insert_styles unless no_style
insert_footnotes
end

Expand Down Expand Up @@ -151,7 +160,7 @@ def attached_file?

def insert_styles
#TODO More customizable(reset.css, from file etc.)
if @@lock_top_right
if lock_top_right
extra_styles = <<-STYLES
#footnotes_debug {position: fixed; top: 0px; right: 0px; width: 100%; z-index: 10000; margin-top: 0;}
#footnotes_debug #toggle_footnotes {position: absolute; right: 0; top: 0; background: #fff; border: 1px solid #ccc; color: #9b1b1b; font-size: 20px; text-align: center; padding: 8px; opacity: 0.9;}
Expand All @@ -167,7 +176,7 @@ def insert_styles
insert_text :before, /<\/head>/i, <<-HTML
<!-- Footnotes Style -->
<style type="text/css">
#footnotes_debug {font-size: #{@@font_size}; font-family: Consolas, monaco, monospace; font-weight: normal; margin: 2em 0 1em 0; text-align: center; color: #444; line-height: 16px; background: #fff;}
#footnotes_debug {font-size: #{font_size}; font-family: Consolas, monaco, monospace; font-weight: normal; margin: 2em 0 1em 0; text-align: center; color: #444; line-height: 16px; background: #fff;}
#footnotes_debug th, #footnotes_debug td {color: #444; line-height: 18px;}
#footnotes_debug a {color: #9b1b1b; font-weight: inherit; text-decoration: none; line-height: 18px;}
#footnotes_debug table {text-align: left; width: 100%;}
Expand All @@ -192,7 +201,7 @@ def insert_footnotes
# Fieldsets method should be called first
content = fieldsets
element_style = ''
if @@lock_top_right
if lock_top_right
element_style = 'style="display: none;"'
end
footnotes_html = <<-HTML
Expand All @@ -208,7 +217,7 @@ def insert_footnotes
var Footnotes = function() {
function hideAll(){
#{close unless @@multiple_notes}
#{close unless multiple_notes}
}
function hideAllAndToggle(id) {
Expand Down
Loading

0 comments on commit 2fe9123

Please sign in to comment.