rails_ops 1.0.0.beta1
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 +7 -0
- data/.gitignore +11 -0
- data/.rubocop.yml +84 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +1216 -0
- data/RUBY_VERSION +1 -0
- data/Rakefile +39 -0
- data/VERSION +1 -0
- data/lib/rails_ops.rb +96 -0
- data/lib/rails_ops/authorization_backend/abstract.rb +7 -0
- data/lib/rails_ops/authorization_backend/can_can_can.rb +14 -0
- data/lib/rails_ops/configuration.rb +4 -0
- data/lib/rails_ops/context.rb +35 -0
- data/lib/rails_ops/controller_mixin.rb +105 -0
- data/lib/rails_ops/exceptions.rb +19 -0
- data/lib/rails_ops/hooked_job.rb +25 -0
- data/lib/rails_ops/hookup.rb +80 -0
- data/lib/rails_ops/hookup/dsl.rb +29 -0
- data/lib/rails_ops/hookup/dsl_validator.rb +45 -0
- data/lib/rails_ops/hookup/hook.rb +11 -0
- data/lib/rails_ops/log_subscriber.rb +24 -0
- data/lib/rails_ops/mixins.rb +2 -0
- data/lib/rails_ops/mixins/authorization.rb +83 -0
- data/lib/rails_ops/mixins/log_settings.rb +20 -0
- data/lib/rails_ops/mixins/model.rb +4 -0
- data/lib/rails_ops/mixins/model/authorization.rb +64 -0
- data/lib/rails_ops/mixins/model/nesting.rb +180 -0
- data/lib/rails_ops/mixins/policies.rb +42 -0
- data/lib/rails_ops/mixins/require_context.rb +33 -0
- data/lib/rails_ops/mixins/routes.rb +35 -0
- data/lib/rails_ops/mixins/schema_validation.rb +25 -0
- data/lib/rails_ops/mixins/sub_ops.rb +35 -0
- data/lib/rails_ops/model_casting.rb +17 -0
- data/lib/rails_ops/model_mixins.rb +12 -0
- data/lib/rails_ops/model_mixins/ar_extension.rb +20 -0
- data/lib/rails_ops/model_mixins/parent_op.rb +10 -0
- data/lib/rails_ops/model_mixins/protected_attributes.rb +78 -0
- data/lib/rails_ops/model_mixins/virtual_attributes.rb +24 -0
- data/lib/rails_ops/model_mixins/virtual_attributes/virtual_column_wrapper.rb +9 -0
- data/lib/rails_ops/model_mixins/virtual_has_one.rb +32 -0
- data/lib/rails_ops/operation.rb +215 -0
- data/lib/rails_ops/operation/model.rb +168 -0
- data/lib/rails_ops/operation/model/create.rb +35 -0
- data/lib/rails_ops/operation/model/destroy.rb +26 -0
- data/lib/rails_ops/operation/model/load.rb +72 -0
- data/lib/rails_ops/operation/model/update.rb +31 -0
- data/lib/rails_ops/patches/active_type_patch.rb +52 -0
- data/lib/rails_ops/profiler.rb +47 -0
- data/lib/rails_ops/profiler/node.rb +64 -0
- data/lib/rails_ops/railtie.rb +19 -0
- data/lib/rails_ops/scoped_env.rb +20 -0
- data/lib/rails_ops/virtual_model.rb +19 -0
- data/rails_ops.gemspec +58 -0
- data/test/test_helper.rb +3 -0
- metadata +252 -0
@@ -0,0 +1,168 @@
|
|
1
|
+
class RailsOps::Operation::Model < RailsOps::Operation
|
2
|
+
include RailsOps::Mixins::Model::Authorization
|
3
|
+
include RailsOps::Mixins::Model::Nesting
|
4
|
+
|
5
|
+
class_attribute :_model_class
|
6
|
+
|
7
|
+
# Allows to set or get a model class associated with this operation.
|
8
|
+
#
|
9
|
+
# To get the model class, call this method without any parameters. Note that
|
10
|
+
# this will result in an exception if the model class has not been previously
|
11
|
+
# set.
|
12
|
+
#
|
13
|
+
# To set the model class, there are 3 ways:
|
14
|
+
#
|
15
|
+
# - You only specify an existing class as `model_class`.
|
16
|
+
# - You only specify a block of code. This will result in a dynamically
|
17
|
+
# created class using the provided code block. The base class that is
|
18
|
+
# extended by this block is determined by the method
|
19
|
+
# {default_model_class}. Check this method to determine the used base class
|
20
|
+
# or overwrite it. This only makes sense for inheritance as there is the
|
21
|
+
# parameter `model_class` (see next point).
|
22
|
+
# - You specify a `model_class` as well as a block of code. In this case, the
|
23
|
+
# supplied class will be dynamically extended with the code block provided.
|
24
|
+
#
|
25
|
+
# This is a DSL method. Do not call it at runtime (i.e. after bootup) as it
|
26
|
+
# is not thread-safe and may generate anonymous classes.
|
27
|
+
#
|
28
|
+
# @param model_class [Class] The model class to take or extend
|
29
|
+
# @param name [String,Symbol] A custom name that will be used for generating
|
30
|
+
# the anonymous class
|
31
|
+
# @yield [] This optional block is executed in context of the newly generated,
|
32
|
+
# anonymous class and allows you to extend it
|
33
|
+
def self.model(model_class = nil, name = nil, &block)
|
34
|
+
if model_class || block_given?
|
35
|
+
fail 'Model class can only be set once.' if _model_class
|
36
|
+
|
37
|
+
model_class ||= default_model_class
|
38
|
+
|
39
|
+
# ---------------------------------------------------------------
|
40
|
+
# If we're given a block or we're configured to always extend the
|
41
|
+
# given model class, create a new model class inheriting from the
|
42
|
+
# given one and mix in required modules.
|
43
|
+
# ---------------------------------------------------------------
|
44
|
+
if block_given? || always_extend_model_class?
|
45
|
+
self._model_class = Class.new(model_class)
|
46
|
+
|
47
|
+
# Include operation mixins that provide various functionality needed for
|
48
|
+
# operation-specific models. We can safely include this even if it has
|
49
|
+
# already been included, as module includes only happen once.
|
50
|
+
_model_class.send(:include, RailsOps::ModelMixins)
|
51
|
+
|
52
|
+
# Apply the given block to the newly created class.
|
53
|
+
_model_class.class_eval(&block) if block_given?
|
54
|
+
|
55
|
+
# Set virtual model name if given.
|
56
|
+
if name && _model_class.respond_to?(:virtual_model_name=)
|
57
|
+
_model_class.virtual_model_name = ActiveModel::Name.new(_model_class, nil, name.to_s)
|
58
|
+
end
|
59
|
+
|
60
|
+
# ---------------------------------------------------------------
|
61
|
+
# We just use the given model class without any adaptions
|
62
|
+
# ---------------------------------------------------------------
|
63
|
+
else
|
64
|
+
self._model_class = model_class
|
65
|
+
end
|
66
|
+
elsif _model_class.nil?
|
67
|
+
fail 'No model class has been set.'
|
68
|
+
end
|
69
|
+
|
70
|
+
return _model_class
|
71
|
+
end
|
72
|
+
|
73
|
+
# This method determines whether the given model class (specified using the
|
74
|
+
# static `model` method) is always extended with an anonymous class. This can
|
75
|
+
# be required if the operation modifies the model class.
|
76
|
+
def self.always_extend_model_class?
|
77
|
+
false
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns the class used for extension when defining a model using the {model}
|
81
|
+
# method.
|
82
|
+
def self.default_model_class
|
83
|
+
ActiveType::Object
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns an instance of the operation model class. The instance is obtained
|
87
|
+
# using {build_model} and cached for the lifespan of the operation instance.
|
88
|
+
def model
|
89
|
+
unless @model
|
90
|
+
build_model
|
91
|
+
end
|
92
|
+
|
93
|
+
return @model
|
94
|
+
end
|
95
|
+
|
96
|
+
# Assigns attributes to a model. If no arguments are given, it extracts the
|
97
|
+
# attributes from the {params} hash and assigns them to the {model}.
|
98
|
+
#
|
99
|
+
# @param attributes [Hash] The attributes hash to assign. If not given,
|
100
|
+
# attributes will be obtained from the {params} hash using
|
101
|
+
# {extract_attributes_from_params}.
|
102
|
+
# @param without_protection [Boolean] If `true`, attributes wil be assigned without
|
103
|
+
# mass assignment protection.
|
104
|
+
# @param model [ActiveRecord::Base] Allows to manually specify a model the attributes
|
105
|
+
# will be assigned to. If not given, the model will be obtained from the
|
106
|
+
# {model} method.
|
107
|
+
# @param without_nested_models [Boolean] Per default, attribute params that
|
108
|
+
# are meant for nested models (registered via `nested_model_op`) will not
|
109
|
+
# be assigned. You can turn this filtering off by passing `false`.
|
110
|
+
def assign_attributes(attributes = nil, model: nil, without_protection: false, without_nested_models: true)
|
111
|
+
model ||= self.model
|
112
|
+
|
113
|
+
unless attributes
|
114
|
+
# Extract attributes from params hash
|
115
|
+
attributes = extract_attributes_from_params(model)
|
116
|
+
end
|
117
|
+
|
118
|
+
if without_nested_models
|
119
|
+
# Remove parameters that will be passed to nested model operations
|
120
|
+
# such as `post_attributes: {}`.
|
121
|
+
attributes = attributes.except(*self.class.nested_model_param_keys)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Do nothing if there are no attributes to assign
|
125
|
+
return if attributes.nil? || attributes.empty?
|
126
|
+
|
127
|
+
# Assign attributes, either with or without protection
|
128
|
+
if !without_protection && model.respond_to?(:assign_attributes_with_protection)
|
129
|
+
model.assign_attributes_with_protection(attributes)
|
130
|
+
else
|
131
|
+
model.assign_attributes(attributes)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Extracts model attributes from the {params} hash using
|
136
|
+
# `model.model_name.param_key`. If no model is given, the model will be
|
137
|
+
# obtained from the {model} method.
|
138
|
+
# @param model [ActiveRecord::Base] An optional model for determining the
|
139
|
+
# param key name
|
140
|
+
def extract_attributes_from_params(model = nil)
|
141
|
+
model ||= self.model
|
142
|
+
return params[model.model_name.param_key] || {}
|
143
|
+
end
|
144
|
+
|
145
|
+
protected
|
146
|
+
|
147
|
+
# Performs nested model operations and then saves the model. If you have
|
148
|
+
# nested model operations, you should call this method instead of calling
|
149
|
+
# `model.save!` directly.
|
150
|
+
def save!
|
151
|
+
perform_nested_model_ops!
|
152
|
+
model.save!
|
153
|
+
end
|
154
|
+
|
155
|
+
# Override of the base classes method to provide the model under key `model`
|
156
|
+
# to operations called via the `after_run` hook. You can still override this
|
157
|
+
# to suit your own operation's needs.
|
158
|
+
def after_run_trigger_params
|
159
|
+
{ model: model }
|
160
|
+
end
|
161
|
+
|
162
|
+
# This method must be implemented on superclasses in order to return an
|
163
|
+
# instance of the operation model class. This can be a new or existing record,
|
164
|
+
# depending on the respective operation's needs.
|
165
|
+
def build_model
|
166
|
+
fail NotImplementedError, 'Method `build_model` must be implemented.'
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class RailsOps::Operation::Model::Create < RailsOps::Operation::Model
|
2
|
+
model_authorization_action :create
|
3
|
+
|
4
|
+
policy :on_init do
|
5
|
+
model_authorization
|
6
|
+
end
|
7
|
+
|
8
|
+
# As this operation might extend the model class, we need to make sure that
|
9
|
+
# the operation works using an extended 'copy' of the given model class.
|
10
|
+
def self.always_extend_model_class?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
def model_authorization
|
15
|
+
return unless authorization_enabled?
|
16
|
+
|
17
|
+
unless model_authorization_action.nil?
|
18
|
+
authorize_model! model_authorization_action, model
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def build_model
|
23
|
+
fail 'Model can only be built once.' if @model
|
24
|
+
@model = self.class.model.new
|
25
|
+
if @model.respond_to?(:parent_op=)
|
26
|
+
@model.parent_op = self
|
27
|
+
end
|
28
|
+
build_nested_model_ops :create
|
29
|
+
assign_attributes
|
30
|
+
end
|
31
|
+
|
32
|
+
def perform
|
33
|
+
save!
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class RailsOps::Operation::Model::Destroy < RailsOps::Operation::Model::Load
|
2
|
+
model_authorization_action :destroy
|
3
|
+
|
4
|
+
def model_authorization
|
5
|
+
return unless authorization_enabled?
|
6
|
+
|
7
|
+
unless load_model_authorization_action.nil?
|
8
|
+
authorize_model_with_authorize_only! load_model_authorization_action, model
|
9
|
+
end
|
10
|
+
|
11
|
+
unless model_authorization_action.nil?
|
12
|
+
authorize_model! model_authorization_action, model
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
policy do
|
17
|
+
if model.respond_to?(:deleteable?) && !model.deleteable?
|
18
|
+
fail RailsOps::Exceptions::ModelNotDeleteable
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def perform
|
23
|
+
trigger :before_destroy, model: model
|
24
|
+
model.destroy!
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
class RailsOps::Operation::Model::Load < RailsOps::Operation::Model
|
2
|
+
class_attribute :_lock_model_at_build
|
3
|
+
class_attribute :_load_model_authorization_action
|
4
|
+
|
5
|
+
policy :on_init do
|
6
|
+
model_authorization
|
7
|
+
end
|
8
|
+
|
9
|
+
# Gets or sets the action verb used for authorizing models on load.
|
10
|
+
def self.load_model_authorization_action(*action)
|
11
|
+
if action.size == 1
|
12
|
+
self._load_model_authorization_action = action.first
|
13
|
+
elsif action.size > 1
|
14
|
+
fail ArgumentError, 'Too many arguments'
|
15
|
+
end
|
16
|
+
|
17
|
+
return _load_model_authorization_action
|
18
|
+
end
|
19
|
+
|
20
|
+
def model_authorization
|
21
|
+
return unless authorization_enabled?
|
22
|
+
|
23
|
+
unless load_model_authorization_action.nil?
|
24
|
+
authorize_model! load_model_authorization_action, model
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def load_model_authorization_action
|
29
|
+
self.class.load_model_authorization_action
|
30
|
+
end
|
31
|
+
|
32
|
+
load_model_authorization_action :read
|
33
|
+
|
34
|
+
def self.lock_model_at_build(enabled = true)
|
35
|
+
self._lock_model_at_build = enabled
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.lock_model_at_build?
|
39
|
+
_lock_model_at_build.nil? ? RailsOps.config.lock_models_at_build? : _lock_model_at_build
|
40
|
+
end
|
41
|
+
|
42
|
+
def model_id_field
|
43
|
+
:id
|
44
|
+
end
|
45
|
+
|
46
|
+
def find_model
|
47
|
+
unless params[model_id_field]
|
48
|
+
fail "Param #{model_id_field.inspect} must be given."
|
49
|
+
end
|
50
|
+
|
51
|
+
# Get model class
|
52
|
+
relation = self.class.model
|
53
|
+
|
54
|
+
# Express intention to lock if required
|
55
|
+
relation = relation.lock if self.class.lock_model_at_build?
|
56
|
+
|
57
|
+
# Fetch (and possibly lock) model
|
58
|
+
return relation.find_by!(model_id_field => params[model_id_field])
|
59
|
+
end
|
60
|
+
|
61
|
+
def build_model
|
62
|
+
@model = find_model
|
63
|
+
|
64
|
+
if @model.respond_to?(:parent_op=)
|
65
|
+
@model.parent_op = self
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def extract_id_from_params
|
70
|
+
params[model_id_field]
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class RailsOps::Operation::Model::Update < RailsOps::Operation::Model::Load
|
2
|
+
model_authorization_action :update
|
3
|
+
|
4
|
+
# As this operation might extend the model class, we need to make sure that
|
5
|
+
# the operation works using an extended 'copy' of the given model class.
|
6
|
+
def self.always_extend_model_class?
|
7
|
+
true
|
8
|
+
end
|
9
|
+
|
10
|
+
def model_authorization
|
11
|
+
return unless authorization_enabled?
|
12
|
+
|
13
|
+
unless load_model_authorization_action.nil?
|
14
|
+
authorize_model_with_authorize_only! load_model_authorization_action, model
|
15
|
+
end
|
16
|
+
|
17
|
+
unless model_authorization_action.nil?
|
18
|
+
authorize_model! model_authorization_action, model
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def build_model
|
23
|
+
super
|
24
|
+
build_nested_model_ops :update
|
25
|
+
assign_attributes
|
26
|
+
end
|
27
|
+
|
28
|
+
def perform
|
29
|
+
save!
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Internal reference: #25564
|
2
|
+
module ActiveType
|
3
|
+
class TypeCaster
|
4
|
+
def type_cast_from_user(value)
|
5
|
+
# For some reason, Rails defines additional type casting logic
|
6
|
+
# outside the classes that have that responsibility.
|
7
|
+
case @type
|
8
|
+
when :integer
|
9
|
+
if value == ''
|
10
|
+
nil
|
11
|
+
else
|
12
|
+
native_type_cast_from_user(value)
|
13
|
+
end
|
14
|
+
when :timestamp, :datetime
|
15
|
+
time = native_type_cast_from_user(value)
|
16
|
+
if time && ActiveRecord::Base.time_zone_aware_attributes
|
17
|
+
time = ActiveSupport::TimeWithZone.new(nil, Time.zone, time)
|
18
|
+
end
|
19
|
+
time
|
20
|
+
when Integer.class
|
21
|
+
if value == ''
|
22
|
+
nil
|
23
|
+
else
|
24
|
+
value.to_i
|
25
|
+
end
|
26
|
+
else
|
27
|
+
native_type_cast_from_user(value)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module NativeCasters
|
32
|
+
class DelegateToType
|
33
|
+
def initialize(type, connection)
|
34
|
+
# The specified type (e.g. "string") may not necessary match the
|
35
|
+
# native type ("varchar") expected by the connection adapter.
|
36
|
+
# PostgreSQL is one of these. Perform a translation if the adapter
|
37
|
+
# supports it (but don't turn a mysql boolean into a tinyint).
|
38
|
+
if !type.nil? && !(type == :boolean) && type.respond_to?(:to_sym) && connection.respond_to?(:native_database_types)
|
39
|
+
native_type = connection.native_database_types[type.try(:to_sym)]
|
40
|
+
if native_type && native_type[:name]
|
41
|
+
type = native_type[:name]
|
42
|
+
else
|
43
|
+
# unknown type, we just dont cast
|
44
|
+
type = nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
@active_record_type = connection.lookup_cast_type(type)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module RailsOps
|
2
|
+
class Profiler
|
3
|
+
def self.profile(object_id, description = nil, &_block)
|
4
|
+
node = tstore_nodes[object_id] = ::RailsOps::Profiler::Node.new(object_id, description, tstore_current_parent)
|
5
|
+
self.tstore_current_parent = node
|
6
|
+
res = yield
|
7
|
+
self.tstore_current_parent = node.parent
|
8
|
+
node.finish_measure
|
9
|
+
res
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.time(descr = nil, &_block)
|
13
|
+
descr += ' - ' if descr
|
14
|
+
start = Time.now
|
15
|
+
res = yield
|
16
|
+
puts "#{descr}#{((Time.now - start).to_f * 1000).round(1)}ms elapsed.".magenta
|
17
|
+
res
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.forget_all
|
21
|
+
Thread.current[:rails_ops_profiler][:nodes] = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.forget(object_id)
|
25
|
+
tstore_nodes.delete(object_id)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.node(object_id)
|
29
|
+
tstore_nodes[object_id] || fail("Unkown object_id #{object_id}.")
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.tstore_current_parent
|
33
|
+
Thread.current[:rails_ops_profiler] ||= {}
|
34
|
+
Thread.current[:rails_ops_profiler][:current_parent]
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.tstore_current_parent=(parent)
|
38
|
+
Thread.current[:rails_ops_profiler] ||= {}
|
39
|
+
Thread.current[:rails_ops_profiler][:current_parent] = parent
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.tstore_nodes
|
43
|
+
Thread.current[:rails_ops_profiler] ||= {}
|
44
|
+
Thread.current[:rails_ops_profiler][:nodes] ||= {}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module RailsOps
|
2
|
+
class Profiler::Node
|
3
|
+
def initialize(object_id, description = nil, parent = nil)
|
4
|
+
@object_id = object_id
|
5
|
+
@description = description
|
6
|
+
@parent = parent
|
7
|
+
parent&.add_child(self)
|
8
|
+
@children = []
|
9
|
+
@t_start = Time.now
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :parent
|
13
|
+
attr_reader :description
|
14
|
+
|
15
|
+
def finish_measure
|
16
|
+
@t_stop = Time.now
|
17
|
+
end
|
18
|
+
|
19
|
+
def t_self
|
20
|
+
t_total - t_kids
|
21
|
+
end
|
22
|
+
|
23
|
+
def t_kids
|
24
|
+
@children.map(&:t_total).inject(:+) || 0
|
25
|
+
end
|
26
|
+
|
27
|
+
def t_total
|
28
|
+
fail "Measure for object_id #{@object_id} (#{@description}) is not finished." unless @t_stop
|
29
|
+
(@t_stop - @t_start).to_f
|
30
|
+
end
|
31
|
+
|
32
|
+
def t_self_s
|
33
|
+
t_self
|
34
|
+
end
|
35
|
+
|
36
|
+
def t_kids_s
|
37
|
+
t_kids
|
38
|
+
end
|
39
|
+
|
40
|
+
def t_total_s
|
41
|
+
t_total
|
42
|
+
end
|
43
|
+
|
44
|
+
def t_self_ms
|
45
|
+
t_self * 1000
|
46
|
+
end
|
47
|
+
|
48
|
+
def t_kids_ms
|
49
|
+
t_kids * 1000
|
50
|
+
end
|
51
|
+
|
52
|
+
def t_total_ms
|
53
|
+
t_total * 1000
|
54
|
+
end
|
55
|
+
|
56
|
+
def free
|
57
|
+
::RailsOps::Profiler.forget(@object_id) unless parent
|
58
|
+
end
|
59
|
+
|
60
|
+
def add_child(child)
|
61
|
+
@children << child
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|