artrest 0.0.1

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.
Files changed (61) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +20 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +29 -0
  6. data/Rakefile +31 -0
  7. data/artrest.gemspec +30 -0
  8. data/bin/artrest +194 -0
  9. data/lib/artrest.rb +81 -0
  10. data/lib/artrest/build.rb +31 -0
  11. data/lib/artrest/buildnumber.rb +26 -0
  12. data/lib/artrest/builds.rb +46 -0
  13. data/lib/artrest/dir_entry.rb +115 -0
  14. data/lib/artrest/repositories.rb +61 -0
  15. data/lib/artrest/repository.rb +42 -0
  16. data/lib/artrest/resource.rb +366 -0
  17. data/lib/artrest/resources.rb +54 -0
  18. data/lib/artrest/system.rb +74 -0
  19. data/lib/artrest/system_general_configuration.rb +45 -0
  20. data/lib/artrest/version.rb +3 -0
  21. data/spec/artrest/build_spec.rb +62 -0
  22. data/spec/artrest/buildnumber_spec.rb +45 -0
  23. data/spec/artrest/builds_spec.rb +69 -0
  24. data/spec/artrest/folder_spec.rb +88 -0
  25. data/spec/artrest/repositories_spec.rb +47 -0
  26. data/spec/artrest/repository_spec.rb +63 -0
  27. data/spec/artrest/resource_spec.rb +385 -0
  28. data/spec/artrest/resources_spec.rb +66 -0
  29. data/spec/artrest/system_general_configuration_spec.rb +72 -0
  30. data/spec/artrest/system_spec.rb +50 -0
  31. data/spec/artrest_spec.rb +30 -0
  32. data/spec/fixtures/build/build_response_correct.txt +7 -0
  33. data/spec/fixtures/buildnumber/buildnumber_25_response.txt +7 -0
  34. data/spec/fixtures/builds/build_api_response_correct.txt +7 -0
  35. data/spec/fixtures/folder/pom_file_response_1.txt +7 -0
  36. data/spec/fixtures/folder/sub_folder_response.txt +7 -0
  37. data/spec/fixtures/folder/top_folder_response.txt +7 -0
  38. data/spec/fixtures/repositories/libs_snapshot_local_folder_response.txt +7 -0
  39. data/spec/fixtures/repositories/repositories_response.txt +7 -0
  40. data/spec/fixtures/repository/libs_snapshot_local_response.txt +7 -0
  41. data/spec/fixtures/resource/artifact_response.txt +7 -0
  42. data/spec/fixtures/resource/build_response.txt +7 -0
  43. data/spec/fixtures/resource/buildnumber_response.txt +7 -0
  44. data/spec/fixtures/resource/builds_response.txt +7 -0
  45. data/spec/fixtures/resource/folder_response.txt +7 -0
  46. data/spec/fixtures/resource/json_response.txt +7 -0
  47. data/spec/fixtures/resource/repositories_response.txt +7 -0
  48. data/spec/fixtures/resource/repository_not_found_response.txt +7 -0
  49. data/spec/fixtures/resource/repository_response.txt +7 -0
  50. data/spec/fixtures/resource/repository_unauthorized_response.txt +8 -0
  51. data/spec/fixtures/resource/string_response.txt +188 -0
  52. data/spec/fixtures/resource/sub_response.txt +188 -0
  53. data/spec/fixtures/resource/system_general_configuration_response.txt +654 -0
  54. data/spec/fixtures/resource/system_response.txt +188 -0
  55. data/spec/fixtures/resources/resources_response.txt +7 -0
  56. data/spec/fixtures/system/200_OK_ping_response.txt +7 -0
  57. data/spec/fixtures/system/general_configuration_response.txt +654 -0
  58. data/spec/fixtures/system/system_response.txt +188 -0
  59. data/spec/spec.opts +5 -0
  60. data/spec/spec_helper.rb +38 -0
  61. metadata +269 -0
@@ -0,0 +1,26 @@
1
+
2
+ module ArtRest
3
+
4
+ # Represents an {Artifactory}[http://www.jfrog.com/home/v_artifactory_opensource_overview]
5
+ # {Build Info}[http://wiki.jfrog.org/confluence/display/RTF/Artifactory's+REST+API#Artifactory'sRESTAPI-BuildInfo]
6
+ # resource.
7
+ #
8
+ # === Example
9
+ #
10
+ # buildInfo = ArtRest::Buildnumber.new('http://localhost:8081/artifactory/api/build/wicket/51', { ... })
11
+ #
12
+ class Buildnumber < ArtRest::Resource
13
+
14
+ class << self
15
+
16
+ def matches_path(path, options) # :nodoc:
17
+ path =~ %r|^/api/build/[a-zA-Z+-._]+/\d+$|
18
+ end
19
+ end
20
+
21
+ self.mime_type = MIME::Types['application/json']
22
+
23
+ self.resource_attributes :uri,
24
+ :buildInfo
25
+ end
26
+ end
@@ -0,0 +1,46 @@
1
+
2
+ module ArtRest
3
+
4
+ # Represents an {Artifactory}[http://www.jfrog.com/home/v_artifactory_opensource_overview]
5
+ # {All Builds}[http://wiki.jfrog.org/confluence/display/RTF/Artifactory's+REST+API#Artifactory'sRESTAPI-AllBuilds]
6
+ # resource.
7
+ #
8
+ # === Example
9
+ #
10
+ # allBuilds = ArtRest::Builds.new('http://localhost:8081/artifactory/api/build', { ... })
11
+ #
12
+ class Builds < ArtRest::Resources
13
+
14
+ class << self
15
+ public
16
+
17
+ # Shortcut method to retrieve *the* ArtRest::Builds
18
+ # instance.
19
+ #
20
+ # * *Args* :
21
+ # - +base_url+ -> Our Artifactory server's base URL
22
+ # - +options+ -> A Hash containing username and password
23
+ # * *Returns* :
24
+ # - *The* ArtRest::Builds instance representing the
25
+ # collection of all Artifactory builds
26
+ #
27
+ def get(base_url, options)
28
+ Builds.new("#{base_url}/api/build", options)
29
+ end
30
+
31
+ def matches_path(path, options) # :nodoc:
32
+ path =~ %r|^/api/build/?$|
33
+ end
34
+ end
35
+
36
+ self.mime_type = MIME::Types['application/json']
37
+
38
+ self.resources_creator = Proc.new do |content, options|
39
+ self_url = content['uri']
40
+ (content['builds'] || []).map { |build| ArtRest::Build.new("#{self_url}#{build['uri']}", options) }
41
+ end
42
+
43
+ self.resource_attributes :uri,
44
+ :builds
45
+ end
46
+ end
@@ -0,0 +1,115 @@
1
+
2
+ module ArtRest
3
+
4
+ module DirEntry
5
+
6
+ def self.create_node(resource_url, options, resource_descriptor, &block)
7
+ if resource_descriptor['folder'] or not resource_descriptor['children'].nil?
8
+ ArtRest::Folder.new(resource_url, options, &block)
9
+ else
10
+ ArtRest::Artifact.new(resource_url, options, &block)
11
+ end
12
+ end
13
+ end
14
+
15
+
16
+ # Represents an {Artifactory}[http://www.jfrog.com/home/v_artifactory_opensource_overview]
17
+ # {Folder Info}[http://wiki.jfrog.org/confluence/display/RTF/Artifactory's+REST+API#Artifactory'sRESTAPI-FolderInfo]
18
+ # resource.
19
+ #
20
+ # === Example
21
+ #
22
+ # folder = ArtRest::Folder.new('http://localhost:8081/artifactory/api/storage/libs-release-local/commons-lang/commons-lang', { ... })
23
+ #
24
+ class Folder < ArtRest::Resources
25
+ include ArtRest::DirEntry
26
+
27
+ class << self
28
+
29
+ def matches_path(path, options) # :nodoc:
30
+ return false unless path =~ %r|^/api/storage/[a-zA-Z0-9_.+-]+/.+$|
31
+ content_hash = JSON.parse(RestClient::Resource.new([options[:base_url], path].join, options[:user], options[:password]).get)
32
+ return content_hash['children']
33
+ end
34
+ end
35
+
36
+ self.mime_type = MIME::Types['application/json']
37
+
38
+ self.resources_creator = Proc.new do |content, options|
39
+ (content['children'] || []).map { |child| ArtRest::DirEntry.create_node("#{content['uri']}#{child['uri']}", options, child) }
40
+ end
41
+
42
+ self.resource_attributes :path,
43
+ :lastUpdated,
44
+ :repo,
45
+ :uri,
46
+ :modifiedBy,
47
+ :created,
48
+ :createdBy,
49
+ :lastModified,
50
+ :metadataUri
51
+
52
+ public
53
+
54
+ def [](suburl, &new_block) # :nodoc:
55
+ subresource_content = JSON.parse(RestClient::Resource.new(url, options, &new_block)[suburl].get)
56
+ ArtRest::DirEntry.create_node([url, suburl].join, options, subresource_content, &new_block)
57
+ end
58
+
59
+ def traverse(&block)
60
+ each do |child_entry|
61
+ block.call(child_entry)
62
+ if child_entry.is_a?(ArtRest::Folder) then
63
+ child_entry.each &block
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ # Represents an {Artifactory}[http://www.jfrog.com/home/v_artifactory_opensource_overview]
70
+ # {File Info}[http://wiki.jfrog.org/confluence/display/RTF/Artifactory's+REST+API#Artifactory'sRESTAPI-FileInfo]
71
+ # resource.
72
+ #
73
+ # === Example
74
+ #
75
+ # file = ArtRest::File.new('http://localhost:8081/artifactory/api/storage/libs-release-local/commons-lang/commons-lang/3.2/commons-lang-3.2.jar', { ... })
76
+ #
77
+ class Artifact < ArtRest::Resource
78
+ include ArtRest::DirEntry
79
+
80
+ class << self
81
+
82
+ def matches_path(path, options) # :nodoc:
83
+ return false unless path =~ %r|^/api/storage/[a-zA-Z0-9_.+-]+/.+$|
84
+ content_hash = JSON.parse(RestClient::Resource.new([options[:base_url], path].join, options[:user], options[:password]).get)
85
+ return ! content_hash['children']
86
+ end
87
+ end
88
+
89
+ self.mime_type = MIME::Types['application/json']
90
+
91
+ self.resource_attributes :path,
92
+ :lastUpdated,
93
+ :repo,
94
+ :uri,
95
+ :modifiedBy,
96
+ :created,
97
+ :createdBy,
98
+ :lastModified,
99
+ :metadataUri,
100
+ :mimeType,
101
+ :downloadUri,
102
+ :size,
103
+ :checksums,
104
+ :originalChecksums
105
+
106
+ public
107
+
108
+ # Overwritten to always throw NotImplementedError since ArtRest::Artifacts
109
+ # don't have sub resources.
110
+ #
111
+ def [](suburl, &new_block)
112
+ raise NotImplementedError.new("Instances of ArtRest::Artifact don't have child resources")
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,61 @@
1
+
2
+ module ArtRest
3
+
4
+ # Represents an {Artifactory}[http://www.jfrog.com/home/v_artifactory_opensource_overview]
5
+ # {Repositories}[http://wiki.jfrog.org/confluence/display/RTF/Artifactory's+REST+API#Artifactory'sRESTAPI-GetRepositories]
6
+ # resource.
7
+ #
8
+ # === Example
9
+ #
10
+ # repos = ArtRest::Repositories.new('http://localhost:8081/artifactory/api/repositories', { ... })
11
+ #
12
+ class Repositories < ArtRest::Resources
13
+
14
+ class << self
15
+ public
16
+
17
+ # Shortcut method to retrieve *the* ArtRest::Repositories
18
+ # instance.
19
+ #
20
+ # * *Args* :
21
+ # - +base_url+ -> Our Artifactory server's base URL
22
+ # - +options+ -> A Hash containing username and password
23
+ # * *Returns* :
24
+ # - *The* ArtRest::Repositories instance representing the
25
+ # collection of all Artifactory repositories
26
+ #
27
+ def get(base_url, options)
28
+ Repositories.new("#{base_url}/api/repositories", options)
29
+ end
30
+
31
+ def matches_path(path, options) # :nodoc:
32
+ path =~ %r|^/api/repositories/?$|
33
+ end
34
+ end
35
+
36
+ self.mime_type = MIME::Types['application/json']
37
+
38
+ self.resources_creator = Proc.new do |content, options|
39
+ content.map { |repo| ArtRest::Repository.new(repo['url'], options) }
40
+ end
41
+
42
+ # Look up and return the repository[rdoc-ref:ArtRest::Repository] named
43
+ # +repo_name+. Optionally yield that repository to a block provided a
44
+ # block is given.
45
+ #
46
+ # * *Args* :
47
+ # [+repo_name+] Name of the repository to return
48
+ # * *Returns* :
49
+ # - The repository named +repo_name+, an ArtRest::Repository instance
50
+ # * *Raises* :
51
+ # [+RestClient::ResourceNotFound+] If +repo_name+ does not point to an
52
+ # existing repository
53
+ #
54
+ def repository(repo_name) # :yields: repository
55
+ repo_url = concat_urls(base_url, "/#{repo_name}/")
56
+ repo = ArtRest::Repository.new(repo_url, options, block)
57
+ yield repo if block_given?
58
+ repo
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,42 @@
1
+
2
+ module ArtRest
3
+
4
+ # Represents an {Artifactory}[http://www.jfrog.com/home/v_artifactory_opensource_overview]
5
+ # repository resource.
6
+ #
7
+ # *IMPORTANT* This is *not* the {Repository Configuration}[http://wiki.jfrog.org/confluence/display/RTF/Artifactory's+REST+API#Artifactory'sRESTAPI-RepositoryConfiguration]
8
+ # resource as this would require Artifactory Pro. It is rather a repository's
9
+ # top-level {folder}[rdoc-ref:ArtRest::Folder] "/".
10
+ #
11
+ # === Example
12
+ #
13
+ # libs_releas_local = ArtRest::Repository.new('http://localhost:8081/artifactory/api/storage/libs-release-local', { ... })
14
+ #
15
+ class Repository < ArtRest::Resources
16
+
17
+ class << self
18
+
19
+ def matches_path(path, options) # :nodoc:
20
+ path =~ %r|^/api/storage/[a-zA-Z+-._]+/?$|
21
+ end
22
+ end
23
+
24
+ self.mime_type = MIME::Types['application/json']
25
+
26
+ self.resources_creator = Proc.new do |content, options|
27
+ self_url = content['uri']
28
+ (content['children'] || []).map { |child| ArtRest::DirEntry.create_node("#{self_url}#{child['uri']}", options, child) }
29
+ end
30
+
31
+ self.resource_attributes :path,
32
+ :lastUpdated,
33
+ :repo,
34
+ :uri,
35
+ :modifiedBy,
36
+ :created,
37
+ :createdBy,
38
+ :lastModified,
39
+ :metadataUri,
40
+ :children
41
+ end
42
+ end
@@ -0,0 +1,366 @@
1
+
2
+ module ArtRest
3
+
4
+ # Abstract base class for all Artifactory resources.
5
+ #
6
+ class Resource < RestClient::Resource
7
+
8
+ class << self
9
+
10
+ public
11
+
12
+ # Add +subclass+ to the list of known subclasses.
13
+ #
14
+ # * *Args* :
15
+ # - +subclass+ -> The subclass to add
16
+ #
17
+ def inherited(subclass)
18
+ @@subclasses ||= []
19
+ @@subclasses << subclass
20
+ end
21
+
22
+ # Define a singleton method +mime_type+ on the class this
23
+ # method is called on, where calling +mime_type+ will return
24
+ # +value+.
25
+ #
26
+ # This essentially emulates a "proper" class variable
27
+ # +mime_type+.
28
+ #
29
+ # * *Args* :
30
+ # - +value+ -> This class' mime type [MIME::Type/required]
31
+ #
32
+ def mime_type=(value)
33
+ self.singleton_class.class_eval do
34
+ define_method(:mime_type) do
35
+ value
36
+ end
37
+ end
38
+ end
39
+
40
+ # Take an array of symbols +attrs+ and for each symbol
41
+ # "attr" define an instance method +attr+ on this class
42
+ # that returns <tt>content["attr"]</tt>.
43
+ #
44
+ # So, given an attribute <tt>:modifiedBy</tt> this will define
45
+ #
46
+ # def modifiedBy
47
+ # content['modifiedBy']
48
+ # end
49
+ #
50
+ def resource_attributes(*attrs)
51
+ attrs.each do |attr|
52
+ define_method(attr) do
53
+ return content[attr.to_s] unless block_given?
54
+ yield content[attr.to_s]
55
+ end
56
+ end
57
+ end
58
+
59
+ # Factory method: create and return an instance of a concrete
60
+ # ArtRest::Resource subclass appropriate for the supplied
61
+ # +resource_url+.
62
+ #
63
+ # === Example
64
+ #
65
+ # ArtRest::Resource.create('http://localhost:8081/artifactory/api/system,
66
+ # options)
67
+ #
68
+ # will return an ArtRest::System instance.
69
+ #
70
+ # * *Args* :
71
+ # [+resource_url+] URL of the resource to create [required]
72
+ # [+options+] Options hash holding base_url, user and
73
+ # password [required]
74
+ # [+block+] Passed on to <tt>RestClient::Resource.initialize</tt> [optional]
75
+ # * *Returns* :
76
+ # - An instance of a concrete ArtRest::Resource subclass appropriate
77
+ # for the supplied +resource_url+
78
+ #
79
+ def create(resource_url, options, &block)
80
+ check_options(options)
81
+ @@subclasses.each do |subclass|
82
+ return subclass.new(resource_url, options, &block) if
83
+ subclass.respond_to?(:matches_path) and
84
+ subclass.matches_path(relative_path(resource_url, options), options)
85
+ end
86
+ raise ArgumentError.new("Unsupported resource URL #{resource_url}")
87
+ end
88
+
89
+ def check_options(options) # :nodoc:
90
+ raise ArgumentError, "Must pass :base_url" unless options[:base_url]
91
+ raise ArgumentError, "Must pass :user" unless options[:user]
92
+ raise ArgumentError, "Must pass :password" unless options[:password]
93
+ end
94
+
95
+ protected
96
+
97
+ def relative_path(resource_url, options) # :nodoc:
98
+ check_options(options)
99
+ base_path = Addressable::URI.parse(options[:base_url]).path
100
+ resource_path = Addressable::URI.parse(resource_url).path
101
+ resource_path.slice!(base_path)
102
+ resource_path
103
+ end
104
+ end
105
+
106
+ # This class' mime type
107
+ self.mime_type = MIME::Types['application/json']
108
+
109
+ public
110
+
111
+ # Create a new ArtRest::Resource instance, representing the Artifactory
112
+ # resource located at +resource_url+. Use username and password
113
+ # contained in +options+ to authenticate against the Artifactory server.
114
+ # Optionally take and cache the parsed +content+ of the resource pointed
115
+ # to by +resource_url+ in case the caller has already accessed that
116
+ # resource. A +block+, if given, will transparently be passed on to
117
+ # RestClient::Resource's - our ancestor's - constructor.
118
+ #
119
+ # * *Args* :
120
+ # - +resource_url+ -> URL of the Artifactory resource
121
+ # - +options+ -> A hash containing our Artifactory server's base url,
122
+ # and a username and a password to authenticate against that server
123
+ # - +content+ -> The *parsed* content of the resource, if the caller
124
+ # has already accessed that resource [optional]
125
+ # - +block+ -> A block that will be passed on to
126
+ # RestClient::Resource#initialize [optional]
127
+ #
128
+ def initialize(resource_url, options, content = nil, &block)
129
+ self.class.check_options(options)
130
+ super(resource_url, options, &block)
131
+ @content = content if content
132
+ end
133
+
134
+ # Return our Artifactory server's <em>base url</em>.
135
+ #
136
+ # === Example
137
+ #
138
+ # http://localhost:8081/artifactory
139
+ #
140
+ def base_url
141
+ options[:base_url]
142
+ end
143
+
144
+ # Return this resource's *parsed* content. In almost all cases this will
145
+ # be a Hash representing this resource's JSON content. The only
146
+ # exception is ArtRest::System, where the content is a plain text
147
+ # representation and thus returned as a String.
148
+ #
149
+ # If called with a +block+, yield this resource's parsed content to that
150
+ # block.
151
+ #
152
+ # * *Args* :
153
+ # [+block+] A block to yield this resource's parsed content to [optional]
154
+ # * *Returns* :
155
+ # - If called *without* a block, this resource's *parsed* content, most
156
+ # often a +Hash+, otherwise a +String+.
157
+ # If called *with* a block, +self+
158
+ # * *Raises* :
159
+ # [+RestClient::ResourceNotFound+] If the resource this instance
160
+ # represents does in fact not exist
161
+ # on the server
162
+ # [+RestClient::Unauthorized+] If accessing this resource is not
163
+ # authorized
164
+ #
165
+ def content # :yields: content
166
+ return _content unless block_given?
167
+ # If our block returns a value, this will become this resource's new
168
+ # content
169
+ yield _content
170
+ self
171
+ end
172
+
173
+ # Return this resource's *unparsed* content as a string[rdoc-ref:String].
174
+ #
175
+ # Most of Artifactory's resources are represented in JSON, and these
176
+ # will be parsed into an equivalent ruby {hash}[rdoc-ref:Hash] upon load. This
177
+ # method will *unparse* that hash back into a JSON string.
178
+ #
179
+ # In all other cases, when a resource's mime type is *not*
180
+ # 'application/json', that resource's representation will remain
181
+ # unchanged, i.e. will be stored as a string, and this method will
182
+ # return that string as is.
183
+ #
184
+ # * *Returns* :
185
+ # - This resource's unparsed content, a {string}[rdoc-ref:String]
186
+ # * *Raises* :
187
+ # [+RestClient::ResourceNotFound+] If the resource this instance
188
+ # represents does in fact not exist
189
+ # on the server
190
+ # [+RestClient::Unauthorized+] If accessing this resource is not
191
+ # authorized
192
+ #
193
+ def unparsed_content(fmt = :plain)
194
+ return _content unless self.class.mime_type == MIME::Types['application/json']
195
+ case fmt
196
+ when :pretty then
197
+ JSON.pretty_generate(_content)
198
+ else
199
+ JSON.generate(_content)
200
+ end
201
+ end
202
+
203
+ # Set this resource's representation to +value+. Handle +value+ as
204
+ # appropriate for this resource's mime type:
205
+ #
206
+ # === application/json
207
+ #
208
+ # If we are dealing with a JSON resource - the most common case -
209
+ # +value+ may be one of
210
+ #
211
+ # * Hash: will be accepted as is
212
+ # * String: will be interpreted as a JSON encoded string and parsed into a
213
+ # ruby Hash
214
+ #
215
+ # Raise an +ArgumentError+ in all other cases.
216
+ #
217
+ # === text/plain or application/xml
218
+ #
219
+ # In this case, +value+ may be of any kind, but will be converted into a
220
+ # String via calling +value+.#to_s.
221
+ #
222
+ # * *Args* :
223
+ # - +value+ -> This resource's new content/representation. See above.
224
+ # * *Raises* :
225
+ # - +ArgumentError+ -> If this resource's mime type is
226
+ # +application/json+ and value is neither a Hash nor a String
227
+ #
228
+ def content=(value)
229
+ if value.nil?
230
+ @content = nil
231
+ return
232
+ end
233
+ case self.class.mime_type
234
+ when MIME::Types['application/json'] then
235
+ if value.is_a? Hash
236
+ @content = value
237
+ elsif value.is_a? String
238
+ @content = JSON.parse(value)
239
+ else
240
+ raise ArgumentError, "Can only accept Hash and String. Got: #{value.class}"
241
+ end
242
+ else
243
+ @content = value.to_s
244
+ end
245
+ end
246
+
247
+ # Yield this resource's parsed content to +block+. If block returns a value,
248
+ # accept this value as this resource's new content. Calling this method
249
+ # is therefore a potentially destructive operation.
250
+ #
251
+ # Also see #content.
252
+ #
253
+ # * *Args* :
254
+ # [+block+] A block to yield this resource's parsed content to
255
+ # * *Returns* :
256
+ # - +self+ This instance
257
+ # * *Raises* :
258
+ # [+RestClient::ResourceNotFound+] If the resource this instance
259
+ # represents does in fact not exist
260
+ # on the server
261
+ # [+RestClient::Unauthorized+] If accessing this resource is not
262
+ # authorized
263
+ #
264
+ def content! &block # :yields: content
265
+ # If our block returns a value, this will become this resource's new
266
+ # content
267
+ new_content = yield _content
268
+ self.content = new_content if new_content
269
+ self
270
+ end
271
+
272
+ # Look up and return the ArtRest::Resource instance that is located at
273
+ # +relative_path+ relative to this resource. Take care to return the
274
+ # appropriate ArtRest::Resource subtype.
275
+ #
276
+ # === Example
277
+ #
278
+ # Given
279
+ #
280
+ # repository = ArtRest::Repository.new('http://localhost:8081/artifactory/api/storage/libs-release-local', { ... })
281
+ #
282
+ # the expression
283
+ #
284
+ # folder = repository['/commons-lang/commons-lang/3.2.0']
285
+ #
286
+ # will return an ArtRest::Folder instance.
287
+ #
288
+ # * *Args* :
289
+ # [+relative_path+] The path of the resource to look up, relative to
290
+ # this resource
291
+ # [+block+] A block that will be transparently passed on to
292
+ # RestClient::Resource#initialize [optional]
293
+ # * *Returns* :
294
+ # - An ArtRest::Resource instance of appropriate type representing the
295
+ # Artifactory resource located at +relative_path+ relative to this resource
296
+ #
297
+ def [](relative_path, &new_block)
298
+ case
299
+ when block_given? then
300
+ ArtRest::Resource.create(concat_urls(url, relative_path), options, &new_block)
301
+ when block then
302
+ ArtRest::Resource.create(concat_urls(url, relative_path), options, &block)
303
+ else
304
+ ArtRest::Resource.create(concat_urls(url, relative_path), options)
305
+ end
306
+ end
307
+
308
+ # Hide method from RestClient::Resource by default since they
309
+ # may make no sense on a concrete subclass.
310
+ protected :get, :post, :put, :delete, :head, :patch
311
+
312
+ protected
313
+
314
+ # Post changes made to this resource's content/representation since
315
+ # loading it from Artifactory - if any - back to the server, i.e.
316
+ # *update* the resource this instance represents.
317
+ #
318
+ # === Example
319
+ #
320
+ # sysConfig = ArtRest::System::GeneralConfiguration.get
321
+ # sysConfig.content = ...
322
+ # sysConfig.update!
323
+ #
324
+ # * *Args* :
325
+ # - +additional_headers+ -> Any additional headers to be set on the
326
+ # POST request
327
+ #
328
+ def update!(additional_headers = {}, &block)
329
+ post(self.unparsed_content, additional_headers, &block)
330
+ end
331
+
332
+ # Take +content+ and make it this resource's new #content by calling
333
+ # #content= +content+. Subsequently, post this resource's changes back to
334
+ # Artifactory, i.e. *update* the resource this instance represents.
335
+ #
336
+ # Calling this method on a resource +res+ is therefore equivalent to
337
+ #
338
+ # res.content = content
339
+ # res.update!(additional_headers, &block)
340
+ #
341
+ # * *Args* :
342
+ # [+content+] This resource's new #content
343
+ # [+additional_headers+] Any additional headers to be set on this POST
344
+ # request that calling this method entails
345
+ # [+block+] A block that will be passed on to
346
+ # RestClient::Resource#post, i.e. it will handle
347
+ # the response to our POST request
348
+ #
349
+ def update_with!(content, additional_headers = {}, &block)
350
+ self.content = content
351
+ update!(additional_headers, &block)
352
+ end
353
+
354
+ private
355
+
356
+ def _content # :nodoc:
357
+ return @content if @content
358
+ raw = get
359
+ @content = case self.class.mime_type
360
+ when MIME::Types['application/json'] then JSON.parse(raw)
361
+ else raw
362
+ end
363
+ @content
364
+ end
365
+ end
366
+ end