Make a route_param embedded into a resource a string - ruby

I am trying to make a route parameter embedded inside a resource as a string.
class Example < Grape::API
resource 'a/:param/b' do
# code
end
end
One way I could think of is by splitting the resource into route_param blocks and make it a string but this will introduce a lot of nesting which would defeat the purpose of the resource having an embedded route_param and thereby preventing the nesting.
class Example < Grape::API
resource 'a' do
route_param :param, type: String do
resource 'b' do
# code
end
end
end
end
Another way I could think of is by explicitly defining in a param block which would potentially duplicate a lot of code.
class Example < Grape::API
resource 'a/:param/b' do
params do
requires :param,
type: String
end
# code
end
end
Is anybody aware of a different way? Thanks!

Related

Undefined method for class - Sinatra

It seems I don't quite understand initializing or using a class within another class.
I have a Sinatra app and have created a class to handle fetching data from an api
# path: ./lib/api/bikes/bike_check
class BikeCheck
def self.check_frame_number(argument)
# logic here
end
end
BikeCheck.new
I then have another class that needs to consume/use the check_frame_number method
require 'slack-ruby-bot'
# Class that calls BikeCheck api
require './lib/api/bikes/bike_check'
class BikeCommands < SlackRubyBot::Bot
match /^Is this bike stolen (?<frame_number>\w*)\?$/ do |client, data, match|
check_frame_number(match[:frame_number])
client.say(channel: data.channel, text: #message)
end
end
BikeCommands.run
When check_frame_number is called I get a undefined method error. What I would like to know is what basic thing am I not doing/understanding, I thought by requiring the file which has the class it would be available to use.
No, you can not require a method defined in class - methods defined in class only available to class, class instances and within the inheritance.
Mixing method only possible with including modules.
To solve you issue you could either do
class BikeCommands < SlackRubyBot::Bot
match /^Is this bike stolen (?<frame_number>\w*)\?$/ do |client, data, match|
BikeCheck.check_frame_number(match[:frame_number]) # <===========
client.say(channel: data.channel, text: #message)
end
end
or write a module with the method and include/extend in class, you want that method to be available in.

How to write regression tests for custom Chef resources?

Given the minimal example
# resources/novowel.rb
resource_name :novowel
property :name, String, name_property: true, regex: /\A[^aeiou]\z/
I would like to write the unit tests in spec/unit/resources/novowel_spec.rb
Resource 'novowel' for name should accept 'k'
Resource 'novowel' for name should accept '&'
Resource 'novowel' for name should NOT accept 'a'
Resource 'novowel' for name should NOT accept 'mm'
to ensure that the name property still works correctly even if the regex is changed for some reason.
I browsed several top notch Chef cookbooks but could not find references for such testing.
How can it be done? Feel free to provide more complex examples with explicitly subclassing Chef::Resource if that helps achieve the task.
Update 1: Could it be that Chef does NOT FAIL when a property does not fit the regex? Clearly this should not work:
link '/none' do
owner 'r<oo=t'
to '/usr'
end
but chef-apply (12.13.37) does not complain about r<oo=t not matching owner_valid_regex. It simply converges as if owner would not have been provided.
You would use ChefSpec and RSpec. I've got examples in all of my cookbooks (ex. https://github.com/poise/poise-python/tree/master/test/spec/resources) but I also use a bunch of custom helpers on top of plain ChefSpec so it might not be super helpful. Doing in-line recipe code blocks in the specs makes it waaaay easier. I've started extracting my helpers out for external use in https://github.com/poise/poise-spec but it's not finished. The current helpers are in my Halite gem, see the readme there for more info.
We wrap the DSL inside a little Ruby in order to know the name of the resource's Ruby class:
# libraries/no_vowel_resource.rb
require 'chef/resource'
class Chef
class Resource
class NoVowel < Chef::Resource
resource_name :novowel
property :letter, String, name_property: true, regex: /\A[^aeiou]\z/
property :author, String, regex: /\A[^aeiou]+\z/
end
end
end
and now we can use RSpec with
# spec/unit/libraries/no_vowel_resource_spec.rb
require 'spec_helper'
require_relative '../../../libraries/no_vowel_resource.rb'
describe Chef::Resource::NoVowel do
before(:each) do
#resource = described_class.new('k')
end
describe "property 'letter'" do
it "should accept the letter 'k'" do
#resource.letter = 'k'
expect(#resource.letter).to eq('k')
end
it "should accept the character '&'" do
#resource.letter = '&'
expect(#resource.letter).to eq('&')
end
it "should NOT accept the vowel 'a'" do
expect { #resource.letter = 'a' }.to raise_error(Chef::Exceptions::ValidationFailed)
end
it "should NOT accept the word 'mm'" do
expect { #resource.letter = 'mm' }.to raise_error(Chef::Exceptions::ValidationFailed)
end
end
describe "property 'author'" do
it "should accept a String without vowels" do
#resource.author = 'cdrngr'
expect(#resource.author).to eq('cdrngr')
end
it "should NOT accept a String with vowels" do
expect { #resource.author = 'coderanger' }.to raise_error(Chef::Exceptions::ValidationFailed)
end
end
end

Reusable Custom DSL Extension in Grape

I have a custom Grape DSL method called scope that remembers the parameter then delegates some work to the params helper.
class API::V1::Walruses < Grape::API
resources :walruses do
scope :with_tusks, type: Integer,
desc: "Return walruses with the exact number of tusks"
scope :by_name, type: String,
desc: "Return walruses whose name matches the supplied parameter"
get do
# Equivalent to `Walrus.with_tusks(params[:with_tusks]).
# by_name(params[:by_name])`, but only calls scopes whose
# corresponding param was passed in.
apply_scopes(Walrus)
end
end
end
This works and is clean, which I like. The problem is that many child endpoints also want to use those same scopes. This means maintaining them in multiple places and keeping them all in sync.
I would like to do the following instead:
class API::V1::Walruses < Grape::API
helpers do
scopes :index do
scope :with_tusks, type: Integer,
desc: "Return walruses with the exact number of tusks"
scope :by_name, type: String,
desc: "Return walruses whose name matches the supplied parameter"
end
end
resources :walruses do
use_scopes :index
#...
end
end
Then API::V1::Walruses will have those scopes available, as will API::V1::Walruses::Analytics (if included), and any others that are nested within.
Params have a similar methodology but I've had trouble understanding how to inherit settings.

Alternative initialize for a Class to avoid processing already known information

I have a class, Autodrop, that contains several methods , a.o. 'metadata', that call an external API (dropbox). They are slow.
However, I already often have that metadata around when initializing the AutodropImage, so I should make the methods smarter.
What I have in mind is this:
class Autodrop
include Dropbox
attr_reader :path
def initialize(path)
#path = path
end
def self.from_entry(drop_entry)
#drop_entry = drop_entry
self.initialize(#drop_entry.path)
end
def metadata
if #drop_entry = nil
return heavy_lifting_and_network_traffic
else
return #drop_entry.metadata
end
end
#...
end
Now, I would expect to call
entry = BarEntry.new()
foo = Autodrop.from_entry(entry)
foo.metadata
In order to avoid that heavy lifting and network traffic call.
But this does not work. And somehow, in all my newbieness, I am sure I am goind at this all wrong.
Is there a term I should look for and read about first? How would you go for this?
Note, that the examples are simplified: in my code, I inherit AutodropImage < Autodrop for example, which is called from withing AutodropGallery < Autodrop. The latter already knows all metadata for the AutodropImage, so I mostly want to avoid AutodropImage going over the heavy lifting again.
You are creating an instance variable #drop_entry in your class method from_entry and obviously it wont be available to your object that you are creating in this method. One workaround is to pass it as a parameter when you are initializing the class. It should work if you do the following modifications:
In your from_entry class method change
self.initialize(#drop_entry)
to
new(#drop_entry)
Modify initialize method to:
def initialize(drop_entry)
#drop_entry = drop_entry
#path = #drop_entry.path
end
Or if your class is tied up to pass only the path parameter, ie. you dont want to change the other existing code then you can use an optional parameter drop entry like so
def initialize(path, drop_entry=nil)
You would need to cache the metadata in a class variable.
Edit: Or in a class level instance variable.
Maybe this read will help: http://railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/

Implementing an ActiveRecord before_find

I am building a search with the keywords cached in a table. Before a user-inputted keyword is looked up in the table, it is normalized. For example, some punctuation like '-' is removed and the casing is standardized. The normalized keyword is then used to find fetch the search results.
I am currently handling the normalization in the controller with a before_filter. I was wondering if there was a way to do this in the model instead. Something conceptually like a "before_find" callback would work although that wouldn't make sense on for an instance level.
You should be using named scopes:
class Whatever < ActiveRecord::Base
named_scope :search, lambda {|*keywords|
{:conditions => {:keyword => normalize_keywords(keywords)}}}
def self.normalize_keywords(keywords)
# Work your magic here
end
end
Using named scopes will allow you to chain with other scopes, and is really the way to go using Rails 3.
You probably don't want to implement this by overriding find. Overriding something like find will probably be a headache down the line.
You could create a class method that does what you need however, something like:
class MyTable < ActiveRecord::Base
def self.find_using_dirty_keywords(*args)
#Cleanup input
#Call to actual find
end
end
If you really want to overload find you can do it this way:
As an example:
class MyTable < ActiveRecord::Base
def self.find(*args)
#work your magic here
super(args,you,want,to,pass)
end
end
For more info on subclassing checkout this link: Ruby Tips
much like the above, you can also use an alias_method_chain.
class YourModel < ActiveRecord::Base
class << self
def find_with_condition_cleansing(*args)
#modify your args
find_without_condition_cleansing(*args)
end
alias_method_chain :find, :condition_cleansing
end
end

Resources