arca 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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