Using Grape to create an API with just Ruby objects instead of a database or Rails - ruby

I am trying to use Grape to create an API using only Ruby objects. I do not want to use a database/Rails/ActiveSupport/etc -- just Rack, Ruby, and Grape.
I have defined a class for a Directory that I want to interact with through the API. So Directory#sort_by("last_name") returns JSON data with a list of People objects in my Directory. I also have a method Directory#create_person(attributes) that takes a string and uses it to add Person objects to the Directory. The directory is populated with people upon creation.
I am new to working with Rack and Grape, so I'm not sure where/how to create my Directory object and have it accessible through the GETs/POSTs in my Grape API class. Using a class variable inside this class appears to work, i.e.,:
module API
class DirectoryAPI < Grape::API
format 'json'
##directory = Directory.new("1.txt", "2.txt", "3.txt")
get 'last_name' do
##directory.sort_by("last_name")
end
end
end
but using class variables just seems wrong. Is there any better/cleaner way to create my Directory object? Perhaps inside my config.ru file? Or could I do it through a class method inside of Directory somehow?

What you are looking for is a singleton:
Usually singletons are used for centralized management of internal or external resources and they provide a global point of access to themselves.
Unfortunately, Ruby just doesn't play well with singletons. But you can use a "class consisting of only class methods," the second strategy advocated in this article.
I believe that you are working on a coding challenge that I completed a few months ago. In my answer, I used a "class consisting of only class methods" called API::Store. Here's the output from rspec -fd:
API::Store
::add
adds record to store
appends data line to file
::has_line?
instantiates a record from the data line
without the record in the store
should equal false
with the record in the store
should equal true
::new
should raise NoMethodError
::records
with original file
on initial access
should eq Records
on subsequent access
should eq Records
when file replaced
should eq OtherRecords
Finished in 0.07199 seconds (files took 2.68 seconds to load)
9 examples, 0 failures
Note that Store can't be instantiated; it throws a NoMethodError if you try. That's not a problem, though. In the Grape endpoint you can call Store.records to access the data.
As for sorting the records, this should be done in another class. Why should a Store or a Directory be sorting the data in its files?
Finally, you asked where to do the initial preparation (not initialization, of course). You can prepare your singleton in config.ru, so that it is ready when the application starts:
# config.ru
# load application code here
file = File.open('data/records.txt', 'a+')
at_exit { file.close }
API::Store.file = file
run API::Base
The challenge's instructions say "You may use any resources you need to complete it," so presumably, asking on Stack Overflow is allowed. If you are doing this challenge for a job application, please do mention so when you ask questions, because it's only fair for those answering to be informed. It would be wise to also mention at your interview that you got help on SO. Good luck, and happy coding.

The main problem I see with your example is not the use of class variables exactly, but instantiating your data inline in the API controller code. Ideally the data should be more self-contained, so you can access the exact same data from other places in your code. If you make an API similar to a light-weight data access module, then you will be using a familiar pattern in your route controllers - also it will become easy to migrate to using SQL or other data store if and when you need to.
There are lots of valid approaches, but I might create a new singleton object to represent your data source, and connect that to your hard-coded data as if it were tables. The end result here would feel a little like using Sequel (but you could follow any other pattern that you prefer):
inline_data.rb
module InlineDB
TABLES = Hash[
:directory => Directory.new("1.txt", "2.txt", "3.txt")
]
def self.[] table_name
TABLES[table_name]
end
end
app.rb
require_relative 'inline_data' # actual path may be more structured
module API
class DirectoryAPI < Grape::API
format 'json'
get 'last_name' do
InlineDB[:directory].sort_by("last_name")
end
end
end

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.

Ruby hash with lazy keys

I have a collection of 'data endpoints'. Each endpoint has a name and can be available or unavailable. In Ruby I want to present the available endpoints as a Hash to make it easy to work with them. The difficulty is that getting information about the endpoints is costly and should be done lazily.
Some examples of how I want my object to behave:
endpoints = get_endpoints.call # No endpoint information is accessed yet
result = endpoints['name1'] # This should only query endpoint "name1"
is_available = endpoints.key? 'name2' # This should only query endpoint "name2"
all_available = endpoints.keys # This has to query all endpoints
The comments describe how the object internally makes requests to the 'data endpoints'.
It is straightforward to make a Hash that can do the first 2 lines. However I don't know how to support the last 2 lines. To do this I need a way to make the keys lazy, not just the values.
Thank you for taking a look!
You'd have to override the key? method, and do your own checking in there.
class LazyHash < Hash
def key?(key)
# Do your checking here. However that looks for your application
end
end
In my opinion, you're asking for trouble though. One of the most powerful virtues in computer science is expectability. If you're changing the behavior of something, modifying it far beyond it's intent, it doesn't serve you to continue calling it by the original name. You don't need to shoe-horn your solution into existing classes/interfaces.
Programming offers you plenty of flexibility, so you can do stuff like this (dependent on the language of course), but in that same argument, you have no reason not to simply build a new object/service with it's own API.
I recommend starting fresh with a new class and building out your desired interface and functionality.
class LazyEndpoints
def on?(name)
end
def set(name, value)
end
end
(Or something like that, the world is yours for the taking!)

In Ruby how to run each method of an object at a particular level in the hierarchy (not the methods it inherits)?

I am collecting tweets using a 'Twitter' gem, gem doc, github page. As it filters tweets according to a criteria, eg:
client.filter(locations: "-122.75,36.8,-121.75,37.8") do |tweet|
puts tweet.text
end
A tweet object is produced from the stream;
(rdb:1) tweet.class
Twitter::Tweet
I can run tweet.methods to get the list of methods available but that includes methods not specific to the tweet api. It includes methods from Object etc. Since I would like to extract from this object all the tweet specific information to see it, is there a way to run all the methods from Class: Twitter::Tweet, from the list of 'Instance Method Summary' and 'Instance Attribute Summary' automatically without having to call each one individually? (I want to make a JSON of all the data in these tweet objects)
and can you explain what this means in terms of the oop paradigm in ruby- "Twitter::Tweet"?
does it mean that the tweet object is part of the Twitter api and Tweet class?
For the first question: you can do it in a little not obvious way. Just ask for own methods of the class:
methods = tweet.class.instance_methods(false)
false parameter is important here, it removes all methods of parent classes. And as a consequence you can run all the methods from the returned list. For example:
methods.each{|method_name| tweet.send(method_name)}
For the second question: Twitter means a main module for all gem related classes, it uses in order to not mess global namespace with twitter-only-related classes.

Ruby: instantiate objects from files

Overview:
main.rb
items/
one.rb
two.rb
three.rb
Every file in items/ should have a human readable description (serialization is out), like so (but maybe a DSL would be better?):
class One < BaseItem
name "Item one"
def meth
"something"
end
main.rb should be able to instantiate all objects from the items/ directory. How could this be accomplished? Not familiar with Ruby, I see the object model allows for some pretty cool things (those class hooks, etc), but I'm having trouble finding a way to solve this.
Any input way appreciated.
EDIT:
Shoot, I may have missed the gist of it - what I didn't mention was the stuff in the items/ dir would be dynamic — treat items as plugins, I'd want main.rb to autodetect everything in that dir at runtime (possibly force a reload during execution). main.rb has no prior knowledge of the objects in there, it just knows what methods to expect from them.
I've looked at building DSLs, considering defining (in main.rb) a spawn function that takes a block. A sample file in items/ would look something like:
spawn do
name "Item name"
def foo
"!"
end
end
And the innards of spawn would create a new object of the base type and pass the block to instance_eval. That meant I'd need to have a method name to set the value, but incidentally, I also wanted the value to be accessible under name, so I had to go around it renaming the attr.
I've also tried the inherit route: make every item file contain a class that inherits from a BaseItem of sorts, and hook into it via inherited ... but that didn't work (the hook never fired, I've lost the code now).
EDIT2:
You could look at what homebrew does with its formulas, that's very close to what I'd want - I just didn't have the ruby prowess to reverse engineer how it handles a formula.
It all boils down to requiring those files, and make sure that you implemented the functionality you want in them.
If you want a more specific response, you need to ask a more specific question.
I am no expert on object persistence, but answer to your specific question is, that you have 2 good choices: One is YAML, and the other is Ruby itself: a DSL written by you or someone else, and specific to your business logic.
But I think that more general answer would require reviewing object persistance in Ruby more systematically. For example, ActiveRecord::Base descendants persists as database tables. There are other ways, I found eg. this http://stone.rubyforge.org/ by googling. This is my problem as well, I'm facing the same question as you in my work.
What you are asking for looks and smells a lot like a normal Ruby script.
class One < BaseItem
name "Item one"
def meth
"something"
end
We'd close the class definition with another end statement. name "Item one" would probably be done inside the initialize method, by setting an instance variable:
attr_reader :name
def initialize(name)
#name = name
end
Typically we wouldn't call the folder "items", but instead it would be "lib", but otherwise what you are talking about is very normal and expected.
Instantiating all items in a folder is easily done by iterating over the folder's contents, requiring the files, and calling the new method for that item. You can figure out the name by mapping the filename to the class name, or by initializing an instance at the end of the file:
one = One.new("item one")
You could keep track of the items loaded in an array or hash, or just hardwire them in. It's up to you, since this is your code.
It sounds like you haven't tried writing any Ruby scripts, otherwise you would have found this out already. Normal Ruby programming books/documentation would have covered this. As is, the question is akin to premature optimization, and working with the language would have given you the answer.

Where and how should I add this new 'non-rails-way' method in my Rails application

I've written a small method to query and retrieve from an MS SQL 2008 server and I am not sure where to put the code in my rails app.
The scenario:
I am writing a Ruby and Rails app with a connection to a legacy MS SQL 2008 server DB.
A lot is working as expected, which is nice.
For now I work off a copy of the legacy DB and I treat it as readonly. It's big (7000+ tables some of which have over 40 million records). I am using it 'as-is' and don't want to change any of the underlying schema.
I do want to extend some very server-specific functionality. For instance, I make use of:
thing = ActiveRecord::Base.connection.exec_query(my_query_string_here)
... and it works. The result is an array that contains a hash and I can get to the relevant hash value by using:
thing[0][""]
... which works.
So, I thought I should write a method to make this easier and I wrote:
Class Tool < ActiveRecord::Base
def self.queryRDW(x)
res=ActiveRecord::Base.connection.exec_query(x)
ret=res.to_hash
return ret[0][""]
end
end
and put it in config/initializers/tool.rb Unfortunately, webrick complains about the file during boot with the following cryptic error:
.../config/initializers/tool.rb:7: syntax error, unexpected keyword_end, expecting $end (SyntaxError)
I recognize that this is not an out-of-the-box rails-way of doing things, so please don't remind me. (My struggles remind me often enough)
My question:
Where should I put this code so that I can invoke it from within a controller or a view in my rails app? Does this need to be a new Class method or something else?
Many thanks!
Addendum:
I changed Class to class (doh!)
I moved tool.rb into lib/
I changed tool.rb to now be:
module Tool
def self.queryRDW(x)
res = ActiveRecord::Base.connection.exec_query(x)
res.to_hash[0][""]
end
end
but doing this in app/views/stats.html.erb
thing=queryRDW("mysql string")
gets me an 'undefined method error'
Addendum 2
I made the directory app/concerns and put tool.rb there.
When I use:
<%=queryRDW("myStringHere")%>
in:
app/views/stats.html.erb
I get:
undefined method `queryRDW' for #<#<Class:0x0000000378ccf8>:0x00000003c1ce58>
You need to lowercase the keyword class in line 1.
I'd also say that this class doesn't need to inherit from ActiveRecord::Base — and doesn't even really need to be a class — if it's simply a wrapper around exec_query. There's nothing "wrong" with this, but if you never intend to instantiate an object of this class, you could just create a simple utility module:
module Tool
def self.queryRDW(x)
res = ActiveRecord::Base.connection.exec_query(x)
res.to_hash[0][""]
end
end
You can save this file in a couple of places:
lib/tool.rb. If you're using Rails 3, you'll need to add (or uncomment) this line in config/application.rb:
# config/application.rb
config.autoload_paths += %W(#{config.root}/lib)
app/concerns/tool.rb. This will automatically be detected by Rails 3.
I generally use app/concerns for tools that are entirely application-specific and lib for utilities that I might reuse among several different applications.
I wouldn't put this in config/initializers. This seems like code you'd put in app/models.
The error you're getting is syntax related, so double check the syntax.
To answer your question more directly, though, it's acceptable to put this stuff in your model if it's model related (in other words, part of your business domain). If it is something extraneous or orthogonal to your domain, I'd put it in lib.
Hope this helps.

Resources