alternative to method_missing in Chef 13 - ruby

upgraded the system from chef 12 to 13 facing issue with method_missing
found from some links that method_missing is deprecated is there any alternative for this.
def method_missing(name, *args, &block)
# Build the set of names to check for a valid resource
lookup_path = ["application_#{name}"]
run_context.cookbook_collection.each do |cookbook_name, cookbook_ver|
if cookbook_name.start_with?("application_")
lookup_path << "#{cookbook_name}_#{name}"
end
end
lookup_path << name
resource = nil
lookup_path = ["application_#{name}"]
# Try to find our resource
lookup_path.each do |resource_name|
begin
Chef::Log.debug "Trying to load application resource #{resource_name} for #{name}"
resource = super(resource_name.to_sym, *args, &block)
break
rescue NameError => e
# Works on any MRI ruby
if e.name == resource_name.to_sym || e.inspect =~ /\b#{resource_name}\b/
next
else
raise e
end
end
end
raise NameError, "No resource found for #{name}. Tried #{lookup_path.join(', ')}" unless resource
# Enforce action :nothing in case people forget
resource.action :nothing
# Make this a weakref to prevent a cycle between the application resource and the sub resources
resource.application WeakRef.new(self)
resource.type name
#sub_resources << resource
resource
end

Ahh I see the real question in comments. The application_php cookbook was never upgraded to work with the rest of the newer application cookbook suite. You would have to write your own version of it.
For anyone else finding this question via Google: the method_missing thing is a total red herring. The user was confused because in Chef we removed a method_missing-based syntax for node attributes. This is unrelated to my use of it for subresource management.

Related

How should I return Sinatra HTTP errors from inside a class where HALT is not available?

I have a large backend API for my native app that's built in Sinatra, that also serves some admin web pages. I'm trying to dry up the codebase and refactor code into classes inside the lib directory.
My API clients expect a status and a message, such as 200 OK, or 404 Profile Not Found. I'd usually do this with something like halt 404, 'Profile Not Found'.
What's the easiest way of halting with an HTTP status code and a message from inside a class?
Old Wet Code
post '/api/process_something'
halt 403, 'missing profile_id' unless params[:profile_id].present?
halt 404, 'offer not found' unless params[:offer_id].present?
do_some_processing
200
end
New Dry Code
post '/api/process_something'
offer_manager = OfferManager.new
offer_manager.process_offer(params: params)
end
offer_manager.rb
class OfferManager
def process_offer(params:)
# halt 403, 'missing profile_id' unless params[:profile_id].present?
# halt 404, 'offer not found' unless params[:offer_id].present?
# halt doesn't work from in here
do_some_processing
200
end
end
This question is probably better for CodeReview but one approach you can see in an OO design here is a 'halt' path and a 'happy' path. Your class just needs to implement a few methods to help this be consistent across all your sinatra routes and methods.
Here's one approach, and it would be easy to adopt this kind of interface across other classes using inheritance.
post '/api/process_something' do
offer_manager = OfferManager.new(params)
# error guard clause
halt offer_manager.status, offer_manager.halt_message if offer_manager.halt?
# validations met, continue to process
offer_manager.process_offer
# return back 200
offer_manager.status
end
class OfferManager
attr_reader :status, :params, :halt_message
def initialize(params)
#params = params
validate_params
end
def process_offer
do_some_processing
end
def halt?
# right now we just know missing params is one error to halt on but this is where
# you could implement more business logic if need be
missing_params?
end
private
def validate_params
if missing_params?
#status = 404
#halt_message = "missing #{missing_keys.join(", ")} key(s)"
else
#status = 200
end
end
def do_some_processing
# go do other processing
end
def missing_params?
missing_keys.size > 0
end
def missing_keys
expected_keys = [:profile_id, :offer_id]
params.select { |k, _| !expected_keys.has_key?(k) }
end
end

Ruby: exception wrongly raised when testing the creation of a directory

I have a module named FileSystem in my app, which executes basic filesystem functionality. Here is the relevant code of it.
module TxtDB
module FileSystem
def self.create_database(db)
fpdb = db_full_path(db)
Dir.mkdir(fpdb) unless ((not valid_parameter?(db)) or (not valid_database?(fpdb)))
end
private
def self.valid_parameter?(db)
raise TxtDB::NIL_PARAMETER_ERROR unless (not db == nil)
raise TxtDB::NOT_A_STRING_ERROR unless (db.is_a? String)
raise TxtDB::EMPTY_PARAMETER_ERROR unless (not db.empty?)
true
end
def self.valid_database?(db)
raise TxtDB::DATABASE_ALREADY_EXISTS_ERROR unless (not Dir.exist?(db_full_path(db)))
true
end
def self.db_full_path(db)
"#{TxtDB::BASE_DIRECTORY}/#{db}"
end
end
end
And this is my Rspec test for this feature
it 'raises a StandardError (Database already exists) if it receives the name of an existing database' do
base_path = TxtDB::BASE_DIRECTORY
if (not Dir.exist?(base_path)) then
Dir.mkdir(base_path)
end
db_path = File.join(TxtDB::BASE_DIRECTORY,'testedb')
if (not Dir.exist?(db_path)) then
Dir.mkdir(db_path)
end
expect {
TxtDB::FileSystem::create_database('testedb')
}.to raise_error(StandardError, TxtDB::DATABASE_ALREADY_EXISTS_ERROR)
end
It happens that when I run my tests I got this error
expected StandardError with "Database already exists", got #<Errno::EEXIST: File exists # dir_s_mkdir - txtdb/testedb>
As I see things, this should not happen, since I'm testing for the existence before calling Dir.mkdir. But I'm obviously wrong, since the error occurs. The question is: Where am I wrong?
==========
According to the suggestion of Peter Alfvin (see answer below), I change my method to
def self.create_database(db)
fpdb = db_full_path(db)
if (valid_parameter?(db) and valid_database?(fpdb)) then
Dir.mkdir(fpdb)
end
end
Now there is no doubt the validations are done beforehand. But I still get the same error.
Dir.exists?(path) returns true if path is a directory, otherwise it returns false. In valid_database?, you are passing it the full path name, which I would guess is pointing to a non-directory file.
See http://ruby-doc.org/core-2.1.2/Dir.html#method-c-exists-3F

Ruby Factory Method - what is wrong?

So I copied this code from a youtube video https://www.youtube.com/watch?v=V9OySOWLYIg
He ran it in his video no problem, but when I run it, it gives me an error
C:/rails/11.rb:17:in create': Unknown hero (RuntimeError)
from C:/rails/11.rb:6:inblock in initialize'
from C:/rails/11.rb:6:in times'
from C:/rails/11.rb:6:ininitialize'
from C:/rails/11.rb:22:in new'
from C:/rails/11.rb:22:in'
class Party
attr_reader :members
def initialize(number, occupation)
#members = []
number.times { members << create(occupation)}
end
end
class PartyFactory < Party
def create(occupation)
if occupation == :warrior
Warrior.new
elseif occupation == :mage
Mage.new
else
raise "Unknown hero"
end
end
end
party = PartyFactory.new(2, :mage)
Another question I have is what if intead of Mage.new , I do Mage.new("fred"), to set the name for mage, where does the "fred" part end up?
Sorry, I am very new to Ruby and can not find a working example to understand how to set up factory methods.
Change elseif to elsif (without the second e).
Then make sure to initialize Mage and Warrior classes as you'll get a NameError if you don't.

How to rescue all exceptions under a certain namespace?

Is there a way to rescue all exceptions under a certain namespace?
For example, I want to rescue all of the Errno::* exceptions (Errno::ECONNRESET, Errno::ETIMEDOUT). I can go ahead and list them all out on my exception line, but I was wondering if I can do something like.
begin
# my code
rescue Errno
# handle exception
end
The above idea doesn't seem to work, thus is there something similar that can work?
All the Errno exceptions subclass SystemCallError:
Module Errno is created dynamically to map these operating system errors to Ruby classes, with each error number generating its own subclass of SystemCallError. As the subclass is created in module Errno, its name will start Errno::.
So you could trap SystemCallError and then do a simple name check:
rescue SystemCallError => e
raise e if(e.class.name.start_with?('Errno::'))
# do your thing...
end
Here is another interesting alternative. Can be adapted to what you want.
Pasting most interesting part:
def match_message(regexp)
lambda{ |error| regexp === error.message }
end
begin
raise StandardError, "Error message about a socket."
rescue match_message(/socket/) => error
puts "Error #{error} matches /socket/; ignored."
end
See the original site for ruby 1.8.7 solution.
It turns out lambda not accepted my more recent ruby versions. It seems the option is to use what worked in 1.8.7 but that's IM slower (to create a new class in all comparisons. So I don't recommend using it and have not even tried it:
def exceptions_matching(&block)
Class.new do
def self.===(other)
#block.call(other)
end
end.tap do |c|
c.instance_variable_set(:#block, block)
end
end
begin
raise "FOOBAR: We're all doomed!"
rescue exceptions_matching { |e| e.message =~ /^FOOBAR/ }
puts "rescued!"
end
If somebody knows when ruby removed lambda support in rescue please comment.
All classes under Errno are subclasses of SystemCallError. And all subclasses of SystemCallError are classes under Errno. The 2 sets are identical, so just rescue SystemCallError. This assumes that you're not using an external lib that adds to one and not the other.
Verify the identity of the 2 sets (using active_support):
Errno.constants.map {|name|
Errno.const_get(name)
}.select{|const|
Class === const
}.uniq.map(&:to_s).sort ==
SystemCallError.subclasses.map(&:to_s).sort
This returns true for me.
So, applied to your example:
begin
# my code
rescue SystemCallError
# handle exception
end
Here is a more generic solution, in the case you wanted to rescue some Errno types and not others.
Create a custom module to be included by all the error classes we want to rescue
module MyErrnoModule; end
Customize this array to your liking, up to the "each" call.
Errno.constants.map {|name|
Errno.const_get(name)
}.select{|const|
Class === const
}.uniq.each {|klass|
klass.class_eval {
include MyErrnoModule
}
}
Test:
begin
raise Errno::EPERM
rescue MyErrnoModule
p "rescued #{$!.inspect}"
end
Test result:
"rescued #<Errno::EPERM: Operation not permitted>"
I would guess this performs slightly better than a solution that needs to check the name of the exception.

Ruby/Rails: accessing a variable inside a .each on my instance variable array causing Ruby interpreter to crash

I've written a mixin (based on something I read in a blog) that seems to be causing a problem
Here is a link to the project: http://www.filehosting.org/file/details/263759/onlinescheduler.zip (or send me an email: aaron.a.ashworth#gmail.com and I'll email it) and I've stripped as much out as I can for it to still cause the problem. The key files to look at are:
/lib/user_role.rb (near line 11)
/app/views/customers/index.html.erb (near line 16)
/app/controllers/customers_controller.rb (near line 47)
I'll layout the important stuff here as well:
/lib/user_role.rb:
module UserRole
def self.included(base)
base.has_one :user, :as => :user_role, :autosave => true
base.validate :user_must_be_valid
base.alias_method_chain :user, :autobuild
base.extend ClassMethods
base.define_user_accessors
end
def user_with_autobuild
user_without_autobuild || build_user
end
def method_missing(meth, *args, &blk)
user.send(meth, *args, &blk)
rescue NoMethodError
super
end
module ClassMethods
def define_user_accessors
all_attributes = User.content_columns.map(&:name) + ["password", "password_confirmation"]
ignored_attributes = ["created_at", "updated_at", "user_role_type"]
attributes_to_delegate = all_attributes - ignored_attributes
attributes_to_delegate.each do |attrib|
class_eval <<-RUBY
def #{attrib}
user.#{attrib}
end
def #{attrib}=(value)
self.user.#{attrib} = value
end
def #{attrib}?
self.user.#{attrib}?
end
RUBY
end
end
end
protected
def user_must_be_valid
Logger.new(STDOUT).info('calling user_must_be_valid')
unless user.valid?
user.errors.each do |attr, message|
errors.add(attr, message)
end
end
end
end
app/views/customers/index.html.erb:
...
<% #customers.each do |customer| %>
<tr>
<td><%= customer.account_id %></td>
...
accessing customer at all causes the problem. I can do anything to #customers
but as soon as I try to access customer.... or even #customers[0].... I get a problem.
Steps to produce
1) After unzipping the file, go into the root directory in terminal and run these commands:
bundle install
bundle exec rake db:drop
bundle exec rake db:migrate
rails s
2) Open your browser to localhost:3000/customers and click New Customer
3) Fill in the form you see like so:
Account: 3
First Name: First
Last Name: Last
Email: first.last#domain.com
Password: 1234
Password confirmation: 1234
4) Click the Create Customer button.
Expected Behavior
You should be redirected to localhost:3000/customers/1
Current Behaviour
The webserver crashes as you get the following message:
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(+0x17b356) [0x7fef4a97e356]
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(+0x1713ee) [0x7fef4a9743ee]
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(+0x177243) [0x7fef4a97a243]
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(rb_vm_invoke_proc+0x9f) [0x7fef4a97b08f]
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(+0x17b644) [0x7fef4a97e644]
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(+0x1713ee) [0x7fef4a9743ee]
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(+0x177243) [0x7fef4a97a243]
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(+0x1784f4) [0x7fef4a97b4f4]
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(+0x178cb5) [0x7fef4a97bcb5]
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(+0x17b50d) [0x7fef4a97e50d]
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(+0x1713ee) [0x7fef4a9743ee]
[NOTE]
You may have encountered a bug in the Ruby interpreter or extension libraries.
Bug reports are welcome.
For details: http://www.ruby-lang.org/bugreport.html
Rerunning the webserver and going to localhost:3000/customers sometimes gives you a different error. A segmentation fault and it complains about /lib/user_role.rb:11
Environment
Ruby 1.9.2-p290
Rails 3.0.9
RVM 1.8.1
Ubuntu 11.04
Edit
Something to note: If you try working the same code that bombs in console it seems fine. Example:
(After entering rails c)
#customers = Customer.all
#customers.each do |customer|
p customer.account_id
end
# this doesn't cause an error or crash.
#customer[0].first_name
=> "First"
If you remove:
def method_missing(meth, *args, &blk)
customer.send(meth, *args, &blk)
rescue NoMethodError
super
end
and
def method_missing(meth, *args, &blk)
user.send(meth, *args, &blk)
rescue NoMethodError
super
end
from the x_role files in your lib directory, it should work fine. On a side note look into inherited resource for your controllers and simple form for your forms.

Resources