Evaluation Error: Unknown function: 'test::sum' - ruby

I'm trying to add a new function to an old puppet module and added a function with the following command.
pdk new function --type=v4 sum
this adds a file lib/puppet/functions/test/sum.rb
# frozen_string_literal: true
# https://github.com/puppetlabs/puppet-specifications/blob/master/language/func-api.md#the-4x-api
Puppet::Functions.create_function(:"test::sum") do
dispatch :sum do
param 'Numeric', :a
return_type 'Numeric'
end
# the function below is called by puppet and and must match
# the name of the puppet function above. You can set your
# required parameters below and puppet will enforce these
# so change x to suit your needs although only one parameter is required
# as defined in the dispatch method.
def sum(x)
x * 2
end
# you can define other helper methods in this code block as well
end
I can also see the function in pdk console when I run the functions command.
but when I try to call the function as follows
test::sum(5) # Evaluation Error: Unknown function: 'test::sum'
test::sum.call(5) # Evaluation Error: Unknown function: 'test::sum'
Deferred(test::sum, [5]).call # Evaluation Error: Unknown function: 'test::sum'
Why it is not working? Am I doing something wrong?
Note: I'm quite new to the puppet.

Related

Ruby syntax error unexpected tLABEL

I have the following configuration for using redis as a rails cache, adapted closely from the rails doc:
Rails.application.configure do
...
config.cache_store = :redis_cache_store, url: "redis://example.com:6379/0"
...
end
Using rails 5.2.0.beta2 and ruby 2.3.1p112, my IDE is telling me the following things about the config.cache_store = line:
unused literal ignored
syntax error, unexpected tLABEL
If I enclose the hash in {}, the errors go away, i.e.:
config.cache_store = :redis_cache_store, { url: "redis://example.com:6379/0" }
Is the documentation in error, or is it a ruby version thing (e.g. maybe the doc is assuming ruby >= 2.4)?
In any case, I don't understand what the assignment is actually doing - how can two things be assigned to one?
When the config block hits cache_store and a symbol is given, it assumes it is a cache store class and does things like:
:redis_cache_store.to_s.classify.constantize
# => RedisCacheStore
which is presumably a class. You can infer that Rails does this by looking at the docs and seeing that you can also pass a class.
RedisCacheStore is defined inside the ActiveSupport::Cache namespace, which inherits from ActiveSupport::Cache::Store, which is defined in cache.rb. This store class only initializes with a single argument of options = nil:
def initialize(options = nil)
#options = options ? options.dup : {}
end
which is indicative of a requirement to pass a hash. For one reason or another, it doesn't initialize with options = {}, which would make your initial code correct. Hence, passing a hash like in your second example resolved the error.

RSpec mocking, `name` not available from within an example group

I have the following Ruby code:
def report_deviation(departure)
deviation = departure.fetch('Dev')
trip = departure.fetch('Trip')
run_id = trip.fetch('RunId')
headsign = trip.fetch('InternetServiceDesc')
timestamp = Time.now.strftime '%l:%M %P'
FileUtils.mkdir 'log' unless File.directory? 'log'
File.open DAILY_LOG_FILE, 'a' do |file|
file.puts "#{timestamp}, #{name}: Run #{run_id} (#{headsign}), deviation #{deviation}"
end
end
Tested by the following RSpec code:
describe 'report_deviation' do
let(:departure) { double }
let(:trip) { double }
let(:file) { double }
it 'appends to a log file with the correct entry format' do
expect(departure).to receive(:fetch).with('Trip').and_return trip
expect(departure).to receive(:fetch).with('Dev').and_return 'DEVIATION'
expect(trip).to receive(:fetch).with('RunId')
.and_return 'RUN'
expect(trip).to receive(:fetch).with('InternetServiceDesc')
.and_return 'HEADSIGN'
stub_const 'DeviationValidator::DAILY_LOG_FILE', :log_file
expect(File).to receive(:open).with(:log_file, 'a').and_yield file
timestamp = '12:00 pm: Run RUN (HEADSIGN), deviation DEVIATION'
expect(file).to receive(:puts).with timestamp
Timecop.freeze(Time.new 2017, 7, 31, 12) { report_deviation(departure) }
end
end
But when I run I receive the failure message:
`name` is not available from within an example (e.g. an `it` block) or from constructs that run in the scope of an example (e.g. `before`, `let`, etc). It is only available on an example group (e.g. a `describe` or `context` block).
The word name isn't written anywhere in here, and if I remove the final line of the test (which invokes the actual code) I get the test failures I would expect for unsatisfied exceptions. I normally would boil my code down to the pieces that are causing the error, but I have no idea what's causing the error.
For what it's worth, the specific line number mentioned in the backtrace is the file.puts within the File.open block - but I don't understand why that should cause a failure. I've set up test doubles such that those objects are nothing special - File receives open and yields file, whose only job is to listen for receiving puts with the string I expect. So what piece of code is calling what happens to be a keyword RSpec method name?
The problem is from rspec gem, if you are using Rails 6 you need to use gem 'rspec-rails', '~> 4.1.0'
name is not a keyword RSpec method, it's a method that report_deviation is trying to call
file.puts "#{timestamp}, #{name}: Run #{run_id} (#{headsign}), deviation #{deviation}"
but the method is not defined.
You need to define the name method in the class where report_deviation is defined. Or, if report_deviation is defined and used in the spec file, add a simple variable called name:
describe 'report_deviation' do
let(:departure) { double }
let(:trip) { double }
let(:file) { double }
let(:name) { "simple name" }
...
`name` is not available from within an example (e.g. an `it` block) [...]
I had a similar problem today. The final solution for the issue for now with a monkeypatch to go back to using method_name.
Create config/initializers/monkeypatches.rb file and fill inside with the following lines.
# config/initializers/monkeypatches.rb
#
# This fixes what seems to be a bug introduced by
# https://github.com/rails/rails/pull/37770
# "Modify ActiveRecord::TestFixtures to not rely on AS::TestCase:"
#
module ActiveRecord::TestFixtures
def run_in_transaction?
use_transactional_tests &&
!self.class.uses_transaction?(method_name) # this monkeypatch changes `name` to `method_name`
end
end
Credits: https://github.com/graphql-devise/graphql_devise/issues/42

How do I call a function in Ruby?

I'm trying to call but I keep getting an error. This is my code:
require 'rubygems'
require 'net/http'
require 'uri'
require 'json'
class AlchemyAPI
#Setup the endpoints
##ENDPOINTS = {}
##ENDPOINTS['taxonomy'] = {}
##ENDPOINTS['taxonomy']['url'] = '/url/URLGetRankedTaxonomy'
##ENDPOINTS['taxonomy']['text'] = '/text/TextGetRankedTaxonomy'
##ENDPOINTS['taxonomy']['html'] = '/html/HTMLGetRankedTaxonomy'
##BASE_URL = 'http://access.alchemyapi.com/calls'
def initialize()
begin
key = File.read('C:\Users\KVadher\Desktop\api_key.txt')
key.strip!
if key.empty?
#The key file should't be blank
puts 'The api_key.txt file appears to be blank, please copy/paste your API key in the file: api_key.txt'
puts 'If you do not have an API Key from AlchemyAPI please register for one at: http://www.alchemyapi.com/api/register.html'
Process.exit(1)
end
if key.length != 40
#Keys should be exactly 40 characters long
puts 'It appears that the key in api_key.txt is invalid. Please make sure the file only includes the API key, and it is the correct one.'
Process.exit(1)
end
#apiKey = key
rescue => err
#The file doesn't exist, so show the message and create the file.
puts 'API Key not found! Please copy/paste your API key into the file: api_key.txt'
puts 'If you do not have an API Key from AlchemyAPI please register for one at: http://www.alchemyapi.com/api/register.html'
#create a blank file to hold the key
File.open("api_key.txt", "w") {}
Process.exit(1)
end
end
# Categorizes the text for a URL, text or HTML.
# For an overview, please refer to: http://www.alchemyapi.com/products/features/text-categorization/
# For the docs, please refer to: http://www.alchemyapi.com/api/taxonomy/
#
# INPUT:
# flavor -> which version of the call, i.e. url, text or html.
# data -> the data to analyze, either the the url, text or html code.
# options -> various parameters that can be used to adjust how the API works, see below for more info on the available options.
#
# Available Options:
# showSourceText -> 0: disabled (default), 1: enabled.
#
# OUTPUT:
# The response, already converted from JSON to a Ruby object.
#
def taxonomy(flavor, data, options = {})
unless ##ENDPOINTS['taxonomy'].key?(flavor)
return { 'status'=>'ERROR', 'statusInfo'=>'Taxonomy info for ' + flavor + ' not available' }
end
#Add the URL encoded data to the options and analyze
options[flavor] = data
return analyze(##ENDPOINTS['taxonomy'][flavor], options)
print
end
**taxonomy(text,"trees",1)**
end
In ** ** I have entered my call. Am I doing something incorrect. The error I receive is:
C:/Users/KVadher/Desktop/testrub:139:in `<class:AlchemyAPI>': undefined local variable or method `text' for AlchemyAPI:Class (NameError)
from C:/Users/KVadher/Desktop/testrub:6:in `<main>'
I feel as though I'm calling as normal and that there is something wrong with the api code itself? Although I may be wrong.
Yes, as jon snow says, the function (method) call must be outside of the class. The methods are defined along with the class.
Also, Options should be a Hash, not a number, as you call options[flavor] = data, which is going to cause you another problem.
I believe maybe you meant to put text in quotes, as that is one of your flavors.
Furthermore, because you declared a class, this is called an instance method, and you must make an instance of the class to use this:
my_instance = AlchemyAPI.new
my_taxonomy = my_instance.taxonomy("text", "trees")
That's enough to get it to work, it seems like you have a ways to go to get this all working though. Good luck!

Multiple Parameters

I have an object that allows users to connect to HDFS. One method allows them to download data, and the other upload data. The initialize method looks something like this:
def initialize(file=nil,set=nil,action)
end
How can I change the way arguments are passed to be more efficient? The action param is required every time, but file and file_set are only required depending on the action. I.e., if they want to upload data they need to pass set, if they want to download data, they just pass file.
Since Ruby 2.0 you can use keyword parameters:
def initialize(action, file: nil,set: nil)
unless file.nil?
# do stuff with file
end
end
So you can call it:
MyClass.new(some_action)
MyClass.new(some_action, set: some_set)
MyClass.new(some_action, file: some_file)
MyClass.new(some_action, set: some_set, file: some_file)
...
First of all you should pass requred parameter first e.g.:
def initialize(action, file = nil, set = nil)
end
Then you may want to use hash to pass optional params:
def initialize(action, options = {})
end
The passing hash is a common way when you need to pass more than one optional parameter.
When you need to pass file, set or both you may call initialize method as follow (assume that this method is defened in the class MyModel):
MyModel.new(action, {file: ''})
MyModel.new(action, {set: ''})
MyModel.new(action, {file: '', set: ''})
Or when you don't want to pass any optional params, simply call:
MyModel.new(action)
In this case you will have empty options hash passed in you initialize method.
Quite common is to use positional parameters when they are mandatory, and an options hash
for the rest. You just need to check action to verify given params are then present:
def initialize(action, opts={})
if action == 'foo'
raise ArgumentError, "requires either :file or :set" unless
([:file, :set] & opts.keys).size == 1
end
...
end

How do I re-pass multiple method arguments in Ruby 1.8.5?

I'm using ruby 1.8.5 and I'd like to use a helper method to help filter a user's preferences like this:
def send_email(user, notification_method_name, *args)
# determine if the user wants this email
return if !user.send("wants_#{notification_method_name}?")
# different email methods have different argument lengths
Notification.send("deliver_#{notification_method_name}", user, *args)
end
This works in ruby 1.8.6, however when I try to do this in 1.8.5 and try to send more than one arg I get an error along the lines of:
wrong number of arguments (2 for X)
where X is the number of arguments that particular method requires. I'd rather not rewrite all my Notification methods - can Ruby 1.8.5 handle this?
A nice solution is to switch to named-arguments using hashes:
def send_email(args)
user = args[:user]
notification_method_name = args[:notify_name]
# determine if the user wants this email
return if !user.send("wants_#{notification_method_name}?")
# different email methods have different argument lengths
Notification.send("deliver_#{notification_method_name}", args)
end
send_email(
:user => 'da user',
:notify_name => 'some_notification_method',
:another_arg => 'foo'
)

Resources