I have an application which uses a combination of Chef, Vagrant, and custom Ruby code I've written to build a Virtual Machine. The application does a lot of things aside from creating the virtual machine, so I've written a class App which lives outside of Chef, but which has methods which would also be useful as chef helper methods.
Is it possible to import a Chef Library module into my custom class so that I can share these methods accordingly?
When I tried to do this, I used the following approach:
Given the project directory which contains
--| cookbooks
--| helpers
--| libraries
DemoStructureHelper.rb
--| lib
main_app.rb
...
Vagrantfile
main_app.rb contains my custom class App
Vagrantfile references App as one would expect: app = App.new and so forth
The DemoStuctureHelper.rb has a module DemoStructureHelper with a collection of methods which would also benefit my App class
The DemoStructureHelper is defined like so:
module DemoStructureHelper
def self.my_helper_method
...
end
...
end
In main_app.rb, I have:
require_relative '../cookbooks/helpers/libraries/demo_structure_helper'
include DemoStructureHelper
class App
def my_custom_method
DemoStructureHelper.my_helper_method
...
end
...
end
When I use the App class in the Vagrantfile, I get the following error using the above:
Message: NameError: uninitialized constant App::DemoStructureHelper
My thought was that I somehow needed to make my class aware of Chef; perhaps something like one of these:
include Chef::DemoStructureHelper
include Chef::DemoStructure::Helper
include Chef::Helper::DemoStructure
etc., but these all yield:
Message: NameError: uninitialized constant App::Chef
Hence my question: How can I include a Chef library module in my custom class outside of chef so that I can share methods between chef and my custom code?
Related
I'm building a platform in Rails that will act as a basis for a few other Rails applications. Ideally I would like this to be a shared engine, on which the other concrete applications can build.
It would be nice if the applications could extend the base classes that the engine provides. AFAIK this can be done using monkey patching, but that feels 'hacky' to me.
I stumbled onto what looked like the solution, which was to just create 'mirror' classes in the main Rails app, which extend those of the engine:
# SharedEngine/models/shared_engine/post.rb
module SharedEngine
class Post < ActiveRecord::Base
def hello
"Hello"
end
end
end
# App/models/shared_engine/post.rb
require SharedEngine::Engine.root.join('app', 'models', 'shared_engine', 'post')
class Post < SharedEngine::Post
def hello
super + " world"
end
end
However, it looks like there are some autoloading problems. After server startup, it prints "Hello". Then after I save the app model, it says "Hello world".
The Rails engine guide suggests putting shared code into concerns. Is there any other way to get this working cleanly without having to create concerns for every class?
Since you need to create a basis for a few Rails apps to reduce boilerplate i suggest having a look a Rails Application Templates. Using templates you may provide all the necessary scaffolding without monkey-patching any class.
I have created a simple class under the models folder like so:
class CaffeineShops
def initialize
end
end
When I run rails console and try CaffeineShops.new(), I get a message: "NoMethodError: undefined method 'new' for CaffeineShops::Module"
I am using Rails 4.1.
Any ideas why I am getting that error?
You cannot name a model the same name as the application itself. When you run rails g caffeine_shops this creates a module named CaffeineShops which will power your application.
The module is utilized in many files including
config\application.rb
config\environment.rb
config\environments\development.rb
config\environments\production.rb
config\environments\test.rb
config\initializers\secret_token.rb
config\initializers\session_store.rb
config\routes.rb
config.ru *actually starts the application on Rack-based servers
Rakefile *loads rake tasks for the application
When you then try to name a class the same name it creates ambiguity in the rails application so your code is assuming you mean the module CaffeineShops not the class CaffeineShops.
If you were to actually use a generator to define this model rails would make it very clear there was a problem with this e.g.
rails g caffeine_shops
cd caffeine_shops
rails g model caffeine_shops
#=>The name 'CaffeineShops' is either already used in your application or reserved
by Ruby on Rails. Please choose an alternative and run this generator again.
I'm using YARD to generate docs for a couple projects that I'm working on. In one, I'm co-developing a gem that hosts a lot of redundant and shared resources that will be used by other projects.
What'd I'd like is for references from the first project to classes or methods in the gem to show up in the docs for the main project. For example
Gem
class Widget
# Spins the widget of course.
def spin
end
end
Project
class SpecialWidget
# Jump and {#spin}.
def dance
end
end
I'd like the docs for SpecialWidget to generate an actual link to the Widget#spin method.
Can this be done?
I have a library of functions that is packaged up as a gem. One of these functions requires the use of a third-party gem that itself has a long list of dependencies.
Is there a way that I can set up my gem so that users can install it and use those functions in the gem that don't have the third-party dependency without the runtime complaining?
I want an exception to be raised when the method with the dependency is called, but the user should be able to use the other functions without a runtime error.
Is this possible?
You can split your functions to modules (maybe modules to files too) that are depending or not depending gem. including GemDependent module trying to require your gem if not then redefine all gem dependent functions to raise exception.
module YourFunctions
module GemDependent
def self.included(klass)
require "yourgem"
rescue LoadError
instance_methods.each do |m|
define_method(m) { raise "you need yourgem to run #{m}" }
end
end
def gem_dependent_function
end
end
include GemDependent
def no_dependent_function
end
end
include YourFunctions
gem_dependent_function
# "you need yourgem to run gem_dependent_function"
if you want to know how to do this, have a look at the various wrapper libraries for http, json, yaml etc.
a good example is https://github.com/rubiii/httpi where you can configure one of 3 http adapters. the adapters themselves require the individual dependencies within ruby.
the problem is, that the end-user needs to somehow know how to install the 3rd party gems. it's currently not possible to implement optional dependencies within ruby gemspecs.
I have to develop a plugin based software in ruby. What's the best architeture tu use?
I am thinking about plugin like this, each in a separate .rb file:
class MyPlugin < Plugin
def info
infos
end
def run
# run
end
end
How i can write a plugin manager to call these plugins?
You'd have to clearly define what "calling the plugins" exactly mean.
For start, you can check out here how to require all the files from a directory, put your plugins into a single directory and require them all.
Then you need to somehow pick which one to use, whether it be:
passing its classname as a string through a command line argument or a config file parameter, and looking for a class by that name using const_get, or
presenting a user a list of all plugins (all descendants of your Plugin class) - check out here how to do it
Finally, you instantiate your plugin and do whatever you need to do with it.