I am currently building a Thor executable that supports several different tasks. Some tasks inoke other tasks. For example
Auth
authenticates the local user
Create
Invoke Auth
Does something
Invoke Configure
Configure:
Invoke Auth
Does something
The nice thing about thor is that I can now call all of the three tasks separately, but be sure that authenticate for example is only called once through invoke.
But what is now the best way to share Data between those tasks. For example when Authenticating I want to set the User-ID of the current user. Thus when I want to request Data from our webservice I can take the user id from anywhere in my code the same way.
Right now I am implementing this through Modules, but I am not really sure if this is the best way to do it.
Using Singleton would be another way, but somehow this seems like a little bit too much for this purpose.
Maybe you guys have experience with a better way to do this, or maybe using modules is the best way to store the information anyway.
I would have done something like this:
module PRJ
class << self
attr_accessor :api_key, :account_name, :api_version, :api_url, ......
def configure
yield self
end
end
end
require "#{directory_of_all_other_classes}"
require 'thor'
class CLI < Thor
include Thor::Actions
def login
...
PRJ.api_key = response[:api_key]
end
As you want to invoke Auth in every cases, so I think it should be in main CLI file.
Regarding storing and using data it would be better to use attributes instead of constant. I may not be right about this but I prefer using class attributes.
Now you can access and change PRJ.api_key from anywhere (require PRJ module in every file)
P.S. The structure of code snippet might not be accurate, I just wanted to share some basic skeleton. Do something like above for remaining classes.
If you just need to store simple data structures I would go for common class variables
class Test < Thor
##my_shared_var = nil
desc 'my_first_task','sdfasdf'
def my_first_task
##my_shared_var = 'foo'
end
desc 'my_second_task','sdfasdf'
def my_second_task
invoke :my_first_task
puts ##my_shared_var
end
end
Related
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.
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
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.
I am using Rails Warden plugin. It defines a helper method 'user' that returns current user. See the link for the source code.
Now I have an business logic object that does not have any reference to the controller. But I would like to get the current user. Is there any way of accessing this?
I have tried
ActionController::Base.helpers.user
or even
RailsWarden::Mixins::HelperMethods.user
with no luck. Thanks.
Now I have an business logic object
that does not have any reference to
the controller. But I would like to
get the current user. Is there any way
of accessing this?
So why can't you just pass the current user to those methods?
Additionally you can mix them in.
I strongly discourage you to write the static helpers (it is not Java, it is Ruby!).
Instead, where you need those helpers include them as a module:
module SuperLogic
def calculate_stuff(current_user=nil)
(current_user || user || self).bills.sum
end
edn
Then include this where you need it:
# user model
class User
include SuperLogic
#it will get the `calculate_stuff` method
end
# controller
include SuperLogic
# so you can use it as one of
calculate_stuff user
calculate_stuff
and so on...
additionally where you access your business logic, you can just create an instance of the class instead of "static" methods (in ruby they are "class" methods):
# controller
def calculate
#result = BusinessLogic.new(user).calculate_stuff
end
This is probably the easiest thing you can do.
Really, you don't need to access whole HTTP context in your business objects (I'm not even talking about testing it).
The way I think of business logic, it's something that sits between the controller and the model. I think it would be ok to pass an instance of the request to the logic methods, and since you're using warden, you can get the user from 'request.env['warden'].user'.
I haven't encountered a good reason not to have logic methods be static (self.) methods of a module. Maybe Dmytrii's suggestion works for you, but I prefer to 'require' than to dynamically include one-off logic bits.
I'm a Java-developer toying with Ruby, and loving it. I have understood that because of Ruby's metaprogramming facilities my unit-tests become much cleaner and I don't need nasty mocking frameworks. I have a class which needs the File class's services and in my test I don't want to touch my real filesystem. In Java I would use some virtual file system for easier "seams" to pass fake-objects in but in Ruby that's obviously overkill. What I come up seems already really nice compared to the Java-world. In my class under test I have an optional constructor parameter:
def initialize(file_class=File)
When I need to open files within my class, I can then do this:
#file_class.open(filename)
And the call goes to either the real File-class, or in case of my unit-test, it goes to a fake-class which doesn't touch the filesystem. I know there must be a better way to do this with metaprogramming?
Mocha (http://mocha.rubyforge.org/) is a very good mocking library for ruby. Depending on what you're actually wanting to test (i.e. if you want to just fake out the File.new call to avoid the file system dependency or if you want to verify that the correct arguments are passed into File.new) you could do something like this:
require 'mocha'
mock_file_obj = mock("My Mock File") do
stubs(:some_instance_method).returns("foo")
end
File.stubs(:new).with(is_a(String)).returns(mock_file_obj)
In the case you've outlined, I'd suggest that what you're doing seems fine. I know that it's a technique that James Mead (the author of Mocha) has advocated. There's no need to do metaprogramming just for the sake of it. Here's what James has to say about it (and a long list of other techniques you could try)
This is a particularly difficult challenge for me. With the help I received on this question, and some extra work on my behalf, here's the solution I arrived at.
# lib/real_thing.rb
class RealThing
def initialize a, b, c
# ...
end
end
# test/test_real_thing.rb
class TestRealThing < MiniTest::Unit::TestCase
class Fake < RealThing; end
def test_real_thing_initializer
fake = mock()
Fake.expects(:new).with(1, 2, 3).returns(fake)
assert_equal fake, Fake.new(1, 2, 3)
end
end