Loading attributes only once in Grape - ruby

I'm little new to Ruby, but I have the following coded using Grape API in Ruby. I have #data = YAML.load() called every time it hits GET /api/v1/foo, is there a way in Grape to only load it once and use it? This way is more optimized and not calling YAML.load() at every time. Should I override the initialize method and put a super() for this operation?
Thanks,
require 'grape'
require 'json'
require "yaml"
module MyProject
CONFIG_FILE = "./config.yml"
class Api < Grape::API
rescue_from :all
prefix 'api'
version 'v1'
format :json
resources :foo do
get do
#data = YAML.load(File.open(MyProject::CONFIG_FILE))
end
end
end
end

The short answer is that Grape doesn't work quite how you think, and attribute variables of the MyProject::Api are not the way forward for your new web service. However, it is an interesting question, and worth exploring why this is so.
If you add a puts self.inspect inside the resources :foo block, and run using rackup, when you call the route you should see that self is in fact a Grape::Endpoint object. Also, no matter what you try to do with instance variables, they will always start in the same state for each request. That is because Grape turns your route definitions into prepared Grape::Endpoint objects, with a lot of the definition data and setup put into a quickly-accessible form (so that it is not figured out on each request). Eventually, on each request, the matching Grape::Endpoint object including your block (and other details you defined for the route) is duplicated before being called, meaning that state is not maintained between requests.
This may seem complicated, but most frameworks covering web service requests will do something similar. Generally you don't want request-handling state to persist between requests. Frameworks with larger scope - e.g. Rails - have places to put more persistent data planned out for you. Grape does not have this defined, which has its pros and cons. One obvious plus point is that you are more free to use whatever other data persistence approach that you wish to.
23tux's answer will sort you out immediately for loading config. Although I'm not entirely sure how ##data becomes accessible to the endpoint block (it may even be creating a closure around the variable).
Longer term, you should look to moving config management out of your MyProject::Api class, and including it as a module via Grape's helpers method (I'm happy to provide an example if you are interested).
Edit: Example based on your current code, but moving config management to a separate module:
require 'grape'
require 'json'
require "yaml"
module MyProject
module Config
CONFIG_FILE = "./config.yml"
##data = nil
def config
##data ||= YAML.load( File.open( CONFIG_FILE ) )
end
end
class Api < Grape::API
rescue_from :all
prefix 'api'
version 'v1'
format :json
helpers MyProject::Config
resources :foo do
get do
config
end
end
end
end
This is one step further, structurally, than 23tux's answer, but is still not completely separating concerns of storage (and caching etc) versus api access. As you progress towards a more sophisticated web service, you will want to keep the Grape route definitions simple, with only a small amount of logic that manages or manipulates the data - well, at least as seen directly in the blocks.
One way to link between your Grape definitions, and other gems that might manage config, logging, authentication and other services, is via Grape's helpers method. Grape also has some built-in helper methods for common tasks.
The main exception to using helpers MyModule to add shared functions into Grape is when you want to manage displaying data objects (aka "models") from your core application. For that you have a few choices, but the grape-entity gem and the present method is not a bad place to start.

If the #data is the same for the whole api, and doesn't change at any time, just use a class variable
require 'grape'
require 'json'
require "yaml"
module MyProject
CONFIG_FILE = "./config.yml"
class Api < Grape::API
##data = YAML.load(File.open(MyProject::CONFIG_FILE))
rescue_from :all
prefix 'api'
version 'v1'
format :json
resources :foo do
get do
puts ##data
end
end
end
end
Not tested, but with this way, you ensure that the data is only loaded once, when your Api class is loaded

Related

Nesting modules in classes and directly calling module functions

For testing and administration purposes I am looking to build a class to communicate with an API. I've got the connection and authentication down but am struggling with the base structure and size of the class.
My main goal is to keep each application domain split, but still easy to access by one class/connection.
I've made an simpler example of what I'm looking for. In reality each domain has its own set of business rules to follow, which is why I want to keep them separate, whilst the API connection stays the same.
For instance, on CLI level I want to invoke:
$ client_one = Api.new("one")
$ client_two = Api.new("two")
$ client_one.Bikes.delete(1)
> deleted bike 1 from one
$ client_two.Phones.new(phone)
> posted phone iPhone to two
My thought proces was to nest modules inside an Api class but I can't get it to work or find the right syntax.
class Api
def initialize(client)
#client = client
#connection = Authentication.get_connection(#client)
end
#preferable put each submodule in a separate file
module Authentication
def get_connection(client)
#code to get Faraday connection
end
end
module Bikes
def new(object)
#code to post new bike
#connection.post(object)
puts "posted bike #{object.name} to #{#client}"
end
def delete(id)
#code to delete old bike
#connection.delete(id)
puts "deleted bike #{id} from #{#client}"
end
end
module Phones
def new(object)
#code to post new phone
#connection.post(object)
puts "posted phone #{object.name} to #{#client}"
end
end
end
This results in errors like:
NoMethodError: undefined method `Bikes' for #<Api:0x0000000003a543a0>
Is it possible to achieve my goal or are there better 'Ruby' ways to accomplish it?
Furthermore, is it possible to split the submodules to different files? eg:
api.rb
modules
+ -- authentication.rb
+ -- bikes.rb
+ -- phones.rb
There are some fundamental misconceptions of how Ruby OOP works in your example, and without a full code sample and the opportunity to interrogate you about what you're trying to accomplish it's hard to guide you to what might be the most appropriate answer. Any answer I give will be based partly on experience and partly on opinion, so you may see other answers as well.
At a high level, you should have classes in modules and not modules in classes. Although you can put modules in classes you better have a good understanding of why you're doing that before doing it.
Next, the modules and methods you've defined in them do not automatically become accessible to instances of the parent class, so client.Bikes will never work because Ruby expects to find an instance method named Bikes inside the Api class; it won't look for a module with that name.
The only way to access the modules and module methods that you have defined is to use them at the class/module level. So if you have this:
class Foo
module Bar
def baz
puts 'foobarbaz'
end
end
end
You can do this at the class/module level:
Foo::Bar.baz
foobarbaz
=> nil
But you can't do anything at the instance level:
Foo.new::Bar.baz
TypeError: #<Foo:0x00007fa037d39260> is not a class/module
Foo.new.Bar.baz
NoMethodError: undefined method `Bar' for #<Foo:0x00007fa037162e28>
So if you understand so far why the structure of your example doesn't work, then you can work on building something a little more sensible. Let's start with naming and the class/module structure.
First, Api is a poor name here because you'll typically use Api for something that provides an API, not connects to one, so I would recommend making the name a bit more descriptive and using a module to indicate that you are encapsulating one or more related classes:
module MonthyApiClient
end
Next, I'd recommend adding a Client class to encapsulate everything related to instantiating a client used to connect to the API:
module MonthyApiClient
class Client
def initialize
#client = nil # insert your logic here
#connection = nil # insert your logic here
end
end
end
The relationship between client and connection in your code example isn't clear, so for simplicity I am going to pretend that they can be combined into a single class (Client) and that we are dropping the module Authentication entirely.
Next, we need a reasonable way to integrate module Bikes and module Phones into this code. It doesn't make sense to convert these to classes because there's no need to instantiate them. These are purely helper functions that do something for an instance of Client, so they should be instance methods within that class:
module MonthyApiClient
class Client
def initialize
# insert your logic here
#client = nil
#connection = nil
end
def create_bike
# insert your logic here
# e.g., #connection.post(something)
end
def delete_bike
# insert your logic here
# e.g., #connection.delete(something)
end
def create_phone
# insert your logic here
# e.g., #connection.post(something)
end
end
end
Note that we've swapped new for create; you don't want to name a method new in Ruby, and in the context we're using this new would mean instantiate but do not save a new object whereas create would mean instantiate and save a new object.
And now that we're here, and now that we've eliminated all the nested modules by moving their logic elsewhere, we can see that the parent module we set up originally is unnecessarily redundant, and can eliminate it:
class MonthyApiClient
def initialize
# insert your logic here
#client = nil
#connection = nil
end
def create_bike
# insert your logic here
# e.g., #connection.post(something)
end
def delete_bike
# insert your logic here
# e.g., #connection.delete(something)
end
def create_phone
# insert your logic here
# e.g., #connection.post(something)
end
end
Then you can accomplish your original goal:
client_one = MonthyApiClient.new
client_one.create_bike
client_two = MonthyApiClient.new
client_two.create_phone
Having worked through this explanation, I think your original code is an example of spending a lot of time trying to over-optimize prematurely. It's better to plan out your business logic and make it as simple as possible first. There's some good information at https://softwareengineering.stackexchange.com/a/80094 that may help explain this concept.
I've even skipped trying to optimize the code I've shown here because I don't know exactly how much commonality there is between creating and deleting bikes and phones. With this functional class, and with a better understanding of other code within this app, I might try to DRY it up (and that might mean going back to having a module with a Client class and either module methods or other classes to encapsulate the DRY logic), but it would be premature to try.
Your last question was about how to structure files and directories for modules and classes, and I would refer you to Ideal ruby project structure (among many other questions on this site) for more information.

Want help regarding 'module' usage in ruby project (using 3rd party gem of 'ruby-jmeter'

I am trying to create performance automation framework for my company . Beinbg newbie to ruby field , I thought to keep it simple . Here is structure for performance framewrok
I do have multiple ruby files , as request.rb,payload.rb etc in 'common' folder(as these are containing some utilities) and then my test in test.rb (under one of 'TestFlows->SimpleFlow->test.rb) .
See above structure for more detail
Exact Code , which I am having right now under those files are
request.rb
require 'ruby-jmeter' #(Consider any 3rd party gem )
require 'rubygems'
module Company #Note that i am using same module name 'company'
module Request
def self.send_request()
visit '192.148.1.2' # this is function defined under ruby-jmeter
end
end
end
payload.rb
require 'ruby-jmeter' #(Consider any 3rd party gem )
require 'rubygems'
module Company #Note that i am using same module 'company'
module Payload
def self.get_payload()
------- Again some 'ruby-jmeter' function calls
end
end
end
etc files as well
Test.rb
require 'ruby-jmeter' #(Consider any 3rd party gem )
require 'rubygems'
require 'require_all' #(gem to load all files from folder)
require_all '../../common/'
module Company #Note that i am using same module 'company'
test name:'GooglePerformanceTest' do
defaults domain: 'http://google.com' ,protocol: http
threads name: 'NoOfUsers',scheduler: false,continue_forever:
false, count: 2 do
Request.send_request()
end
end #end of testPlan
view_results_tree
puts "JMX FILE IS GONNA SAVED #
"+Dir.pwd+"/CreatedJMeter_DB2Navigation.jmx"
end.jmx(file: Dir.pwd+"/CreatedJMeter_DB2Navigation.jmx")
end
When I run this programme , it goes to above , I am getting errors of those (3rd party ruby gem's function undefined).
Can anyone point me out my problem regarding above structure & suggest me proper way to do this ?
edit :
I am getting below error
Test.rb:3:in `send_request': undefined method `visit ' for
company::Request:Module (NoMethodError)
Edit
Have defined my changes here & it is working fine
Facing issues while calling method of module(having object of class , contained in another ruby file)
I'm assuming you're using the gem hosted at this repository: https://github.com/flood-io/ruby-jmeter, latest release version (2.13.8).
Looking through the source code and examples, there are a couple things to think about.
The 'visit' method is an alias for a method called 'get' defined here
.
That is an object method, for an object of class RubyJmeter::ExtendedDSL. You are using module methods, and should probably consider making an object and calling the method on that.
obj = RubyJmeter::ExtendedDSL.new
obj.visit '192.148.1.2' # 'visit' is a method alias for 'get'
However, the examples listed by the gem developer follow a different pattern, calling 'visit' from within a closure (the do..end code block, known simply as a 'block' in Ruby). You could rewrite your code to build test plans and run them in this fashion.
It's not clear from what you post why you are using modules. Typically, you use 'include' within a Module or Class to mix in the gem's methods into your namespace. But since you're attempting to use module methods, I'm not sure mixing in object methods will be fruitful.
Since I can't see how your Test.rb file is run, I can't say for sure that your test code is loading your own modules. Typically a 'load' or 'include' statement would be used to load the code you wrote in request.rb and payload.rb
Personally, I'd try to follow the pattern shown by the developer of RubyJmeter; as a second approach, I'd write a class that inherits from RubyJemeter's subclass and extend its behavior to suit. My test code would initialize an object and call its methods directly. (Your Mileage May Vary.)
Updated to add: if you want your module method to stand in place of the object method mentioned, you can simply call the latter inside your method:
def self.send_request()
RubyJmeter::ExtendedDSL.new.visit '192.148.1.2'
end
Doing this essentially creates a disposable object, which is destroyed after the :visit method returns its data.
P.S. Always capitalize your class and module names. It's a Ruby best practice and your module name 'company' will raise warnings from the interpreter.

sinatra request scope like php

I'm not sure this is the best topic name I could use but is there a way in sinatra+rack to use classes/modules like in php+apache way? I mean is there a way you can use Singleton or any other static typed variables in just request scope? What I need(would want to) is:
class Config # might be just module
# some methods that are request scoped
# cant be shared between users/reuqest
def server_settings
# possible server dependent settings
# can be shared between requests on same server
end
end
class Something
def do_thing
# I would like to access Config class here without passing it as parameter
# setted up for the current request
end
end
class MyController < Sinatra::Base
# request handling, creates Config class, setting up request parameters once
def handle_it
Something.new.do_thing
end
end
No rails here and I'm very limited on things I can do / gems I can use. MyController comes from internal framework. I can ofc extend it though. Php+apache goes from nothing every request so all your code is request only unless you use sessions/cookies.
The reason why I would like to use this is to avoid passing config instance(or any other kind of data repository) into every other instance as parameter and simply access it "globaly".
I think you looking for something like this answer Page Views counter sinatra?
tl;dr version is using set
set :my_config_property, 'hello world'

Is there a way in Sinatra to separate helpers used in views and helpers used in routes?

All helpers in Sinatra can be accessed from both my HAML code and my routes. But I'm finding myself writing two completely separate sets of code for these two.
Ideally I'd like to keep them apart. Currently I have them in two different directories, but that seems kind of pointless. Can anyone suggest a way I can limit the potential damage caused by namespace collision?
At first I thought modules. From the Sinatra readme:
module FooUtils
def foo(name) "#{name}foo" end
end
module BarUtils
def bar(name) "#{name}bar" end
end
helpers FooUtils, BarUtils
But is there any point in actually doing this? Aren't they going to have to share the same namespace inside my Sinatra app?
Some tentative and not-entirely-satisfactory answers:
1) Methods defined in the same way as helpers but without the call to Sinatra::helpers don't appear to be accessible from views, but are accessible from within the Sinatra application block. That is:
require 'sinatra'
module Sinatra
module MyThing
# Helper classes and methods go here
end
helpers MyThing # <- leave this out if you don't want access from views
end
2) You can of course also have MyThingHelpers and MyThingLib modules, instead of just MyThing, to prevent namespace collision.
3) And you can put them in separate subdirectories.
I've yet to be convinced that these represent a comprehensive solution to my concerns. Time and testing will tell, I suppose, unless someone can give me a better answer here.

How to mount a Sinatra application inside another Sinatra app?

I am trying to write a Sinatra application that groups components together (sort of like controllers). So for the "blog" related things, I want an app called Blog mounted at /blog. All of the routes contained in the Blog app would be relative to its mounted path, so I could simply define an index route without having to specify the mount path in the route.
I originally handled this by using a config.ru file and maping the routes to the different apps. The problem with this that I ran into, was that I was using various sinatra gems/extensions/helpers that needed to be included in all of the apps, so there was a lot of duplicate code.
How can I mount one sinatra app inside of another so that the routes defined in the app are relative to where the app is mounted? If this is not possible out-of-the-box, can you show a code sample of how this could be done?
Here's a simplified example of what it might look like:
class App
mount Blog, at: '/blog'
mount Foo, at: '/bar'
end
class Blog
get '/' do
# index action
end
end
class Foo
get '/' do
# index action
end
end
Take a look at https://stackoverflow.com/a/15699791/335847 which has some ideas about namespacing.
Personally, I would use the config.ru with mapped routes. If you're really in that space between "should this be a separate app or is it just helpful to organize it like this" it allows that, and then later you can still farm off one of the apps on its own without changing the code (or only a little). If you're finding that there's a lot of duplicated set up code, I'd do something like this:
# base_controller.rb
require 'sinatra/base'
require "haml"
# now come some shameless plugs for extensions I maintain :)
require "sinatra/partial"
require "sinatra/exstatic_assets"
module MyAmazingApp
class BaseController < Sinatra::Base
register Sinatra::Partial
register Sinatra::Exstatic
end
class Blog < BaseController
# this gets all the stuff already registered.
end
class Foo < BaseController
# this does too!
end
end
# config.ru
# this is just me being lazy
# it'd add in the /base_controller route too, so you
# may want to change it slightly :)
MyAmazingApp.constants.each do |const|
map "/#{const.name.downcase}" do
run const
end
end
Here's a quote from Sinatra Up and Running:
Not only settings, but every aspect of a Sinatra class will be inherited by its subclasses. This includes defined routes, all the error handlers, extensions, middleware, and so on.
It has some good examples of using this technique (and others). Since I'm in shameless plug mode I recommend it, even though I've nothing to do with it! :)
I just meet the same problems.
I found that whatever inclued extend register or rack middleware style use had problems.
You can use Ruby to solve this problems like that:
require "sinatra"
class App < Sinatra::Base
class << self
def define_routes(&block)
class_eval(&block)
end
end
end
App.define_routes do
get "/hello"
"hello world"
end
end

Resources