How do I preserve case with http.get? - ruby-on-rails

I have a requirement to send an HTTP header in a specific character-case. I am aware that this is against the RFC, but I have a requirement.
http.get seems to change the case of the headers dictionary I supply it. How can I preserve the character-case?

Based on the Tin Man's answer that the Net::HTTP library is calling #downcase on your custom header key (and all header keys), here are some additional options that don't monkey-patch the whole of Net::HTTP.
You could try this:
custom_header_key = "X-miXEd-cASe"
def custom_header_key.downcase
self
end
To avoid clearing the method cache, either store the result of the above in a class-level constant:
custom_header_key = "X-miXEd-cASe"
def custom_header_key.downcase
self
end
CUSTOM_HEADER_KEY = custom_header_key
or subclass String to override that particular behavior:
class StringWithIdentityDowncase < String
def downcase
self
end
end
custom_header_key = StringWithIdentityDowncase.new("X-miXEd-cASe")

The accepted answer does not work. Frankly, I doubt that it ever did since it looks like it would have had to also override split and capitalize, I followed that method back a few commits, it's been that way at least since 2004.
Here is my solution, in answer to this closed question:
require 'net/http'
class Net::HTTP::ImmutableHeaderKey
attr_reader :key
def initialize(key)
#key = key
end
def downcase
self
end
def capitalize
self
end
def split(*)
[self]
end
def hash
key.hash
end
def eql?(other)
key.eql? other.key.eql?
end
def to_s
key
end
end
Now you need to be sure to always use instances of this class as your keys.
request = Net::HTTP::Get.new('/')
user_key = Net::HTTP::ImmutableHeaderKey.new("user")
request[user_key] = "James"
require 'stringio'
StringIO.new.tap do |output|
request.exec output, 'ver', 'path'
puts output.string
end
# >> GET path HTTP/ver
# >> Accept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
# >> Accept: */*
# >> User-Agent: Ruby
# >> user: James
# >>

Mine is one way to do it, but I recommend doing it as #yfeldblum recommends, simply short-circuit downcase for the header keys that need to have their case left-alone.
In multiple places in Net::HTTP::HTTPHeader the headers get folded to lower-case using downcase.
I think it is pretty drastic to change that behavior, but this will do it. Add this to your source and it will redefine the methods in the HTTPHeader module that had downcase in them.
module HTTPHeader
def initialize_http_header(initheader)
#header = {}
return unless initheader
initheader.each do |key, value|
warn "net/http: warning: duplicated HTTP header: #{key}" if key?(key) and $VERBOSE
#header[key] = [value.strip]
end
end
def [](key)
a = #header[key] or return nil
a.join(', ')
end
def []=(key, val)
unless val
#header.delete key
return val
end
#header[key] = [val]
end
def add_field(key, val)
if #header.key?(key)
#header[key].push val
else
#header[key] = [val]
end
end
def get_fields(key)
return nil unless #header[key]
#header[key].dup
end
def fetch(key, *args, &block) #:yield: +key+
a = #header.fetch(key, *args, &block)
a.kind_of?(Array) ? a.join(', ') : a
end
# Removes a header field.
def delete(key)
#header.delete(key)
end
# true if +key+ header exists.
def key?(key)
#header.key?(key)
end
def tokens(vals)
return [] unless vals
vals.map {|v| v.split(',') }.flatten\
.reject {|str| str.strip.empty? }\
.map {|tok| tok.strip }
end
end
I think this is a brute force way of going about it, but nothing else more elegant jumped to mind.
While this should fix the problem for any Ruby libraries using Net::HTTP, it will probably fail for any gems that use Curl or libcurl.

Joshua Cheek's answer is great, but it does in work anymore in Ruby 2.3
This modification fix it:
class Net::HTTP::ImmutableHeaderKey
...
def to_s
caller.first.match(/capitalize/) ? self : #key
end
end

It all falls down into the net/generic_request#write_header.
You could monkey patch the code
# 'net/generic_request' line 319
def write_header(sock, ver, path)
customheaders = {
"My-Custom-Header" => "MY-CUSTOM-HEADER",
"Another-Custom-Header" => "aNoThErCuStOmHeAdEr"
}
buf = "#{#method} #{path} HTTP/#{ver}\r\n"
each_capitalized do |k,v|
customheaders.key?(k) ? kk = customheaders[k] : kk = k
buf << "#{kk}: #{v}\r\n"
end
buf << "\r\n"
sock.write buf
end
and you don't need to rewrite the whole net/http/header, net/generic_request and net/http chain.
It's not the best solution, but it's the easiest one I guess and there's least amount of monkey patching.
Hope it helps.

Related

How to implement an object in Ruby with regex

Absolute beginner in Ruby.
I need to create a class that contains the following keys:
I know that this might be the structure, but can anybody help me with syntax?
class PixKey
def cpf
^[0-9]{11}$
end
def cnpj
^[0-9]{14}$
end
def phone
^\+[1-9][0-9]\d{1,14}$
end
def email
^[a-z0-9.!#$&'*+\/=?^_`{
end
def evp
[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}
end
end
You can define regular expressions using the /.../ regular expression literal.
Since regular expressions are immutable, I would simply use constants:
class PixKey
CPF = /^[0-9]{11}$/
CNPJ = /^[0-9]{14}$/
PHONE = /^\+[1-9][0-9]\d{1,14}$/
EMAIL = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+#[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
EVP = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/
end
In the above, I've changed the email regexp to the one suggested by the HTML standard because the one in your screenshot was probably destroyed by a markdown parser.
You can use the above like this:
PixKey::CPF.match?('12345678901') #=> true
PixKey::CNPJ.match?('12345678901234') #=> true
PixKey::PHONE.match?('+5510998765432') #=> true
PixKey::EMAIL.match?('pix#bcb.gov.br') #=> true
PixKey::EVP.match?('123e4567-e89b-12d3-a456-426655440000') #=> true
Of course, you're not limited to match?, you can use any method from the Regexp class or pattern matching methods from String.
Note that in Ruby, ^ and $ match beginning and end of line which can cause problems in multi-line strings:
string = "before
+5510998765432
after"
string.match?(PixKey::PHONE) #=> true
If you want to match beginning and end of string (i.e. only match whole strings), you can use \A and \z instead:
PixKey::PHONE = /\A\+[1-9][0-9]\d{1,14}\z/
string = "before
+5510998765432
after"
string.match?(PixKey::PHONE) #=> false
string = '+5510998765432'
string.match?(PixKey::PHONE) #=> true
To return the regular expressions for further use, you could return the regex using /:
class PixKey
def cpf
/^[0-9]{11}$/
end
end
You could run PixKey.new.cpf to return the regex:
irb(main):022:0> PixKey.new.cpf
=> /^[0-9]{11}$/
You could also make it a class method by putting self. in front of the method name or add the line class << self as the first line in your class to make them all class methods by default (don't forget the end in this case).
class PixKey
def self.cpf
/^[0-9]{11}$/
end
end
class PixKey
class << self
def cpf
/^[0-9]{11}$/
end
end
end
With this you could run PixKey.cpf to return the regex:
irb(main):022:0> PixKey.cpf
=> /^[0-9]{11}$/

Neat way to get and set keys of json column in Rails

I have a model/table with a json column in it as follows
t.json :options, default: {}
The column can contain many keys within it, something like this
options = {"details" : {key1: "Value1", key2: "Value2"}}
I want to set and get these values easily. So i have made getters and setters for the same.
def key1
options['details']&.[]('key1')
end
def key1=(value)
options['details'] ||= {}
options['details']['key1'] ||=0
options['details']['key1'] += value
end
But this just adds lines to my code, and it does not scale when more details are added. Can you please suggest a clean and neat way of doing this?
Use dynamic method creation:
options['details'].default_proc = ->(_,_) {{}}
ALLOWED_KEYS = %i[key1 key2 key3]
ALLOWED_KEYS.each do |key|
define_method key do
options['details'][key] if options['details'].key?(key)
end
define_method "#{key}=" do |value|
(options['details'][key] ||= 0) += value
end
end
You can just pass the key as a parameter as well right?
def get_key key=:key1
options['details']&.[](key)
end
def set_key= value, key=:key1
options['details'] ||= {}
options['details'][key] ||=0
options['details'][key] += value
end
Simple & Short
Depending on re-usability you can choose different options. The short option is to simply define the methods using a loop in combination with #define_method.
class SomeModel < ApplicationRecord
option_accessors = ['key1', 'key2']
option_accessors.map(&:to_s).each do |accessor_name|
# ^ in case you provide symbols in option_accessors
# this can be left out if know this is not the case
define_method accessor_name do
options.dig('details', accessor_name)
end
define_method "#{accessor_name}=" do |value|
details = options['details'] ||= {}
details[accessor_name] ||= 0
details[accessor_name] += value
end
end
end
Writing a Module
Alternatively you could write a module that provide the above as helpers. A simple module could look something like this:
# app/model_helpers/option_details_attribute_accessors.rb
module OptionDetailsAttributeAccessors
def option_details_attr_reader(*accessors)
accessors.map(&:to_s).each do |accessor|
define_method accessor do
options.dig('details', accessor)
end
end
end
def option_details_attr_writer(*accessors)
accessors.map(&:to_s).each do |accessor|
define_method "#{accessor}=" do |value|
details = options['details'] ||= {}
details[accessor] ||= 0
details[accessor] += value
end
end
end
def option_details_attr_accessor(*accessors)
option_details_attr_reader(*accessors)
option_details_attr_writer(*accessors)
end
end
Now you can simply extend your class with these helpers and easily add readers/writers.
class SomeModel < ApplicationRecord
extend OptionDetailsAttributeAccessors
option_details_attr_accessor :key1, :key2
end
If anything is unclear simply ask away in the comments.

How can I cleanly define "antonym" or "opposite" methods in Ruby / Rails?

I'm pretty often defining methods and their antonyms in the code I'm writing, as in:
def happy?
#happiness > 3
end
def sad?
!happy?
end
Which is fine, but I'm a little surprised that Ruby or ActiveSupport doesn't give me something like:
def happy?
#happiness > 3
end
alias_opposite :sad? :happy?
Or am I just looking in the wrong place?
There is no such method in popular libraries, but there is how this could be implemented
class Module
def alias_opposite(a, b)
define_method(a) { !self.send(b) }
end
end
Usage
class A < Struct.new(:happiness)
def happy?
happiness > 3
end
alias_opposite :sad?, :happy?
end
p A.new(1).sad? # => true
p A.new(5).sad? # => false
I suspect this pattern is not as common in ruby because the unless keyword often does the trick:
# ...
clap_your_hands if happy?
stomp_your_feet unless happy?
# ...
Of course, its simple to roll your own:
module Antonymator
def define_antonym(as, of)
define_method(as.to_sym) do |*args|
return !(send(of.to_sym, *args))
end
end
end
# Usage Example
class AreThey
extend Antonymator
define_antonym :uneql?, :eql?
define_antonym :nonconsecutive?, :consecutive?
def eql?(a, b)
a == b
end
def consecutive?(a, b)
a.next == b
end
end
are_they = AreThey.new
puts are_they.uneql? 1, 2 # true
puts are_they.nonconsecutive? 1, 2 # false
If your methods return a Boolean, you can always include the positive method in the negative method.
def drinking_age?(age)
age > #restricted_age
end
def not_drinking_age?(age)
!drinking_age?(age)
end
#restricted_age = 20
Hope that helps.
I guess it depends on what 'opposite' means in the context.

Ruby/Rails: Prepend, append code to all methods

I wrote a small benchmarking Class for testing my code doing development. At the moment I have to add the Class to the beginning and end of every method. Is it posible to prepend, append on the fly, so that I don't have to clutter my code?
class ApplicationController
before_filter :init_perf
after_filter :write_perf_results_to_log!
def init_perf
#perf ||= Perf.new
end
def write_perf_results_to_log!
#perf.results
end
end
class Products < ApplicationsController
def foo
#perf.log(__methond__.to_s)
caculation = 5 *4
#perf.write!
end
def bar
#perf.log(__methond__.to_s)
caculation = 1 / 5
#perf.write!
end
end
This is the Perf class. It is located in the services folder.
class Perf
def initialize
#results = []
end
def log(note)
#start = Time.now
#note = note
end
def write!
if #results.find {|h| h[:note] == #note } # Update :sec method exists in results
#results.select { |h| h["note"] == #note; h[":sec"] = (Time.now - #start).round(3) }
else # Add new Hash to results
#results << { :note => #note, :sec => (Time.now - #start).round(3) }
end
end
def results
content = "
PERFORMANCE STATISTICS!
"
#results.each do |r|
content += r[:note] + " " + r[:sec].to_s + "
"
end
content += "
"
Rails.logger.info content
end
end
In general computing terms what you want to do is called code instrumentation. There are several ways to accomplish this, however here's one (crude) example using some metaprogramming:
First define a new method that we will use for injecting our instrumentation code:
class ApplicationController
def self.instrument_methods(*methods)
methods.each { |m|
# Rename original method
self.send(:alias_method, "#{m}_orig", m)
# Redefine old method with instrumentation code added
define_method m do
puts "Perf log #{m}"
self.send "#{m}_orig"
puts "Perf write"
end
}
end
end
How to use it:
class Product < ApplicationController
def foo
puts "Foo"
end
def bar
puts "Bar"
end
# This has to be called last, once the original methods are defined
instrument_methods :foo, :bar
end
Then:
p = Product.new
p.foo
p.bar
Will output:
Perf log foo
Foo
Perf write
Perf log bar
Bar
Perf write
Here are some other ways to instrument ruby code and measure performance:
http://ruby-prof.rubyforge.org/
http://www.igvita.com/2009/06/13/profiling-ruby-with-googles-perftools/
There is better solution.
class ApplicationController
def self.inherited(klass)
def klass.method_added(name)
return if #_not_new
#_not_new = true
original = "original #{name}"
alias_method original, name
define_method(name) do |*args, &block|
puts "==> called #{name} with args: #{args.inspect}"
result = send original, *args, &block
puts "<== result is #{result}"
result
end
#_not_new = false
end
end
end
class Product < ApplicationController
def meth(a1, a2)
a1 + a2
end
end
product = Product.new
puts product.meth(2,3)
And the result:
==> called meth with args: [2, 3]
<== result is 5
5
The source & explanation are here: http://pragprog.com/screencasts/v-dtrubyom/the-ruby-object-model-and-metaprogramming. I recommend to spend not a big money to get this course.
I'm the author of aspector gem. Thanks to dimuch for mentioning it.
I've come up with a solution using aspector. Here are the high level steps:
Create an aspect as a subclass of Aspector::Base
Inside the aspect, define advices (before/after/around are the primary types of advices)
Apply the aspect on target class (or module/object)
The full code can be found in this gist. Please feel free to let me know if you have questions or the solution doesn't do what you intend to.
class PerfAspect < Aspector::Base
around options[:action_methods] do |proxy|
#perf ||= Perf.new
proxy.call
#perf.results
end
around options[:other_methods], :method_arg => true do |method, proxy, *args, &block|
#perf.log(method)
result = proxy.call *args, &block
#perf.write!
result
end
end
action_methods = [:action]
other_methods = Products.instance_methods(false) - action_methods
PerfAspect.apply(Products, :action_methods => action_methods, :other_methods => other_methods)
Guess aspector gem can help. It's not well documented but has useful examples.

mongo_mapper custom data types for localization

i have created a LocalizedString custom data type for storing / displaying translations using mongo_mapper.
This works for one field but as soon as i introduce another field they get written over each and display only one value for both fields. The to_mongo and from_mongo seem to be not workings properly. Please can any one help with this ? her is the code :
class LocalizedString
attr_accessor :translations
def self.from_mongo(value)
puts self.inspect
#translations ||= if value.is_a?(Hash)
value
elsif value.nil?
{}
else
{ I18n.locale.to_s => value }
end
#translations[I18n.locale.to_s]
end
def self.to_mongo(value)
puts self.inspect
if value.is_a?(Hash)
#translations = value
else
#translations[I18n.locale.to_s] = value
end
#translations
end
end
Thank alot
Rick
The problem is that from within your [to|from]_mongo methods, #translations refers to a class variable, not the instance variable you expect. So what's happening is that each time from_mongo is called, it overwrites the value.
A fixed version would be something like this:
class LocalizedString
attr_accessor :translations
def initialize( translations = {} )
#translations = translations
end
def self.from_mongo(value)
if value.is_a?(Hash)
LocalizedString.new(value)
elsif value.nil?
LocalizedString.new()
else
LocalizedString.new( { I18n.locale.to_s => value })
end
end
def self.to_mongo(value)
value.translations if value.present?
end
end
I found that jared's response didn't work for me -- I would get that translations was not found when using LocalizedString in an EmbeddedDocument.
I would get a similar problem on rick's solution where translations was nil when using embedded documents. To get a working solution, I took Rick's solution, changed the translation variable to be an instance variable so it wouldn't be overwritten for each new field that used LocalizedString, and then added a check to make sure translations wasn't nil (and create a new Hash if it was).
Of all the LocalizedString solutions floating around, this is the first time I've been able to get it working on EmbeddedDocuments and without the overwritting problem -- there still may be other issues! :)
class LocalizedString
attr_accessor :translations
def self.from_mongo(value)
puts self.inspect
translations ||= if value.is_a?(Hash)
value
elsif value.nil?
{}
else
{ I18n.locale.to_s => value }
end
translations[I18n.locale.to_s]
end
def self.to_mongo(value)
puts self.inspect
if value.is_a?(Hash)
translations = value
else
if translations.nil?
translations = Hash.new()
end
translations[I18n.locale.to_s] = value
end
translations
end
end
I found this post: which was very helpful. He extended HashWithIndifferentAccess to work as a LocalizedString. The only thing I didn't like about it was having to explicly specify the locale when setting it each time -- I wanted it to work more like a string. of course, you can't overload the = operator (at least I don't think you can) so I used <<, and added a to_s method that would output the string of the current locale....
class LocalizedString < HashWithIndifferentAccess
def self.from_mongo(value)
LocalizedString.new(value || {})
end
def available_locales
symbolize_keys.keys
end
def to_s
self[I18n.locale]
end
def in_current_locale=(value)
self[I18n.locale] = value
end
def << (value)
self[I18n.locale] = value
end
end
and then I have a class like:
class SimpleModel
include MongoMapper::Document
key :test, LocalizedString
end
and can do things like
I18n.locale = :en
a = SimpleModel.new
a.test << "English"
I18n.locale = :de
a.test << "German"
puts a.test # access the translation for the current locale
I18n.locale = :en
puts a.test # access the translation for the current locale
puts a.test[:de] # access a translation explicitly
puts a.test[:en]
puts a.test.inspect
and get
German
English
German
English
{"en"=>"English", "de"=>"German"}
so there we go -- this one actually seems to work for me. Comments welcome, and hope this helps someone!

Resources