Class: ADSL::Extract::Rails::RailsExtractor
Instance Attribute Summary collapse
Instance Method Summary
collapse
#instrument_gem_authlogic, #instrument_gem_cancan, #instrument_gem_devise, #instrument_gem_ransack, #instrument_gems
#halting_status_of, #interrupt_callback_chain_on_render, #split_into_callbacks, #split_into_paths_that_will_or_will_not_halt
Constructor Details
Returns a new instance of RailsExtractor.
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
# File 'lib/adsl/extract/rails/rails_extractor.rb', line 20
def initialize(options = {})
options = Hash[
:ar_classes => default_activerecord_models,
:invariants => Dir['invariants/**/*_invs.rb'],
:instrumentation_filters => []
].merge options
@ar_classes = options[:ar_classes].map do |ar_class|
generator = ActiveRecordMetaclassGenerator.new ar_class
generator.generate_class
end
ar_class_names = @ar_classes.map(&:adsl_ast_class_name)
@invariant_extractor = ADSL::Extract::Rails::InvariantExtractor.new ar_class_names
@invariants = @invariant_extractor.(options[:invariants]).map{ |inv| inv.adsl_ast }
@instrumentation_filters = @invariant_extractor.instrumentation_filters
@instrumentation_filters += options[:instrumentation_filters]
@action_instrumenter = ADSL::Extract::Rails::ActionInstrumenter.new ar_class_names
@action_instrumenter.instrumentation_filters = @instrumentation_filters
@actions = []
all_routes.each do |route|
translation = action_to_adsl_ast(route)
@actions << translation unless translation.nil?
end
end
|
Instance Attribute Details
Returns the value of attribute actions.
18
19
20
|
# File 'lib/adsl/extract/rails/rails_extractor.rb', line 18
def actions
@actions
end
|
#ar_classes ⇒ Object
Returns the value of attribute ar_classes.
18
19
20
|
# File 'lib/adsl/extract/rails/rails_extractor.rb', line 18
def ar_classes
@ar_classes
end
|
#instrumentation_filters ⇒ Object
Returns the value of attribute instrumentation_filters.
18
19
20
|
# File 'lib/adsl/extract/rails/rails_extractor.rb', line 18
def instrumentation_filters
@instrumentation_filters
end
|
#invariants ⇒ Object
Returns the value of attribute invariants.
18
19
20
|
# File 'lib/adsl/extract/rails/rails_extractor.rb', line 18
def invariants
@invariants
end
|
Instance Method Details
#action_name_for(route) ⇒ Object
71
72
73
|
# File 'lib/adsl/extract/rails/rails_extractor.rb', line 71
def action_name_for(route)
"#{route[:controller]}__#{route[:action]}"
end
|
#action_of(route) ⇒ Object
182
183
184
185
|
# File 'lib/adsl/extract/rails/rails_extractor.rb', line 182
def action_of(route)
return nil unless route.defaults.include? :action
route.defaults[:action].to_sym
end
|
#action_to_adsl_ast(route) ⇒ Object
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
# File 'lib/adsl/extract/rails/rails_extractor.rb', line 101
def action_to_adsl_ast(route)
instrumentation_allows = @instrumentation_filters.map do |f|
f.allow_instrumentation? route[:controller].new, route[:action]
end
return nil if instrumentation_allows.include? false
action_name = action_name_for route
potential_adsl_asts = @actions.select{ |action| action.name.text == action_name }
raise "Multiple actions with identical names" if potential_adsl_asts.length > 1
return potential_adsl_asts.first if potential_adsl_asts.length == 1
prepare_instrumentation route[:controller], route[:action]
session = ActionDispatch::Integration::Session.new(::Rails.application)
::Rails.application.config.action_dispatch.show_exceptions = false
block = @action_instrumenter.exec_within do
@action_instrumenter.exec_within do
request_method = route[:request_method].to_s.downcase.split('|').first
session.send request_method, route[:url]
end
@action_instrumenter.abb.root_lvl_adsl_ast
end
interrupt_callback_chain_on_render block, route[:action]
action = ADSL::Parser::ASTAction.new({
:name => ADSL::Parser::ASTIdent.new(:text => action_name),
:arg_cardinalities => [],
:arg_names => [],
:arg_types => [],
:block => block
})
action = action.optimize
action.prepend_global_variables_by_signatures /^at__.*/, /^atat__.*/
action
end
|
201
202
203
204
205
206
207
|
# File 'lib/adsl/extract/rails/rails_extractor.rb', line 201
def adsl_ast
ADSL::Parser::ASTSpec.new(
:classes => @ar_classes.map(&:adsl_ast),
:actions => @actions,
:invariants => @invariants
)
end
|
#all_routes ⇒ Object
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
# File 'lib/adsl/extract/rails/rails_extractor.rb', line 48
def all_routes
::Rails.application.routes.routes.map{ |route|
{
:request_method => request_method_for(route),
:url => url_for(route),
:controller => controller_of(route),
:action => action_of(route)
}
}.select{ |route|
!route[:action].nil? &&
!route[:controller].nil? &&
!route[:url].nil? &&
!route[:request_method].nil? &&
route[:controller].action_methods.include?(route[:action].to_s)
}.uniq{ |a|
[a[:controller], a[:action]]
}.sort{ |a, b| [a[:controller].to_s, a[:action]] <=> [b[:controller].to_s, b[:action]] }
end
|
#callbacks(controller) ⇒ Object
75
76
77
|
# File 'lib/adsl/extract/rails/rails_extractor.rb', line 75
def callbacks(controller)
controller.respond_to?(:_process_action_callbacks) ? controller._process_action_callbacks : []
end
|
#controller_of(route) ⇒ Object
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
# File 'lib/adsl/extract/rails/rails_extractor.rb', line 167
def controller_of(route)
return nil unless route.defaults.include? :controller
possible_names = [
"#{route.defaults[:controller].pluralize}_controller".camelize,
"#{route.defaults[:controller]}_controller".camelize,
]
possible_names.each do |name|
begin
return name.constantize
rescue NameError
end
end
raise "No controller class found for #{route.defaults}; attempted class names are #{possible_names}"
end
|
#default_activerecord_models ⇒ Object
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
# File 'lib/adsl/extract/rails/rails_extractor.rb', line 141
def default_activerecord_models
models_dir = Rails.respond_to?(:root) ? Rails.root.join('app', 'models') : Pathname.new('app/models')
classes = Dir[models_dir.join '**', '*.rb'].map{ |path|
relative_path = /^#{Regexp.escape models_dir.to_s}\/(.*)\.rb$/.match(path)[1]
klass = nil
while klass.nil? && !relative_path.empty?
begin
klass = relative_path.camelize.constantize
rescue NameError, LoadError
end
if klass.nil?
relative_path = /^[^\/]+\/(.*)$/.match(relative_path)[1]
end
end
raise "Could not find class corresponding to path #{path}" if klass.nil?
klass
}.select{ |klass| klass < ActiveRecord::Base }
until_no_change(classes) do |classes|
all = classes.dup
classes.each do |c|
all << c.superclass unless classes.include?(c.superclass) || c.superclass == ActiveRecord::Base
end
all
end
end
|
#prepare_instrumentation(controller_class, action) ⇒ Object
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
# File 'lib/adsl/extract/rails/rails_extractor.rb', line 79
def prepare_instrumentation(controller_class, action)
controller_class.class_eval <<-ruby, __FILE__, __LINE__ + 1
def default_render(*args); end
def verify_authenticity_token; end
def params
ADSL::Extract::Rails::PartiallyUnknownHash.new(
:controller => '#{ controller_class.controller_name }',
:action => '#{ action }'
)
end
ruby
instrument_gems controller_class, action
controller = controller_class.new
@action_instrumenter.instrument controller, action
callbacks(controller_class).each do |callback|
next if callback.filter.is_a?(String)
@action_instrumenter.instrument controller, callback.filter
end
end
|
#request_method_for(route) ⇒ Object
187
188
189
190
191
|
# File 'lib/adsl/extract/rails/rails_extractor.rb', line 187
def request_method_for(route)
method_s = route.verb.source.match(/^\^?(.*?)\$?$/)[1]
return nil if method_s.empty?
method_s.to_sym
end
|
#route_for(controller, action) ⇒ Object
67
68
69
|
# File 'lib/adsl/extract/rails/rails_extractor.rb', line 67
def route_for(controller, action)
all_routes.select{ |a| a[:controller] == controller && a[:action] == action.to_sym}.first
end
|
#url_for(route) ⇒ Object
193
194
195
196
197
198
199
|
# File 'lib/adsl/extract/rails/rails_extractor.rb', line 193
def url_for(route)
params = {}
route.required_parts.each do |part|
params[part] = 0
end
route.format(params)
end
|