arca 1.0.0 → 2.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0e3c9c827e804d9e159cd1287dd96d2f51a73b7c
4
- data.tar.gz: 2258bba04de95cb191e8db9819ff6b170cd96ab0
3
+ metadata.gz: ca8734a74ea5a5b2ab9c36ec62439ecb515a9669
4
+ data.tar.gz: 2638b950202f3f0c760bebeb7bf4f39ebc75adcb
5
5
  SHA512:
6
- metadata.gz: a769d84288997f5dcb1557d3e8061b2c215a1bf492f63da1c9d84d4b37a6552a59b90a53f6531104e0b2ecfe0cdf20b88b34c6611d243b85385cfff04a24ae85
7
- data.tar.gz: cbebb0af251cfbd5d8900d85aaaa9c973e8ca29a03cb9d73a94d0591f640b6cf6e7f5e03455859bea967346a01e2481bbae49949b6fb06ca8573c8dc3228a74b
6
+ metadata.gz: b4dfcbd12c0f48b3c33db3de0be2855edc7bb829b8fc7712f7a9666c27c8480cf7d6e8730a8a64859a1cc8ab27ff110d0d5b01da799e49ed2ecbf036a9463f5d
7
+ data.tar.gz: edc74a7ad1d0e13e57802db09dcacb9c0c9b45f4c8e3c366d7727870a721b5b137259ea8c5e3bed544cee82a85df663d64fe03dca9f89dcea466e2f5154512a4
data/README.md CHANGED
@@ -16,20 +16,21 @@ Add the gem to your Gemfile and run `bundle`.
16
16
  gem 'arca'
17
17
  ```
18
18
 
19
- Add an initializer to require the library and configure it (`config/initializers/arca.rb` for example). There's no magic here and Arca doesn't assume your root project path or the path to your `ActiveRecord` models so you have to specify those paths yourself in the initializer.
19
+ In your test helper (`test/test_helper.rb` for example) require the Arca library and include the `Arca::Collector` in `ActiveRecord::Base`.
20
20
 
21
21
  ```
22
22
  require "arca"
23
23
 
24
- Arca.root_path = Rails.root
25
- Arca.model_path = Rails.root.join("app", "models")
24
+ class ActiveRecord::Base
25
+ include Arca::Collector
26
+ end
26
27
  ```
27
28
 
28
- Include `Arca::Collector` in the models you want to analyze. It must be included before any callbacks so I recommend including it right after the class definition.
29
+ In this example the `Annoucements` module is included in `Ticket` and defines it's own callback.
30
+
29
31
 
30
32
  ```ruby
31
33
  class Ticket < ActiveRecord::Base
32
- include Arca::Collector
33
34
  include Announcements
34
35
 
35
36
  before_save :set_title, :set_body
@@ -53,8 +54,6 @@ class Ticket < ActiveRecord::Base
53
54
  end
54
55
  ```
55
56
 
56
- In this example the `Annoucements` module is included in `Ticket` and defines it's own callback.
57
-
58
57
  ```ruby
59
58
  module Announcements
60
59
  def self.included(base)
@@ -69,7 +68,7 @@ module Announcements
69
68
  end
70
69
  ```
71
70
 
72
- Next use `Arca[Ticket].report` to analyze the callbacks for the `Ticket` class.
71
+ Use `Arca[Ticket].report` to analyze the callbacks for the `Ticket` class.
73
72
 
74
73
  ```ruby
75
74
  > Arca.root_path = `pwd`.chomp
data/arca.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "arca"
3
- spec.version = "1.0.0"
3
+ spec.version = "2.0.0"
4
4
  spec.date = "2015-07-11"
5
5
  spec.summary = "ActiveRecord callback analyzer"
6
6
  spec.description = "Arca is a callback analyzer for ActiveRecord ideally suited for digging yourself out of callback hell"
@@ -80,6 +80,7 @@ module Arca
80
80
  # Public: Boolean representing whether the callback target is located in the
81
81
  # same file where the callback is defined.
82
82
  def external_target?
83
+ return false if target_symbol == :block
83
84
  target_file_path != callback_file_path
84
85
  end
85
86
 
@@ -4,35 +4,22 @@ module Arca
4
4
  # about how callbacks are being used.
5
5
  module Collector
6
6
 
7
- # Error raised if Arca.model_root_path is nil.
8
- class ModelRootPathRequired < StandardError; end
9
-
10
7
  # Internal: Regular expression used for extracting the file path and line
11
8
  # number from a caller line.
12
- ARCA_LINE_PARSER_REGEXP = /\A(.+)\:(\d+)\:in\s(.+)\z/
13
- private_constant :ARCA_LINE_PARSER_REGEXP
9
+ ARCA_CALLBACK_FINDER_REGEXP = /\A(.+)\:(\d+)\:in\s`.+'\z/
10
+ private_constant :ARCA_CALLBACK_FINDER_REGEXP
14
11
 
15
12
  # Internal: Array of conditional symbols.
16
- ARCA_CONDITIONALS = [:if, :unless]
13
+ ARCA_CONDITIONALS = [:if, :unless, :on]
17
14
  private_constant :ARCA_CONDITIONALS
18
15
 
19
16
  # http://ruby-doc.org/core-2.2.1/Module.html#method-i-included
20
17
  def self.included(base)
21
- # Raise error if Arca.model_root_path is nil.
22
- raise ModelRootPathRequired if Arca.model_root_path.nil?
23
-
24
- # Get the file path to the model class that included the collector.
25
- model_file_path, = caller[0].partition(":")
26
-
27
18
  base.class_eval do
28
- # Define :arca_callback_data for storing the data we collect.
29
- define_singleton_method :arca_callback_data do
30
- @callbacks ||= {}
19
+ define_singleton_method(:arca_callback_data) do
20
+ @arca_callback_data ||= Hash.new {|k,v| k[v] = [] }
31
21
  end
32
22
 
33
- # Collect the model_file_path.
34
- arca_callback_data[:model_file_path] = model_file_path
35
-
36
23
  # Find the callback methods defined on this class.
37
24
  callback_method_symbols = singleton_methods.grep /^(after|around|before)\_/
38
25
 
@@ -42,23 +29,32 @@ module Arca
42
29
 
43
30
  # Redefine the callback method so that data can be collected each time
44
31
  # the callback is used for this class.
45
- define_singleton_method(callback_method.name) do |*args|
32
+ define_singleton_method(callback_method.name) do |*args, &block|
46
33
  # Duplicate args before modifying.
47
34
  args_copy = args.dup
48
35
 
49
- # Get the options hash from the end of the args Array if it exists.
50
- options = args_copy.pop if args[-1].is_a?(Hash)
36
+ # Add target_symbol :block to args_copy if a block was given.
37
+ if block
38
+ args_copy << :block
39
+ else
40
+ # Get the options hash from the end of the args Array if it exists.
41
+ options = args_copy.pop if args[-1].is_a?(Hash)
42
+ end
43
+
44
+ # Get the callback file path and line number from the caller stack.
45
+ callback_file_path, callback_line_number = ARCA_CALLBACK_FINDER_REGEXP.match(caller.first)[1..2]
46
+
47
+ # Extract the model file path from the caller stack.
48
+ caller.each do |line|
49
+ if match = /\A(.+):\d+:in\s`<class:#{name.split("::").last}>'/.match(line)
50
+ self.arca_callback_data[:model_file_path] = match[1]
51
+ break
52
+ end
53
+ end
51
54
 
52
55
  # Iterate through the rest of the args. Each remaining arguement is
53
56
  # a Symbol representing the callback target method.
54
57
  args_copy.each do |target_symbol|
55
-
56
- # Find the caller line where the callback is used.
57
- line = caller.find {|line| line =~ /#{Regexp.escape(Arca.model_root_path)}/ }
58
-
59
- # Parse the line in order to extract the file path and line number.
60
- callback_line_matches = line.match(ARCA_LINE_PARSER_REGEXP)
61
-
62
58
  # Find the conditional symbol if it exists in the options hash.
63
59
  conditional_symbol = ARCA_CONDITIONALS.
64
60
  find {|conditional| options && options.has_key?(conditional) }
@@ -76,8 +72,8 @@ module Arca
76
72
  # this callback_symbol.
77
73
  arca_callback_data[callback_symbol] << {
78
74
  :callback_symbol => callback_symbol,
79
- :callback_file_path => callback_line_matches[1],
80
- :callback_line_number => callback_line_matches[2].to_i,
75
+ :callback_file_path => callback_file_path,
76
+ :callback_line_number => callback_line_number.to_i,
81
77
  :target_symbol => target_symbol,
82
78
  :conditional_symbol => conditional_symbol,
83
79
  :conditional_target_symbol => conditional_target_symbol
@@ -86,7 +82,7 @@ module Arca
86
82
  end
87
83
 
88
84
  # Bind the callback method to self and call it with args.
89
- callback_method.bind(self).call(*args)
85
+ callback_method.bind(self).call(*args, &block)
90
86
  end
91
87
  end
92
88
  end
data/lib/arca/report.rb CHANGED
@@ -1,5 +1,8 @@
1
+ require "forwardable"
2
+
1
3
  module Arca
2
4
  class Report
5
+ extend Forwardable
3
6
 
4
7
  # Arca::Report takes an Arca::Model and compiles the analyzed callback data
5
8
  # into a short overview report for the model.
@@ -51,8 +54,8 @@ module Arca
51
54
  number_of_unique_conditionals(model.analyzed_callbacks_array)
52
55
  end
53
56
 
54
- delegate :lines_between_count, :external_callbacks_count,
55
- :external_targets_count, :external_conditionals_count, :to => :model
57
+ def_delegators :model, :lines_between_count, :external_callbacks_count,
58
+ :external_targets_count, :external_conditionals_count
56
59
 
57
60
  # Public: Integer representing the possible number of permutations stemming
58
61
  # from conditionals for an instance of the model being reported during the
data/lib/arca.rb CHANGED
@@ -20,7 +20,7 @@ module Arca
20
20
  Arca::Model.new(klass)
21
21
  end
22
22
 
23
- # Public: (optional) Writer method for configuring the root path of the
23
+ # Public: Writer method for configuring the root path of the
24
24
  # project where Arca is being used. Setting Arca.root_path will makes
25
25
  # inspecting analyzed callbacks easier by shortening absolute paths to
26
26
  # relative paths.
@@ -32,19 +32,7 @@ module Arca
32
32
 
33
33
  # Public: String representing the root path for the project.
34
34
  def self.root_path
35
- @root_path
36
- end
37
-
38
- # Public: (required) Writer method for configuring the root path to the models
39
- # for the project where Arca is being used. This path is required by the
40
- # Arca::Collector for finding the correct line in the caller Array.
41
- def self.model_root_path=(path)
42
- @model_root_path = path.to_s
43
- end
44
-
45
- # Public: String representing the path to the models for the project.
46
- def self.model_root_path
47
- @model_root_path
35
+ @root_path ||= Dir.pwd
48
36
  end
49
37
 
50
38
  # Public: Helper method for turning absolute paths into relative paths.
@@ -55,8 +43,8 @@ module Arca
55
43
  def self.relative_path(path)
56
44
  return if path.nil?
57
45
 
58
- if @root_path
59
- path.sub(/^#{Regexp.escape(@root_path) || ""}\//, "")
46
+ if root_path
47
+ path.sub(/^#{Regexp.escape(root_path) || ""}\//, "")
60
48
  else
61
49
  path
62
50
  end
@@ -1,6 +1,7 @@
1
1
  module Announcements
2
2
  def self.included(base)
3
3
  base.class_eval do
4
+ before_save { puts "pre-save announcement callback" }
4
5
  after_save :announce_save
5
6
  end
6
7
  end
@@ -1,5 +1,4 @@
1
1
  class Ticket < ActiveRecord::Base
2
- include Arca::Collector
3
2
  include Announcements
4
3
 
5
4
  before_save :set_title, :set_body
@@ -84,9 +84,9 @@ class Arca::CallbackAnalysisTest < Minitest::Test
84
84
  end
85
85
 
86
86
  def test_target_line_number
87
- assert_equal 8, announce_save.target_line_number
88
- assert_equal 8, set_title.target_line_number
89
- assert_equal 16, upcase_title.target_line_number
87
+ assert_equal 9, announce_save.target_line_number
88
+ assert_equal 7, set_title.target_line_number
89
+ assert_equal 15, upcase_title.target_line_number
90
90
  end
91
91
 
92
92
  def test_external_target?
@@ -96,9 +96,9 @@ class Arca::CallbackAnalysisTest < Minitest::Test
96
96
  end
97
97
 
98
98
  def test_lines_to_target
99
- assert_equal 4, announce_save.lines_to_target
100
- assert_equal 3, set_title.lines_to_target
101
- assert_equal 10, upcase_title.lines_to_target
99
+ assert_equal 5, announce_save.lines_to_target
100
+ assert_equal 2, set_title.lines_to_target
101
+ assert_equal 9, upcase_title.lines_to_target
102
102
  end
103
103
 
104
104
  def test_conditional_symbol
@@ -122,12 +122,12 @@ class Arca::CallbackAnalysisTest < Minitest::Test
122
122
  def test_conditional_target_line_number
123
123
  assert_nil announce_save.conditional_target_line_number
124
124
  assert_nil set_title.conditional_target_line_number
125
- assert_equal 20, upcase_title.conditional_target_line_number
125
+ assert_equal 19, upcase_title.conditional_target_line_number
126
126
  end
127
127
 
128
128
  def test_lines_to_conditional_target
129
129
  assert_nil announce_save.lines_to_conditional_target
130
130
  assert_nil set_title.lines_to_conditional_target
131
- assert_equal 14, upcase_title.lines_to_conditional_target
131
+ assert_equal 13, upcase_title.lines_to_conditional_target
132
132
  end
133
133
  end
@@ -3,51 +3,50 @@ require_relative "../../test_helper"
3
3
  class Arca::CollectorTest < Minitest::Test
4
4
  def test_collects_callback_data
5
5
  callbacks = Ticket.arca_callback_data
6
+ assert_equal 1, callbacks[:after_save].size
7
+ assert_equal 4, callbacks[:before_save].size
6
8
 
7
9
  callback = callbacks[:after_save][0]
8
10
  assert_equal :after_save, callback[:callback_symbol]
9
11
  assert_match "test/fixtures/announcements.rb", callback[:callback_file_path]
10
- assert_equal 4, callback[:callback_line_number]
12
+ assert_equal 5, callback[:callback_line_number]
11
13
  assert_equal :announce_save, callback[:target_symbol]
12
14
  assert_nil callback[:conditional_symbol]
13
15
  assert_nil callback[:conditional_target_symbol]
14
16
 
15
17
  callback = callbacks[:before_save][0]
16
18
  assert_equal :before_save, callback[:callback_symbol]
19
+ assert_match "test/fixtures/announcements.rb", callback[:callback_file_path]
20
+ assert_equal 4, callback[:callback_line_number]
21
+ assert_equal :block, callback[:target_symbol]
22
+ assert_nil callback[:conditional_symbol]
23
+ assert_nil callback[:conditional_target_symbol]
24
+
25
+ callback = callbacks[:before_save][1]
26
+ assert_equal :before_save, callback[:callback_symbol]
17
27
  assert_match "test/fixtures/ticket.rb", callback[:callback_file_path]
18
- assert_equal 5, callback[:callback_line_number]
28
+ assert_equal 4, callback[:callback_line_number]
19
29
  assert_equal :set_title, callback[:target_symbol]
20
30
  assert_nil callback[:conditional_symbol]
21
31
  assert_nil callback[:conditional_target_symbol]
22
32
 
23
- callback = callbacks[:before_save][1]
33
+ callback = callbacks[:before_save][2]
24
34
  assert_equal :before_save, callback[:callback_symbol]
25
35
  assert_match "test/fixtures/ticket.rb", callback[:callback_file_path]
26
- assert_equal 5, callback[:callback_line_number]
36
+ assert_equal 4, callback[:callback_line_number]
27
37
  assert_equal :set_body, callback[:target_symbol]
28
38
  assert_nil callback[:conditional_symbol]
29
39
  assert_nil callback[:conditional_target_symbol]
30
40
 
31
- callback = callbacks[:before_save][2]
41
+ callback = callbacks[:before_save][3]
32
42
  assert_equal :before_save, callback[:callback_symbol]
33
43
  assert_match "test/fixtures/ticket.rb", callback[:callback_file_path]
34
- assert_equal 6, callback[:callback_line_number]
44
+ assert_equal 5, callback[:callback_line_number]
35
45
  assert_equal :upcase_title, callback[:target_symbol]
36
46
  assert_equal :if, callback[:conditional_symbol]
37
47
  assert_equal :title_is_a_shout?, callback[:conditional_target_symbol]
38
48
  end
39
49
 
40
- def test_arca_model_root_path_is_required
41
- model_root_path = Arca.model_root_path
42
- Arca.instance_variable_set(:@model_root_path, nil)
43
-
44
- assert_raises(Arca::Collector::ModelRootPathRequired) do
45
- require_relative "../../fixtures/bar"
46
- end
47
-
48
- Arca.model_root_path = model_root_path
49
- end
50
-
51
50
  def test_callback_is_reapplied_with_original_args
52
51
  foo = Foo.new
53
52
  refute foo.boop?
@@ -12,7 +12,7 @@ class Arca::ModelTest < Minitest::Test
12
12
  def test_source_location
13
13
  source_location = model.source_location(:set_title)
14
14
  assert_match "test/fixtures/ticket.rb", source_location[:file_path]
15
- assert_equal 8, source_location[:line_number]
15
+ assert_equal 7, source_location[:line_number]
16
16
  end
17
17
 
18
18
  def test_source_location_with_method_symbol_with_no_associated_method
@@ -28,25 +28,25 @@ class Arca::ModelTest < Minitest::Test
28
28
  end
29
29
 
30
30
  def test_analyzed_callbacks
31
- assert_equal 3, model.analyzed_callbacks[:before_save].size
31
+ assert_equal 4, model.analyzed_callbacks[:before_save].size
32
32
  assert_equal 1, model.analyzed_callbacks[:after_save].size
33
33
  end
34
34
 
35
35
  def test_analyzed_callbacks_array
36
- assert_equal 4, model.analyzed_callbacks_array.size
36
+ assert_equal 5, model.analyzed_callbacks_array.size
37
37
  assert model.analyzed_callbacks_array[0].is_a?(Arca::CallbackAnalysis)
38
38
  end
39
39
 
40
40
  def test_analyzed_callbacks_count
41
- assert_equal 4, model.analyzed_callbacks_count
41
+ assert_equal 5, model.analyzed_callbacks_count
42
42
  end
43
43
 
44
44
  def test_lines_between_count
45
- assert_equal 6, model.lines_between_count
45
+ assert_equal 5, model.lines_between_count
46
46
  end
47
47
 
48
48
  def test_external_callbacks_count
49
- assert_equal 1, model.external_callbacks_count
49
+ assert_equal 2, model.external_callbacks_count
50
50
  end
51
51
 
52
52
  def test_external_targets_count
data/test/test_helper.rb CHANGED
@@ -1,10 +1,11 @@
1
1
  $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
2
2
  require "active_record"
3
3
  require "minitest/autorun"
4
-
5
4
  require "arca"
6
- Arca.root_path = `pwd`.chomp
7
- Arca.model_root_path = Arca.root_path + "/test/fixtures"
5
+
6
+ class ActiveRecord::Base
7
+ include Arca::Collector
8
+ end
8
9
 
9
10
  require_relative "fixtures/announcements"
10
11
  require_relative "fixtures/ticket"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: arca
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Hoyt
@@ -70,7 +70,6 @@ files:
70
70
  - lib/arca/model.rb
71
71
  - lib/arca/report.rb
72
72
  - test/fixtures/announcements.rb
73
- - test/fixtures/bar.rb
74
73
  - test/fixtures/foo.rb
75
74
  - test/fixtures/ticket.rb
76
75
  - test/lib/arca/callback_analysis_test.rb
@@ -105,7 +104,6 @@ specification_version: 4
105
104
  summary: ActiveRecord callback analyzer
106
105
  test_files:
107
106
  - test/fixtures/announcements.rb
108
- - test/fixtures/bar.rb
109
107
  - test/fixtures/foo.rb
110
108
  - test/fixtures/ticket.rb
111
109
  - test/lib/arca/callback_analysis_test.rb
data/test/fixtures/bar.rb DELETED
@@ -1,3 +0,0 @@
1
- class Bar < ActiveRecord::Base
2
- include Arca::Collector
3
- end