Importing a Ruby class from a socket connection? - ruby

I have this idea for a client/server archetype where the server would hold a hash of Marshal.dump'ed class objects along with their version numbers. Then the client could query the server concerning the version number and import the newer version of the class before instantiating it:
class Stuff
def methods
gibberish
end
end
$obj_hash["Stuff"] = [3.0, Marshal.dump(Stuff)]
The problem I'm running into is that Ruby doesn't seem to want to allow me to Marshal.load the data once I've downloaded it from the server because the class and its methods don't exist in the client. If I bypass this by creating a 'dummy' class I'm then unable to replace the dummy class with the Marshal.load'ed data. If I simply try to use the loaded data as a class it functions according to the contents of the dummy class rather than the downloaded one.
Is there another way to go about this? If not then I guess I could just gz the code and then eval it at the other end, but I'm trying to avoid using eval or sending easily decipherable code over the line.
Thanks in advance for any advice.

Look at what happens.
class Stuff
def methods
"foo"
end
end
ruby-1.8.7-p352 :001 > Marshal.dump(Stuff)
=> "\004\bc\bStuff"
Notice how it says nothing about "methods" or "foo." If the server isn't sending that code down the wire, how is the client supposed to know what Stuff#methods should do?
It won't. :)
To do what you want to do, you'll have to send down the code itself and eval it. You'll have to implement the versioning logic yourself, of course, and "really re-define" the classes (not just monkey-patch) them.
See are you allowed to redefine a class in ruby? or is this just in irb

Related

Using Test::Unit::Data

I am trying to create some data driven API tests using Test::Unit for ruby. The eventual intention is to read a series of test cases in from a .csv file. In looking for something that would be the equivalent of #dataprovider for testng, I found a class called Data which looks like exactly what I need.
http://www.rubydoc.info/github/test-unit/test-unit/Test/Unit/Data/ClassMethods
However, when I tried to create a test case to try to get it working, I get an error saying
"initial_test.rb:4:in <class:InitialTest>': undefined methoddata' for InitialTest:Class (NoMethodError)
"
code I was running:
require "test/unit"
class InitialTest < Test::Unit::TestCase
data("true" => [true],
"false" => [false])
def test_true_is_true(data)
value = data
assert(false, "FAIL!")
end
end
I can't seem to find any mention of the Data class outside of the documentation. Has anyone used this class? Am I missing something?
I'm not familiar with this module in particular, but since data is defined as an instance method on Test::Unit::Data and Test::Unit::Data is a Module, this works:
class YourTest < Test::Unit::TestCase
include Test::Unit::Data
data(…)
end
This said, you're going to want to take a closer look at the docs you linked since your example usage is looking like copy-pasta.
Turns out that the problem was that I was using an older version of Ruby that did not include the class I was trying to use. Updating to a newer version solved the problem.

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

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

What if objects can print themselves in Ruby? [Object#print]

I was thinking wouldn't it be cool to have a print method defined in the Ruby Object class? Consider the following:
class Object
def print
puts self.to_s
end
end
23.times &:print
Is there any issue in having something like this? Seems like a good feature to have. It also appears easy to read.
There's already Object#inspect defined. Plus, there's already Kernel#print defined as private method in Object class and every class that inherits from it.
This method already exists in the Ruby standard library. However, it has a different name: display.
23.times &:display
# 012345678910111213141516171819202122
As you can see, it does not write a newline after the object's string representation; it is ill-suited for object inspection.
The main issue with adding methods to Object is that they become universal and may clash with similarly named methods in other libraries or in your project.
There are already multiple simple ways to output data or convert to string form in Ruby core, so the risk of a clash (on a very useful method name) likely outweighs any benefits from nicer syntax even in your own code.
If you have a smaller set of classes in your own project, where you feel this would be a useful feature to have, then this is an ideal use case for mix-ins.
Define a module:
module CanPrintSelf
def print
puts self.to_s
end
end
And include it in any class you want to have the feature:
class MyClass
include CanPrintSelf
end
my_object = MyClass.new
my_object.print
So you can have this feature if you like it, and you don't need to modify Object.

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.

In Rails, how to add a new method to String class?

I want to build an index for different objects in my Rails project and would like to add a 'count_occurences' method that I can call on String objects.
I saw I could do something like
class String
def self.count_occurences
do_something_here
end
end
What's the exact way to define this method, and where to put the code in my Rails project?
Thanks
You can define a new class in your application at lib/ext/string.rb and put this content in it:
class String
def to_magic
"magic"
end
end
To load this class, you will need to require it in your config/application.rb file or in an initializer. If you had many of these extensions, an initializer is better! The way to load it is simple:
require 'ext/string'
The to_magic method will then be available on instances of the String class inside your application / console, i.e.:
>> "not magic".to_magic
=> "magic"
No plugins necessary.
I know this is an old thread, but it doesn't seem as if the accepted solution works in Rails 4+ (at least not for me). Putting the extension rb file in to config/initializers worked.
Alternatively, you can add /lib to the Rails autoloader (in config/application.rb, in the Application class:
config.autoload_paths += %W(#{config.root}/lib)
require 'ext/string'
See this:
http://brettu.com/rails-ruby-tips-203-load-lib-files-in-rails-4/
When you want to extend some core class then you usually want to create a plugin (it is handy when need this code in another application). Here you can find a guide how to create a plugin http://guides.rubyonrails.org/plugins.html and point #3 show you how to extend String class: http://guides.rubyonrails.org/plugins.html#extending-core-classes

Resources