I have two isolated Ruby installations (one for Chef, one for Sensu). From within the Chef installation, I'd like to get a list of all versions of a gem installed into the Sensu ruby. I know the absolute paths of both installations at runtime.
I've used Gem::Specification::find_all_by_name before to great effect, and this is the sort of method I'm looking for, but it only seems to work on the currently executing ruby, and doesn't seem to have any kind of notion of reusability.
One way would be to use gem list with grep and parse that output, but i'd really rather not have to shell out to solve the problem, I prefer an in-ruby solution. I could also inspect the filesystem myself and recreate the logic of Gem::Specification, but that's not that much better than shelling out.
Is there a way to get equivalent output to Gem::Specification without duplicating the parsing logic myself?
Gem::Specification has a (global) property called dirs which allows you to specify an array of paths that Gem::Specification will use when searching for specifications.
Note that the path it expects must be the parent folder of "specifications", so for instance /opt/embedded/product/lib/specifications will not work, but /opt/embedded/product/lib will work.
Related
I have a gem I want to add types to (either by submitting a PR or by forking to use just in my project), but the documentation does not give any more guidance other than to create an /rbi folder.
Thinking out loud, should it mirror the files in lib/ with added type signatures, or should it be one large file (per version?) like the signatures in sorbet-typed?
I don't know of any gems doing this yet, and the documentation doesn't mention it either.
I recently went through this process for one of my gems - pdf-reader. It was my first experience with sorbet (so I'm no expert), and I also noticed the lack of documentation for the specific use case of adding types to a gem.
I ended up generating the initial rbi file using parlour, which put them all into a single file: https://github.com/yob/pdf-reader/blob/480aa240a531cd6f97a46a29279f19025821e886/rbi/pdf-reader.rbi
Sorbet seems happy with that, so I haven't changed it.
I have a gem I want to add types to (either by submitting a PR or by forking to use just in my project), but the documentation does not give any more guidance other than to create an /rbi folder.
Thinking out loud, should it mirror the files in lib/ with added type signatures, or should it be one large file (per version?) like the signatures in sorbet-typed?
I don't know of any gems doing this yet, and the documentation doesn't mention it either.
I recently went through this process for one of my gems - pdf-reader. It was my first experience with sorbet (so I'm no expert), and I also noticed the lack of documentation for the specific use case of adding types to a gem.
I ended up generating the initial rbi file using parlour, which put them all into a single file: https://github.com/yob/pdf-reader/blob/480aa240a531cd6f97a46a29279f19025821e886/rbi/pdf-reader.rbi
Sorbet seems happy with that, so I haven't changed it.
We have a local Ruby Library that we include in a lot of our ruby projects. It contains a lot of configuration information that would be extremely useful to use in our chef scripts. This allows us to put all of our configuration in one place, so we don't have to make multiple places everytime we change a database etc. Trying to keep the code DRY. That being said, the code are straight ruby functions, not in the chef DSL.
I have been trying to pull the library into Chef, but have found it very difficult. Which makes me think I'm going against some sort of pattern.
I have tried and didn't work.
- Add the Ruby Code via require_relative and includes.
- Add the Ruby Code to it's own cookbook, then wrap the cookbook.
- Create a local ruby gem (not retrievable via rubygems, or other repo)
What worked:
- Copying and pasting the code into a cookbook. (but it's not sharable.)
My real question, what is the best way to share this ruby code library amongst many cookbooks? Depending on the best way, how do you actually do it?, or pointers in the right direction.
thanks.
myles.
I would use Halite (disclaimer: my project) to convert the gem into a cookbook and then upload it as a cookbook. Then you can depend on it and require stuff from it like normal in your Chef code.
You can create a cookbook and copy the ruby code into libraries folder of the cookbook. Then it will be loaded by Chef automatically on node, as soon as you include the cookbook into run list.
Could you tell, what exactly you did, and what didn't work when you tried "Add the Ruby Code to it's own cookbook, then wrap the cookbook"?
I have created a ruby library. Basically, it reads the ip of the server in opsworks. Once it gets that it knows what environment it is in, and it will set a hash. This hash is then used to set the attributes. This way all the server configurations are stored in one place.
I can put the code in each different cookbook that needs it. However, I have to duplicate the code in each different cookbook. I would like to put the code in one place, and include it in each cookbook. That way if I have to modify it, I only have to do it in one place.
I tried making it a gem, and requiring it with no luck.
I also created a cookbook with nothing but the ruby code in a library. I then included "depends etc" that cookbook in another and tried to run the library functions, but I couldn't get it to work.
I may have been on the right track, but I stopped before I got it working.
Essentially I want to write a simple ruby code library, that I can use in any cookbook I want. There must be a way to do this, but I've run into some dead ends. I'll keep banging my head against the wall. Eventually, a hole will open up!
Is there a well-designed method that can run my ruby program from anywhere? I already searched a couple of ways to import my ruby program from the different directories by using the relative path.
i.e.
File.expand_path(“my_path”)
It worked and let me run it anywhere, but somehow, it is a little unreadable and I think it is pretty messy. So I think there may be a solution or convention to deal with this kind of problem when there are many file paths that have to be imported.
I suggest you have a look how to package a gem and build your own and install it. Or you could place your binary in $PATH, but that's a bit more messy.
I just started learning Ruby coming from Java. In Java you would use packages for a bigger projects. Is there anything equivalent to that in Ruby? Or what is the best way to achieve a package like setting?
The way I'm doing it right now is 'load'ing all the needed class into my new Ruby file. Isn't there a way to tell my current Ruby class to use all other Ruby classes in the same folder?
Cheers,
Mike
There's three kinds of package loading in Ruby:
Explicitly loading a library with require
Implicitly loading a library using autoload
Importing a library with gem
The require method is the most direct and has the effect of loading in and executing that particular file. Since that file may go on to require others, as a matter of convenience, you may end up loading in quite a lot at once.
The autoload method declares a module that will be loaded if you reference a given symbol. This is a common method to avoid loading things that you don't need, but making them automatically available if you do. Most large libraries use this method of loading to avoid dumping every single class into memory at once.
The gem approach is a more formalized way of packaging up a library. Although it is uncommon for applications to be split up into one or more gems, it is possible and provides some advantages. There's no obligation to publish a gem as open-source, you can keep it private and distribute it through your own channels, either a private web site or git repository, for instance, or simply copy and install the .gem file as required.
That being said, if you want to make a library that automatically loads a bunch of things, you might take this approach:
# lib/example.rb
Dir.glob(File.expand_path('example/**/*.rb', File.dirname(__FILE__))).each do |file|
require file
end
This would load all the .rb files in lib/example when you call require 'example'.
You probably want to use require rather than load, since it should take care of circular references.
If you want to grab all the files in a given folder, that's easy enough:
Dir.foreach("lib"){|x| require x}
Your other option is to have a file that manually requires everything, and have your other files require that.
You should also look at wrapping the code in your libraries with a module block, to give them their own namespaces.
That said: rightly or wrongly, I tend to feel that this is the one area -- perhaps the only one -- where Ruby is less powerful than Python, say, or Java.
I understand your feeling. It's an ordinary problem you have to face when coming from another language like Java. I'd say try to study Ruby modules but you deserve a longer reply. So my advice is reading a good Ruby book like Eloquent Ruby.