📜 ⬆️ ⬇️

ActiveResource, prefix and nested resources

Prehistory


I am a programmer with very little experience (I recently had about a year in labor).
About six months ago, I started working with Ruby (out of Rails) and immediately became acquainted with Active Resource and Redmine.

It was a very interesting experience, now it seems to me that Ruby is almost an ideal language (it is a language, I don’t wonder about memory consumption and speed).

However, there is a lot of magic in it, which can be difficult to understand when you read the source code of any major projects (ActiveResource I refer to them, although compared to rails, of which it is a part, this hem seems to be a drop in the ocean).
')

Problem


The project was to create a console utility (Thor-based) that works with the Redmine REST API and provides all sorts of nishtyki (by the way, inspired by the project, I am currently working on a similar utility, partially duplicating the functionality: https://github.com/Nondv/ redmine_cli ).

If you look at the documentation for Versions or Issue Relations ( http://www.redmine.org/projects/redmine/wiki/Rest_IssueRelations ), you can pay attention to the fact that the list of relations is obtained using the address of the type issues/<id>/relations.xml , and for a specific object - relations/<id>.xml .

Actually, in order to get the list, we find a solution in the form of using prefix :

 class Relation < ActiveResource::Base self.user = 'yet another apikey' self.password = 'we dont need password when using redmine apikey' self.site = 'www.yet-another-redmine.com' #   ! self.prefix = '/issues/:issue_id/' end Relation.all params: { issue_id: 1 } 

Everything seems smart and works. But what if we need to get (or delete) a separate object?
Relation.find(id) throws an exception ActiveResource::MissingPrefixParam: project_id prefix_option is missing , which is quite reasonable, we after all indicated that we should contact the address with a prefix.

Decision


Redmine REST did not provide the ability to get a separate object in the nested resources.

I personally, having worked a little more than a couple of weeks with Ruby, and having tortured Google with questions, was able to give birth to a solution, in which an anonymous class was used to obtain the list. Now I can not reproduce, but offhand something like this:

 class Issue < ActiveResource::Base self.user = 'yet another apikey' self.password = 'we dont need password when using redmine apikey' self.site = 'http://www.yet-another-redmine.com' # ,    relations,       include, #         def relations tmp_class = Class.new(ActiveResource::Base) do ... self.site = "http://www.yet-another-redmine.com/issues/#{id}/" self.element_name = 'relation' end tm_class.all end end 

You already smell that strange smell, right?
In general, the decision worked and in general it was made, since I could not offer alternatives.

In a personal project (link above) I got the opportunity to correct this misunderstanding (I hope, for the better). The solution is to override the ActiveResource::Base.element_path . For example Version:

 class Version < ActiveResource::Base ... self.prefix = '/projects/:project_id/' # # ,      : # https://github.com/rails/activeresource/blob/master/lib/active_resource/base.rb#L760 #    # # Praise Open Source! # def self.element_path(id, _prefix_options = {}, query_options = nil) "/versions/#{URI.parser.escape id.to_s}#{format_extension}#{query_string(query_options)}" end end class Project < ActiveResource::Base ... def versions Version.all params: { project_id: id } end end 

Conclusion


Actually, why all this? I think people who understand ActiveResource will say that this is an obvious solution.

The bottom line is that I, being only immersed in all this, ran into a problem and could not solve it, even with the help of the All-Knowing. What was my mistake? That I was afraid to understand the source code a little and did not want to study the documentation ( http://www.rubydoc.info is just a find!), In which even after looking at summary it was possible to choose something to solve the problem without involving nano-nuclear magical technology

I hope that if someone is in my position, he will not repeat my mistakes.
A week ago, I saw a statement that Larry Wall considers laziness to be one of the main advantages of a programmer. Not sure, as was written in the original Kemel-Buk (which is from there, I suppose), but in translation he used the word "virtue" rather than "dignity".

No matter how lazy I am, it does not make me a good programmer. Laziness does not always help to find a solution.

PS it seems that the post, suitable for the format of a personal mini-blog, became too fat in the process of writing.

Source: https://habr.com/ru/post/279727/


All Articles