ronin 1.0.0 → 1.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (169) hide show
  1. data/.yardopts +0 -1
  2. data/ChangeLog.md +67 -3
  3. data/Gemfile +20 -12
  4. data/README.md +3 -5
  5. data/gemspec.yml +5 -5
  6. data/lib/ronin/address.rb +8 -3
  7. data/lib/ronin/arch.rb +39 -14
  8. data/lib/ronin/author.rb +2 -0
  9. data/lib/ronin/auto_load.rb +67 -0
  10. data/lib/ronin/campaign.rb +15 -2
  11. data/lib/ronin/class_methods.rb +7 -1
  12. data/lib/ronin/config.rb +5 -0
  13. data/lib/ronin/credential.rb +14 -3
  14. data/lib/ronin/database/database.rb +38 -12
  15. data/lib/ronin/database/migrations.rb +3 -1
  16. data/lib/ronin/database/migrations/add_created_at_column_to_targets_table.rb +48 -0
  17. data/lib/ronin/database/migrations/add_updated_at_column_to_campaigns_table.rb +47 -0
  18. data/lib/ronin/database/migrations/create_licenses_table.rb +1 -1
  19. data/lib/ronin/database/migrations/create_passwords_table.rb +1 -1
  20. data/lib/ronin/database/migrations/{create_cached_files_table.rb → create_script_paths_table.rb} +4 -4
  21. data/lib/ronin/database/migrations/migration.rb +3 -6
  22. data/lib/ronin/database/migrations/migrations.rb +3 -3
  23. data/lib/ronin/email_address.rb +20 -3
  24. data/lib/ronin/environment.rb +0 -3
  25. data/lib/ronin/host_name.rb +20 -6
  26. data/lib/ronin/host_name_ip_address.rb +2 -3
  27. data/lib/ronin/installation.rb +92 -23
  28. data/lib/ronin/ip_address.rb +31 -8
  29. data/lib/ronin/ip_address_mac_address.rb +2 -3
  30. data/lib/ronin/license.rb +1 -1
  31. data/lib/ronin/mac_address.rb +6 -4
  32. data/lib/ronin/model/class_methods.rb +4 -0
  33. data/lib/ronin/model/has_authors/class_methods.rb +4 -0
  34. data/lib/ronin/model/has_authors/has_authors.rb +4 -0
  35. data/lib/ronin/model/has_description/class_methods.rb +2 -0
  36. data/lib/ronin/model/has_description/has_description.rb +2 -0
  37. data/lib/ronin/model/has_license/class_methods.rb +2 -0
  38. data/lib/ronin/model/has_license/has_license.rb +4 -0
  39. data/lib/ronin/model/has_name/class_methods.rb +2 -0
  40. data/lib/ronin/model/has_name/has_name.rb +4 -0
  41. data/lib/ronin/model/has_title/class_methods.rb +2 -0
  42. data/lib/ronin/model/has_title/has_title.rb +2 -0
  43. data/lib/ronin/model/has_unique_name/class_methods.rb +2 -0
  44. data/lib/ronin/model/has_unique_name/has_unique_name.rb +7 -1
  45. data/lib/ronin/model/has_version/class_methods.rb +4 -0
  46. data/lib/ronin/model/has_version/has_version.rb +2 -0
  47. data/lib/ronin/model/model.rb +2 -0
  48. data/lib/ronin/model/types.rb +1 -7
  49. data/lib/ronin/model/types/description.rb +2 -0
  50. data/lib/ronin/network/mixins/esmtp.rb +40 -25
  51. data/lib/ronin/network/mixins/http.rb +336 -73
  52. data/lib/ronin/network/mixins/imap.rb +38 -25
  53. data/lib/ronin/network/mixins/pop3.rb +34 -21
  54. data/lib/ronin/network/mixins/smtp.rb +40 -25
  55. data/lib/ronin/network/mixins/tcp.rb +66 -41
  56. data/lib/ronin/network/mixins/telnet.rb +43 -30
  57. data/lib/ronin/network/mixins/udp.rb +59 -40
  58. data/lib/ronin/open_port.rb +12 -5
  59. data/lib/ronin/organization.rb +1 -2
  60. data/lib/ronin/os.rb +10 -3
  61. data/lib/ronin/os_guess.rb +2 -3
  62. data/lib/ronin/password.rb +11 -3
  63. data/lib/ronin/port.rb +7 -2
  64. data/lib/ronin/{engine/exceptions/deploy_failed.rb → repositories.rb} +4 -6
  65. data/lib/ronin/repository.rb +110 -37
  66. data/lib/ronin/ronin.rb +4 -3
  67. data/lib/ronin/{engine.rb → script.rb} +2 -1
  68. data/lib/ronin/{engine → script}/buildable.rb +43 -29
  69. data/lib/ronin/script/class_methods.rb +84 -0
  70. data/lib/ronin/{engine → script}/deployable.rb +61 -40
  71. data/lib/ronin/{engine → script}/exceptions.rb +4 -3
  72. data/lib/ronin/script/exceptions/deploy_failed.rb +27 -0
  73. data/lib/ronin/{engine/exceptions/not_built.rb → script/exceptions/exception.rb} +2 -2
  74. data/lib/ronin/{engine/exceptions/verification_failed.rb → script/exceptions/not_built.rb} +4 -2
  75. data/lib/ronin/script/exceptions/test_failed.rb +27 -0
  76. data/lib/ronin/script/instance_methods.rb +217 -0
  77. data/lib/ronin/script/path.rb +277 -0
  78. data/lib/ronin/{engine/engine.rb → script/script.rb} +52 -15
  79. data/lib/ronin/{engine/verifiable.rb → script/testable.rb} +97 -68
  80. data/lib/ronin/service.rb +1 -2
  81. data/lib/ronin/service_credential.rb +1 -2
  82. data/lib/ronin/software.rb +3 -2
  83. data/lib/ronin/spec/database.rb +0 -4
  84. data/lib/ronin/target.rb +11 -3
  85. data/lib/ronin/tcp_port.rb +3 -2
  86. data/lib/ronin/udp_port.rb +2 -0
  87. data/lib/ronin/ui/cli/cli.rb +6 -0
  88. data/lib/ronin/ui/cli/command.rb +48 -12
  89. data/lib/ronin/ui/cli/commands/campaigns.rb +3 -3
  90. data/lib/ronin/ui/cli/commands/console.rb +2 -1
  91. data/lib/ronin/ui/cli/commands/creds.rb +3 -3
  92. data/lib/ronin/ui/cli/commands/database.rb +1 -17
  93. data/lib/ronin/ui/cli/commands/emails.rb +3 -3
  94. data/lib/ronin/ui/cli/commands/hosts.rb +3 -7
  95. data/lib/ronin/ui/cli/commands/ips.rb +3 -7
  96. data/lib/ronin/ui/cli/commands/repos.rb +5 -17
  97. data/lib/ronin/ui/cli/commands/urls.rb +3 -3
  98. data/lib/ronin/ui/cli/model_command.rb +82 -97
  99. data/lib/ronin/ui/cli/resources_command.rb +89 -0
  100. data/lib/ronin/ui/cli/script_command.rb +115 -0
  101. data/lib/ronin/ui/console.rb +17 -3
  102. data/lib/ronin/ui/output/helpers.rb +18 -0
  103. data/lib/ronin/ui/output/output.rb +20 -1
  104. data/lib/ronin/ui/output/terminal/color.rb +10 -0
  105. data/lib/ronin/ui/output/terminal/raw.rb +10 -0
  106. data/lib/ronin/ui/shell.rb +2 -0
  107. data/lib/ronin/url.rb +47 -12
  108. data/lib/ronin/url_query_param.rb +4 -0
  109. data/lib/ronin/url_scheme.rb +3 -3
  110. data/lib/ronin/user_name.rb +2 -0
  111. data/lib/ronin/version.rb +1 -1
  112. data/lib/ronin/web_credential.rb +6 -3
  113. data/spec/arch_spec.rb +3 -3
  114. data/spec/classes/my_script.rb +21 -0
  115. data/spec/helpers/repos/{test1 → installed}/ronin.yml +1 -1
  116. data/spec/helpers/repos/{hello/cache → installed/scripts}/.keep +0 -0
  117. data/spec/helpers/repos/local/lib/init.rb +1 -0
  118. data/spec/helpers/repos/{hello → local}/lib/stuff/another_test.rb +0 -0
  119. data/spec/helpers/repos/{hello → local}/lib/stuff/test.rb +0 -0
  120. data/spec/helpers/repos/{random → local}/ronin.yml +1 -1
  121. data/spec/helpers/repos/{random/cache → local/scripts}/.keep +0 -0
  122. data/spec/helpers/repos/{hello → remote}/ronin.yml +1 -1
  123. data/spec/helpers/repos/remote/scripts/.keep +0 -0
  124. data/spec/helpers/repos/{test2 → scripts}/ronin.yml +0 -0
  125. data/spec/helpers/repos/scripts/scripts/cached/cached.rb +10 -0
  126. data/spec/helpers/repos/scripts/scripts/cached/missing.rb +10 -0
  127. data/spec/helpers/repos/scripts/scripts/cached/modified.rb +10 -0
  128. data/spec/helpers/repos/scripts/scripts/cached/unmodified.rb +10 -0
  129. data/spec/helpers/repos/scripts/scripts/failures/exceptions.rb +11 -0
  130. data/spec/helpers/repos/{test2/cache/cacheable_model → scripts/scripts/failures}/load_errors.rb +3 -1
  131. data/spec/helpers/repos/{test2/cache/cacheable_model → scripts/scripts/failures}/name_errors.rb +1 -0
  132. data/spec/helpers/repos/scripts/scripts/failures/no_method_errors.rb +10 -0
  133. data/spec/helpers/repos/scripts/scripts/failures/syntax_errors.rb +11 -0
  134. data/spec/helpers/repos/scripts/scripts/failures/validation_errors.rb +11 -0
  135. data/spec/helpers/repos/scripts/scripts/my_scripts/test.rb +16 -0
  136. data/spec/model/spec_helper.rb +0 -2
  137. data/spec/repository_spec.rb +61 -81
  138. data/spec/ronin_spec.rb +2 -2
  139. data/spec/{engine → script}/buildable_spec.rb +9 -9
  140. data/spec/script/classes/buildable_class.rb +15 -0
  141. data/spec/script/classes/deployable_class.rb +13 -0
  142. data/spec/{engine/classes/engine_class.rb → script/classes/script_class.rb} +3 -3
  143. data/spec/{engine/classes/verifiable_class.rb → script/classes/testable_class.rb} +5 -5
  144. data/spec/{engine → script}/deployable_spec.rb +10 -10
  145. data/spec/{cached_file_spec.rb → script/path_spec.rb} +33 -72
  146. data/spec/script/script_spec.rb +130 -0
  147. data/spec/script/testable_spec.rb +117 -0
  148. data/spec/spec_helper.rb +15 -13
  149. metadata +114 -139
  150. data/lib/ronin/cached_file.rb +0 -247
  151. data/lib/ronin/engine/class_methods.rb +0 -135
  152. data/lib/ronin/engine/instance_methods.rb +0 -97
  153. data/lib/ronin/model/cacheable.rb +0 -21
  154. data/lib/ronin/model/cacheable/cacheable.rb +0 -273
  155. data/lib/ronin/model/cacheable/class_methods.rb +0 -60
  156. data/lib/ronin/ui/cli/engine_command.rb +0 -106
  157. data/spec/engine/classes/buildable_class.rb +0 -15
  158. data/spec/engine/classes/deployable_class.rb +0 -13
  159. data/spec/engine/engine_spec.rb +0 -55
  160. data/spec/engine/verifiable_spec.rb +0 -117
  161. data/spec/helpers/repos/hello/lib/init.rb +0 -1
  162. data/spec/helpers/repos/test1/cache/cacheable_model/one.rb +0 -15
  163. data/spec/helpers/repos/test2/cache/cacheable_model/exceptions.rb +0 -7
  164. data/spec/helpers/repos/test2/cache/cacheable_model/no_method_errors.rb +0 -9
  165. data/spec/helpers/repos/test2/cache/cacheable_model/syntax_errors.rb +0 -7
  166. data/spec/helpers/repos/test2/cache/cacheable_model/two.rb +0 -15
  167. data/spec/helpers/repos/test2/cache/cacheable_model/validation_errors.rb +0 -9
  168. data/spec/model/cacheable_spec.rb +0 -96
  169. data/spec/model/models/cacheable_model.rb +0 -13
@@ -1,247 +0,0 @@
1
- #
2
- # Copyright (c) 2006-2011 Hal Brodigan (postmodern.mod3 at gmail.com)
3
- #
4
- # This file is part of Ronin.
5
- #
6
- # Ronin is free software: you can redistribute it and/or modify
7
- # it under the terms of the GNU General Public License as published by
8
- # the Free Software Foundation, either version 3 of the License, or
9
- # (at your option) any later version.
10
- #
11
- # Ronin is distributed in the hope that it will be useful,
12
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
- # GNU General Public License for more details.
15
- #
16
- # You should have received a copy of the GNU General Public License
17
- # along with Ronin. If not, see <http://www.gnu.org/licenses/>.
18
- #
19
-
20
- require 'ronin/support/inflector'
21
- require 'ronin/model'
22
-
23
- require 'object_loader'
24
-
25
- module Ronin
26
- autoload :Repository, 'ronin/repository'
27
-
28
- #
29
- # The {CachedFile} model stores information in the {Database} about
30
- # files that {Cacheable} Models were cached from. {CachedFile} also
31
- # manages the {Cacheable} objects that were saved in the {Database}.
32
- #
33
- class CachedFile
34
-
35
- include Model
36
-
37
- # The primary key of the cached file
38
- property :id, Serial
39
-
40
- # The path to the file where the object was defined in
41
- property :path, FilePath, :required => true
42
-
43
- # The timestamp of the cached file
44
- property :timestamp, Time, :required => true
45
-
46
- # The class name of the cached object
47
- property :model_name, String, :required => true
48
-
49
- # The repository the file was cached from
50
- belongs_to :repository
51
-
52
- # Any exceptions raise when loading a fresh object
53
- attr_reader :cache_exception
54
-
55
- # Any cache errors encountered when caching the object
56
- attr_reader :cache_errors
57
-
58
- #
59
- # The path to require to access the Class of the cached object.
60
- #
61
- # @return [String]
62
- # The possible path inferred from the class name.
63
- #
64
- def model_path
65
- if self.model_name
66
- Support::Inflector.underscore(self.model_name)
67
- end
68
- end
69
-
70
- #
71
- # The Model of the cached object.
72
- #
73
- # @return [Class, nil]
74
- # Returns the Model of the cached object, or `nil` if the class
75
- # could not be loaded or found.
76
- #
77
- def cached_model
78
- return unless self.model_name
79
-
80
- # filter out unloadable models
81
- begin
82
- require model_path
83
- rescue Gem::LoadError => e
84
- raise(e)
85
- rescue ::LoadError
86
- end
87
-
88
- # filter out missing class names
89
- model = begin
90
- DataMapper::Ext::Object.full_const_get(self.model_name)
91
- rescue NameError
92
- return nil
93
- end
94
-
95
- # filter out non-Cacheable models
96
- return model if model < Cacheable
97
- end
98
-
99
- #
100
- # The object from the Database that was cached from the file.
101
- #
102
- # @return [Model]
103
- # The previously cached object connected to the cache file.
104
- #
105
- def cached_object
106
- if (model = self.cached_model)
107
- return model.first(:cached_file => self)
108
- end
109
- end
110
-
111
- #
112
- # A freshly loaded Cacheable object from the cache file.
113
- #
114
- # @return [Cacheable, nil]
115
- # The first Cacheable object loaded from the cache file.
116
- # If `nil` is returned, the file did not contain any cacheable
117
- # objects or the cache file contained a syntax error.
118
- #
119
- def fresh_object
120
- begin
121
- # load the first found context
122
- blocks = ObjectLoader.load_blocks(self.path)
123
- rescue Exception => e
124
- @cache_exception = e
125
- return nil
126
- end
127
-
128
- blocks.each do |model,block|
129
- if model < Cacheable
130
- # create the fresh object
131
- object = model.new()
132
-
133
- begin
134
- object.instance_eval(&block)
135
-
136
- @cache_exception = nil
137
- return object
138
- rescue Exception => e
139
- @cache_exception = e
140
- end
141
- end
142
- end
143
-
144
- return nil
145
- end
146
-
147
- #
148
- # Determines if the cache file was updated.
149
- #
150
- # @return [Boolean]
151
- # Specifies whether the cache file was updated.
152
- #
153
- def updated?
154
- # assume updates if there is no timestamp
155
- return true unless self.timestamp
156
-
157
- if File.file?(self.path)
158
- return self.path.mtime > self.timestamp
159
- end
160
-
161
- # do not assume updates, if there is no path
162
- return false
163
- end
164
-
165
- #
166
- # Determines if the cache file was deleted.
167
- #
168
- # @return [Boolean]
169
- # Specifies whether the cache file was deleted.
170
- #
171
- def missing?
172
- !(self.path.file?)
173
- end
174
-
175
- #
176
- # Caches the freshly loaded object from the cache file into the
177
- # Database.
178
- #
179
- # @return [Boolean]
180
- # Specifies whether the object was successfully cached.
181
- #
182
- def cache
183
- if (obj = fresh_object)
184
- # reset the model-class
185
- self.model_name = obj.class.to_s
186
-
187
- # update the timestamp
188
- self.timestamp = self.path.mtime
189
-
190
- # re-cache the fresh_object
191
- obj.cached_file = self
192
-
193
- if obj.save
194
- @cache_errors = nil
195
- return true
196
- else
197
- @cache_errors = obj.errors
198
- end
199
- end
200
-
201
- return false
202
- end
203
-
204
- #
205
- # Syncs the cached object in the Database with the object loaded from
206
- # the cache file.
207
- #
208
- # @return [Boolean]
209
- # Specifies whether the cached object was successfully synced.
210
- #
211
- def sync
212
- if missing?
213
- # destroy the cached file, if the actual file is missing
214
- return destroy
215
- elsif updated?
216
- if (obj = cached_object)
217
- # destroy the previously cached object
218
- obj.destroy!
219
- end
220
-
221
- unless cache
222
- # if we couldn't cache anything, self-destruct
223
- destroy
224
- end
225
-
226
- return true
227
- end
228
-
229
- return false
230
- end
231
-
232
- #
233
- # Before destroying the cached file object, also destroy the
234
- # associated cached object.
235
- #
236
- def destroy
237
- unless destroyed?
238
- if (obj = self.cached_object)
239
- obj.destroy!
240
- end
241
- end
242
-
243
- super
244
- end
245
-
246
- end
247
- end
@@ -1,135 +0,0 @@
1
- #
2
- # Copyright (c) 2006-2011 Hal Brodigan (postmodern.mod3 at gmail.com)
3
- #
4
- # This file is part of Ronin.
5
- #
6
- # Ronin is free software: you can redistribute it and/or modify
7
- # it under the terms of the GNU General Public License as published by
8
- # the Free Software Foundation, either version 3 of the License, or
9
- # (at your option) any later version.
10
- #
11
- # Ronin is distributed in the hope that it will be useful,
12
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
- # GNU General Public License for more details.
15
- #
16
- # You should have received a copy of the GNU General Public License
17
- # along with Ronin. If not, see <http://www.gnu.org/licenses/>.
18
- #
19
-
20
- module Ronin
21
- module Engine
22
- #
23
- # Class methods for an {Engine}.
24
- #
25
- module ClassMethods
26
- #
27
- # Finds and loads all matching Ronin Engines.
28
- #
29
- # @param [Hash] options
30
- # Query options.
31
- #
32
- # @option options [String] :name
33
- # The name to search for.
34
- #
35
- # @option options [String] :describing
36
- # The description to search for.
37
- #
38
- # @option options [String] :version
39
- # The version to search for.
40
- #
41
- # @option options [String] :license
42
- # The license to search for.
43
- #
44
- # @return [Array<Engine>]
45
- # The Ronin Engine with the matching attributes.
46
- #
47
- # @since 1.0.0
48
- #
49
- def load_all(options={})
50
- resources = custom_query(options)
51
-
52
- resources.each { |resource| resource.load_original! }
53
- return resources
54
- end
55
-
56
- #
57
- # Finds and loads a specific Ronin Engine.
58
- #
59
- # @param [Hash] options
60
- # Query options.
61
- #
62
- # @option options [String] :name
63
- # The name to search for.
64
- #
65
- # @option options [String] :describing
66
- # The description to search for.
67
- #
68
- # @option options [String] :version
69
- # The version to search for.
70
- #
71
- # @option options [String] :license
72
- # The license to search for.
73
- #
74
- # @return [Array<Engine>]
75
- # The Ronin Engine with the matching attributes.
76
- #
77
- # @since 1.0.0
78
- #
79
- def load_first(options={})
80
- if (resource = custom_query(options).first)
81
- resource.load_original!
82
- end
83
-
84
- return resource
85
- end
86
-
87
- protected
88
-
89
- #
90
- # Creates a custom query for the Ronin Engine.
91
- #
92
- # @param [Hash] options
93
- # Query options.
94
- #
95
- # @option options [String] :name
96
- # The name to search for.
97
- #
98
- # @option options [String] :describing
99
- # The description to search for.
100
- #
101
- # @option options [String] :version
102
- # The version to search for.
103
- #
104
- # @option options [String] :license
105
- # The license to search for.
106
- #
107
- # @return [DataMapper::Collection]
108
- # The custom query for the Ronin Engine.
109
- #
110
- # @since 1.0.0
111
- #
112
- def custom_query(options)
113
- query = all
114
-
115
- if options.has_key?(:name)
116
- query = query.named(options.delete(:name))
117
- end
118
-
119
- if options.has_key?(:describing)
120
- query = query.describing(options.delete(:describing))
121
- end
122
-
123
- if options.has_key?(:version)
124
- query = query.revision(options.delete(:version))
125
- end
126
-
127
- if options.has_key?(:license)
128
- query = query.licensed_under(options.delete(:license))
129
- end
130
-
131
- return query.all(options)
132
- end
133
- end
134
- end
135
- end
@@ -1,97 +0,0 @@
1
- #
2
- # Copyright (c) 2006-2011 Hal Brodigan (postmodern.mod3 at gmail.com)
3
- #
4
- # This file is part of Ronin.
5
- #
6
- # Ronin is free software: you can redistribute it and/or modify
7
- # it under the terms of the GNU General Public License as published by
8
- # the Free Software Foundation, either version 3 of the License, or
9
- # (at your option) any later version.
10
- #
11
- # Ronin is distributed in the hope that it will be useful,
12
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
- # GNU General Public License for more details.
15
- #
16
- # You should have received a copy of the GNU General Public License
17
- # along with Ronin. If not, see <http://www.gnu.org/licenses/>.
18
- #
19
-
20
- module Ronin
21
- module Engine
22
- #
23
- # Instance methods for an {Engine}.
24
- #
25
- module InstanceMethods
26
- #
27
- # Initializes the Ronin Engine.
28
- #
29
- # @param [Hash] attributes
30
- # The attributes or parameter values to initialize the engine with.
31
- #
32
- # @since 1.0.0
33
- #
34
- def initialize(attributes={})
35
- super(attributes)
36
-
37
- initialize_params(attributes)
38
- end
39
-
40
- #
41
- # The engine type.
42
- #
43
- # @return [String]
44
- # The name of the engine class.
45
- #
46
- # @since 1.0.0
47
- #
48
- def engine_name
49
- @ngine_name ||= self.class.base_model.name.split('::').last
50
- end
51
-
52
- #
53
- # Converts the engine to a String.
54
- #
55
- # @return [String]
56
- # The name and version of the engine.
57
- #
58
- # @since 1.0.0
59
- #
60
- def to_s
61
- if (self.name && self.version)
62
- "#{self.name} #{self.version}"
63
- elsif self.name
64
- super
65
- elsif self.version
66
- self.version.to_s
67
- end
68
- end
69
-
70
- #
71
- # Inspects both the properties and parameters of the Ronin Engine.
72
- #
73
- # @return [String]
74
- # The inspected Ronin Engine.
75
- #
76
- # @since 1.0.0
77
- #
78
- def inspect
79
- body = []
80
-
81
- self.attributes.each do |name,value|
82
- body << "#{name}: #{value.inspect}"
83
- end
84
-
85
- param_pairs = []
86
-
87
- self.params.each do |name,param|
88
- param_pairs << "#{name}: #{param.value.inspect}"
89
- end
90
-
91
- body << "params: {#{param_pairs.join(', ')}}"
92
-
93
- return "#<#{self.class}: #{body.join(', ')}>"
94
- end
95
- end
96
- end
97
- end