Ruby Best Practice For Storing Collection of Procs - ruby

I am writing regressive test for my app and I use the class Page. Each page has a nav_to method that needs to be set with a proc when the instance is initialized.
I currently have a list of 40 some procs in the global scope and to me this seems sloppy. What would be the best practice for storing these procs? Should I store them in a module? Hash? Class? Please help!

Consider storing them in a module- (or class-)constant so that they can be grouped and named clearly. The data structure you choose (array vs hash) probably depends most on your desired interface (are they associated with some key or simply ordered?) and performance concerns, if relevant:
module MyTests # ...or "class"
NAV_TO_PROCS = [
Proc.new { ... },
Proc.new { ... },
]
# ... or ...
NAV_TO_BY_PAGE_NAME = {
"page1" => Proc.new { ... },
"page2" => Proc.new { ... },
}
end
As an aside, when using module constants as such I like to "freeze" them to avoid accidental mutation during use (e.g. NAV_TO_PROCS = [...].freeze).

Related

How to override a hash constant using instance_eval in Rails

How I can override a constant in a class with an instance_eval in a plugin?
This is the constant I want to override:
class MessageTemplate < ActiveRecord::Base
TEMPLATE_KEYS = {
:guardian=>{
:guardian_full_name=>"guardian_full_name",
:guardian_first_name=>"guardian_first_name",
:guardian_last_name=>"guardian_last_name",
:ward_full_name=>"ward_full_name",
:guardians_relation=>"guardians_relation",
:ward_batch_name=>"ward_batch_name",
:ward_admission_number=>"ward_admission_number",
:guardian_email=>"guardian_email",
:guardian_mobile_phone_no=>"guardian_mobile_phone_no"
}
}
end
I want to add these values to the above constant from a plugin in Ruby on Rails.
This is what I tried, which failed:
module IndepthEinvoice
module MessageTemplate
def self.included(base)
base.instance_eval do
self.TEMPLATE_KEYS = {
:guardian=>{
:payment_link=>"payment_link",
:ward_balance_fee=>"ward_balance_fee"
}
}
end
end
Modify Duplicates or Clones Instead of Constants
Ruby won't actually stop you from re-assigning a constant; it will generally just issue a warning. Still, it's generally considered bad form to modify constants on purpose. Instead, you should use Hash#merge or Hash#merge! to store your changes in a local Hash object. For example:
TEMPLATE_KEYS = {
guardian: {
guardian_full_name: "guardian_full_name",
guardian_first_name: "guardian_first_name",
guardian_last_name: "guardian_last_name",
ward_full_name: "ward_full_name",
guardians_relation: "guardians_relation",
ward_batch_name: "ward_batch_name",
ward_admission_number: "ward_admission_number",
guardian_email: "guardian_email",
guardian_mobile_phone_no: "guardian_mobile_phone_no",
}
}
# make a copy of your TEMPLATE_KEYS constant
#my_template = TEMPLATE_KEYS.dup
# merge your modifications into an instance variable
#my_template[:guardian].merge!(
{
payment_link: "payment_link",
ward_balance_fee: "ward_balance_fee",
}
)
#my_template.dig :guardian, :payment_link
#=> "payment_link"
There are certainly other ways to do this, some more elegent than others. The principle is sound, though, and is definitely better than trying to modify the constant directly, which could have unintended consequences for your application.

In RSpec, how to prepend a before hook in a subcontext that accesses the subject?

Consider the following:
describe MyModel do
context 'updates fields' do
subject { create(:my_model) }
before do
subject.save
subject.reload
end
context 'when changing foo.bar' do
before { subject.foo.bar = 3 }
it { is_expected.to be_multiple_bar }
end
context 'when changing baz.quux' do
before { subject.baz.quux = 3 }
it { is_expected.to be_multiple_quux }
end
end
end
Now, as you may expect, I want the before hook on line 4 to be invoked after the ones on lines 10 and 15.
I've tried 2 things:
I have tried using prepend_before, but that only works when they're defined in the same context, it doesn't allow you to prepend a hook before one that's defined in the supercontext
I have tried using before(:context) on line 10 and 15, and while this should put them in the right order, RSpec doesn't allow me to mutate the subject at that point yet. (And for good reason, I'm not trying to create a shared state here.)
I really don't want to resort to let(:append_before) { proc { #magic here } }, because it's ugly and hacky as hell. Besides, I think what I want is totally reasonable. Right now I copied the two lines over to all subcontexts, which I am not too happy with.
What is a better way to do this?
I am on RSpec 3.7, FactoryGirl 4.8.0 and Ruby 2.3.1
I don't know what your factory looks like, but instead to creating and persisting my_model, modifying, saving and reloading it, you should create it only once. This will also speed up your specs.
You could write something like this:
describe MyModel do
context 'updates fields' do
subject { create(:my_model, foo: {bar: bar}, baz: {quux: quux}) }
context 'when changing foo.bar' do
let(:bar) { 3 }
it { is_expected.to be_multiple_bar }
end
context 'when changing baz.quux' do
let(:quux) { 3 }
it { is_expected.to be_multiple_quux }
end
end
end
Lazy evaluation of both let and subject makes sure you all your parameters are set correctly depending on the context. In case you need/want to extend your factory to support that, check out http://www.rubydoc.info/gems/factory_bot/file/GETTING_STARTED.md
I hope that makes sense.

Objectify Ruby Hashes from/to JSON API

I just released a ruby gem to use some JSON over HTTP API:
https://github.com/solyaris/blomming_api
My naif ruby code just convert complex/nested JSON data structures returned by API endpoints (json_data) to ruby Hashes ( hash_data), in a flat one-to-one transaltion (JSON to ruby hash and viceversa). Tat's fine, but...
I would like a programming interface more "high level".
Maybe instatiating a class Resource for every endpoint, but I'm confused about a smart implementation.
Let me explain with an abstract code.
Let say I have a complex/nested JSON received by an API,
usually an Array of Hashes, recursively nested as here below (imagination example):
json_data = '[{
"commute": {
"minutes": 0,
"startTime": "Wed May 06 22:14:12 EDT 2014",
"locations": [
{
"latitude": "40.4220061",
"longitude": "40.4220061"
},
{
"latitude": "40.4989909",
"longitude": "40.48989805"
},
{
"latitude": "40.4111169",
"longitude": "40.42222869"
}
]
}
},
{
"commute": {
"minutes": 2,
"startTime": "Wed May 28 20:14:12 EDT 2014",
"locations": [
{
"latitude": "43.4220063",
"longitude": "43.4220063"
}
]
}
}]'
At the moment what I do, when I receive a similar JSON form an API is just:
# from JSON to hash
hash_data = JSON.load json_data
# and to assign values:
coords = hash_data.first["commute"]["locations"].last
coords["longitude"] = "40.00" # was "40.4111169"
coords["latitude"] = "41.00" # was "40.42222869"
that's ok, but with awfull/confusing syntax.
Instead, I probably would enjoy something like:
# create object Resource from hash
res = Resource.create( hash_data )
# ... some processing
# assign a "nested" variables: longitude, latitude of object: res
coords = res.first.commute.locations.last
coords.longitude = "40.00" # was "40.4111169"
coords.latitude = "41.00" # was "40.42222869"
# ... some processing
# convert modified object: res into an hash again:
modified_hash = res.save
# and probably at least I'll recover to to JSON:
modified_json = JSON.dump modified_hash
I read intresting posts:
http://pullmonkey.com/2008/01/06/convert-a-ruby-hash-into-a-class-object/
http://www.goodercode.com/wp/convert-your-hash-keys-to-object-properties-in-ruby/
and copying Kerry Wilson' code, I sketched the implementation here below:
class Resource
def self.create (hash)
new ( hash)
end
def initialize ( hash)
hash.to_obj
end
def save
# or to_hash()
# todo! HELP! (see later)
end
end
class ::Hash
# add keys to hash
def to_obj
self.each do |k,v|
v.to_obj if v.kind_of? Hash
v.to_obj if v.kind_of? Array
k=k.gsub(/\.|\s|-|\/|\'/, '_').downcase.to_sym
## create and initialize an instance variable for this key/value pair
self.instance_variable_set("##{k}", v)
## create the getter that returns the instance variable
self.class.send(:define_method, k, proc{self.instance_variable_get("##{k}")})
## create the setter that sets the instance variable
self.class.send(:define_method, "#{k}=", proc{|v| self.instance_variable_set("##{k}", v)})
end
return self
end
end
class ::Array
def to_obj
self.map { |v| v.to_obj }
end
end
#------------------------------------------------------------
BTW, I studied a bit ActiveResource project (was part of Rails if I well understood).
ARes could be great for my scope but the problem is ARes have a bit too "strict" presumption of full REST APIs...
In my case server API are not completely RESTfull in the way ARes would expect...
All in all I would do a lot of work to subclass / modify ARes behaviours
and at the moment I discarded the idea to use ActiveResource
QUESTIONS:
someone could help me to realize the save() method on the above code (I'm really bad with recursive methods... :-( ) ?
Does exist some gem that to the above sketched hash_to_object() and object_to_hash() translation ?
What do you think about that "automatic" objectifying of an "arbitrary" hash coming froma JSON over http APIs ?
I mean: I see the great pro that I do not need to client-side static-wire data structures, allowing to be flexible to possible server side variations.
But on the other hand, doing this automatic objectify, there is a possible cons of a side effect to allow security issues ... like malicious JSON injection (possible untrasted communication net ...)
What do you think about all this ? Any suggestion is welcome!
Sorry for my long post and my ruby language metaprogramming azards :-)
giorgio
UPDATE 2: I'm still interested reading opinions about question point 3:
Pros/Cons to create Resource class for every received JSON
Pros/Cons to create static (preemptive attributes) / automatich/dynamic nested objects
UPDATE 1: long reply to Simone:
thanks, you are right Mash have a sweet .to_hash() method:
require 'json'
require 'hashie'
json_data = '{
"commute": {
"minutes": 0,
"startTime": "Wed May 06 22:14:12 EDT 2014",
"locations": [
{
"latitude": "40.4220061",
"longitude": "40.4220061"
},
{
"latitude": "40.4989909",
"longitude": "40.48989805"
},
{
"latitude": "40.4111169",
"longitude": "40.42222869"
}
]
}
}'
# trasforma in hash
hash = JSON.load json_data
puts hash
res = Hashie::Mash.new hash
# assign a "nested" variables: longitude, latitude of object: res
coords = res.commute.locations.last
coords.longitude = "40.00" # was "40.4111169"
coords.latitude = "41.00" # was "40.42222869"
puts; puts "longitude: #{res.commute.locations.last.longitude}"
puts "latitude: #{res.commute.locations.last.latitude}"
modified_hash = res.to_hash
puts; puts modified_hash
This feature is provided by a few gem. One of the most known is Hashie, specifically the class Hashie::Mash.
Mash is an extended Hash that gives simple pseudo-object functionality that can be built from hashes and easily extended. It is designed to be used in RESTful API libraries to provide easy object-like access to JSON and XML parsed hashes.
Mash also supports multi-level objects.
Depending on your needs and level of nesting, you may get away with an OpenStruct.
I was working with a simple test stub. Hashie would have worked well, but was a bigger tool than I needed (and added dependency).

rspec `its` syntax with dynamic conditions

I've been really loving using contexts, subjects and its with rspec to really clean up my test code. Typical example:
context "as a user" do
subject{ Factory :user }
its(:name){ should == "Bob" }
end
What I can't figure out though is how I could make this condition dynamic (ie. based on other objects). its appears to instance eval the attribute within the block so I lose access to everything around it. I'd love to do something like:
its(:name){ should == subject.contact.name }
But I can't see any way of achieving this. Does anyone know if there is some a method proxied through to this instance eval that gives access to the origin object? Or if there's any other way I can use methods outside the scope of the instance of the attribute that I'm checking?
additional info
It seems as if subject within the its block gets changed to the actual attribute (name in this case)
Interestingly, I have access to any of my let methods, but again, I don't have access to my original subject in question.
You are almost answering yourself, use a let assignment before you set the subject. Then you can reference it everywhere:
context "as a user" do
let(:user) { Factory(:user) }
subject { user }
its(:name) { should == user.contact.name }
end
I'm a context, subject, its lover too !
Not sure if there's a way to do exactly what you're asking for since the "subject" within the block becomes the return value of User#name.
Instead, I've used the let method along with the following spec style to write this kind of test:
describe User do
describe '#name' do
let(:contact) { Factory(:contact, name: 'Bob') }
let(:user) { Factory(:user, contact: contact) }
subject { user.name }
it { should == 'Bob' }
end
end
This of course makes some assumptions about what your contact represents (here it's an association or similar). You may also choose to stub the return value of User#contact instead of relying on FactoryGirl to set up a "real" association.
Regardless of the choices you make on those fronts, this strategy has worked well for me. I find it allows me to be more concise about what is under test, while preserving info (for other devs & future self) about where the expected return value is coming from.
You can just set an instance variable within the subject block:
context 'as a user' do
subject { #user = FactoryGirl.create(:user) }
its(:name) { should == #user.name }
end

How to construct the 2d structure in a dynamic fashion

I iterate through all cars and its supported attributes (many attributes per car) to create a structure like this, how do I do this in a dynamic fashion.
cars = {
"honda" => {'color' => 'blue', 'type' => 'sedan'}.
"nissan" => {'color' => 'yellow', 'type' => 'sports'}.
...
}
cars.each do |car|
car_attrs = ...
car_attrs.each do |attr|
??? How to construct the above structure
end
end
Your question is not very clear... But i guess this is what you want:
cars = {}
options = {}
options['color'] = 'blue'
...
cars['honda'] = options
Is that what you were looking for?
It sounds like you may be asking for a way to create a 2-dimensional hash without having to explicitly create each child hash. One way to accomplish that is by specifying the default object created for a hash key.
# When we create the cars hash, we tell it to create a new Hash
# for undefined keys
cars = Hash.new { |hash, key| hash[key] = Hash.new }
# We can then assign values two-levels deep as follows
cars["honda"]["color"] = "blue"
cars["honda"]["type"] = "sedan"
cars["nissan"]["color"] = "yellow"
cars["nissan"]["type"] = "sports"
# But be careful not to check for nil using the [] operator
# because a default hash is now created when using it
puts "Found a Toyota" if cars["toyota"]
# The correct way to check would be
puts "Really found a Toyota" if cars.has_key? "toyota"
Many client libraries assume that the [] operator returns a nil default, so make sure other code doesn't depend on that behavior before using this solution. Good luck!
Assuming you are using something similar to ActiveRecord (but easy to modify if you are not):
cars_info = Hash[cars.map { |car| [car.name, car.attributes] }

Resources