How to set DataMapper String length to unlimited for PostgreSQL - ruby

Granted, one could use property :foo, Text, lazy: false all over the place to replace property :foo, String but that, of course, defeats several purposes in one go. Or I could use manual migrations, which I have been doing—I'm looking around now to see if they can finally be abandoned, insofar as VARCHAR v. TEXT is concerned.
In other words, I'd like automigrate to create TEXT fields for PostgreSQL for models with String properties rather than arbitrarily, pointlessly, constrained VARCHAR atop a TEXT.

The Postgres adapter seems to be able to handle it:
# size is still required, as length in postgres behaves slightly differently
def size
case self.type
#strings in postgres can be unlimited length
when :string then return (#options.has_key?(:length) || #options.has_key?(:size) ? #size : nil)
else nil
end
end
Untested suggestion, but considering the source of initialize, and the length function immediately beneath it, try passing nil as the length, or no length at all.

Here's a lousy but working solution I came up with—if you've better one please add it and I'll accept that instead.
In config/initializers/postgresql_strings.rb
module DataMapper
module Migrations
module PostgresAdapter
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def type_map
precision = Property::Numeric.precision
scale = Property::Decimal.scale
super.merge(
Property::Binary => { :primitive => 'BYTEA' },
BigDecimal => { :primitive => 'NUMERIC', :precision => precision, :scale => scale },
Float => { :primitive => 'DOUBLE PRECISION' },
String => { :primitive => 'TEXT' } # All that for this
).freeze
end
end
end
end
end
# If you're including dm-validations, it will surprisingly attempt
# to validate strings to <= 50 characters, this prevents that.
DataMapper::Property::String.auto_validation(false)

Related

Which is the correct way to dynamically use variating static data

Following basic principles what would be the preferred way to structure a program and its data that can variate but is used to accomplish the same "standard" task? It should easily be extendable to include more providers.
For example:
require 'mechanize'
require 'open-uri'
class AccountFetcher
##providers = {}
##providers['provider_a'] = [
'http://url_for_login_form.com/', 'name_of_form',
# the number of fields can vary between different providers
[ ['name_of_field', 'value_for_it'], ['another_field', 'another_value'] ],
['name for username field', 'name for password field']
]
##providers['provider_b'] = [
'http://another_url.com/', 'another_form_name',
[
# array of more fields that are specific to this form
],
['name for username field', 'name for password field']
]
# looks more nice using AccountFetcher.accounts opposed to ##accounts
def self.accounts
##providers
end
# example of a method that uses information that can vary
def self.fetch_form(agent, account_type)
# the first element in the array will always be the url
page = agent.get(AccountFetcher.accounts[account_type][0])
# the second element in the array will always be the name of the form
form = page.form(AccountFetcher.accounts[account_type][1])
end
def self.initialize_form(agent, account_type)
form = AccountFetcher.fetch_form(agent, account_type)
# will cycle through all fields that require 'pre-filling'
AccountFetcher.accounts[account_type][2].each do |f|
# f[0] will always be the name of the field and f[1] will
# always be the value for the field; however, the amount of
# fields may vary
form.field_with(:name => f[0]).value = f[1]
end
form
end
So the general idea is use a class variable that consists of a hash and has arrays containing the appropriate data elements. I would need to do this for every class that has similar functions.
Another idea I had would be to replace the class variables and instead turn each provider into a class database that can be accessed by the other classes. So provider_a would become:
class ProviderA
# all the possible information needed
# methods will have similar names across all the different providers
end
And the appropriate provider can be chosen by
class Providers
##p_providers = {}
##p_providers['provider a'] = ProviderA.new
##p_providers['provider b'] = ProviderB.new
##p_providers['provider c'] = ProviderC.new
##p_providers['provider d'] = ProviderD.new
def self.return_provider(name)
##p_providers[name]
end
end
Is the former or latter solution more appropriate? Or is there a more 'ruby-ish' solution?
I would store this configuration values in an external YAML file. Your configuration file could looke like this:
# config/providers.yaml
provider_a:
url: 'http://url_for_login_form.com/'
name: 'name_of_form'
fields:
- name: 'name_of_field'
value: 'value_for_it'
- name: 'another_field'
value: 'another_value'
provider_b:
url: 'http://...'
...
You could load that file with YAML.file_file that returns nested hash in this example.
require 'yaml'
def AccountFetcher
def self.accounts
#accounts ||= YAML.parse_file("config/providers.yaml")
end
#...
end
You may want to consider using a Gem like has_configuration that makes handling the data structure a bit easier.

How do I validate float values so that nothing but a float can be saved before submitting a form in ruby on rails 4?

I need a way to only allow values like:
Ok: 23.55, 232.43, 300.34 2.34
Not ok: 23.4, 43.344, 343.454, 230, 34
I have a regex in my model but it seems to allow me to save values like 200, 344, 23. I need to restrict things so that I'm only allowed to submit form when values are entered in the format of my Ok list.
Here is my model:
class Garment
include ActiveAttr::Model
#include ActiveModel::Validations
extend CarrierWave::Mount
attribute :price
mount_uploader :image, ImageUploader
price_regex = /\A(?:[1-9]+[0-9]*|0)(?:\.[0-9]{2})?\z/
validates :price, :presence => true,
:numericality => { :less_than => 301.00, :greater_than => 0.00 },
:format => {
:with => price_regex,
:message => "Price must be entered in the correct format e.g. 23.45, 203.43 not 43.3 or 234.5"
}
This is how I save the price entered into the form field:
def create
#garment = Garment.new(params[:garment])
if #garment.valid?
garment = Parse::Object.new("Garments")
garment["price"] = params[:garment][:price].to_f
garment.save
flash[:success] = "Garment successfully added to store!"
redirect_to '/adminpanel/show'
else
render "new"
end
end
I thought my regex was fine but I think I may need to tweak it more. I was wondering maybe I could some how check the value for a decimal and if it hasn't got one add one with 2 zeros after it before it is saved.
However I think the easiest most sensible way would be to do something before the actual form is submitted.
Would appreciate some help
Thanks for your help
Try a custom validator rather than a regex?
validate :valid_price_format
def valid_price_format
unless price.split('.')[1].try(:length) == 2
self.errors.add(:price, I18n.t('.invalid_format') )
end
end
Edited based on comments.
You can scope your translation if it's looking in the wrong place:
I18n.t('en.my.translation.location.invalid_format')
or
I18n.t('invalid_format', scope: 'en.my.translation.location')
Floats have quirks See Here.
Also 23.4 is a valid float. floats do not hold onto trailing zeros past the tenths place so 23.40 comes out as 23.4. You are better off storing prices as integer in cents e.g.
def price=(money)
#to_d returns a BigDecimal you could use to_f if you prefer
self.price = money.to_d * 100 if money
end
def price
#to_d returns a BigDecimal you could use to_f if you prefer
#bu BigDecimal is more accurate in comparisons
price.to_d / 100 if price
end
def display_price
#this will retun the price as in $XXXX.XX format as a String
sprintf("$%0.2f",price.to_f)
end
This way when you set it it will automatically convert it to an Integer and when using the getter method it will return a BigDecimal. Optionally you could leave off display_price method and use the helper method number_to_currency(price) in your views which will also add in commas and other configurable items. See number_to_currency
Also if you decide to forgo the above you can store them as decimals with the appropriate format using a migration like
add_column :table_name,:price, :decimal, precision: 8, scale: 2
Which will store them with 2 decimal places.
Update as a note
garment["price"] = params[:garment][:price].to_f
means that even if the user enters 12.00 it will be passed to save as 12.0 because of what is stated above. So you are assuming all prices do not end with a 0. Otherwise the next line
garment.save
Will fail silently and it will not be saved in the manor that you expect.
Also you are on rails 4 but not using strong_parameters?
#garment = Garment.new(params[:garment])
shouldn't this be
#garment = Garment.new(garment_params)
where garment_params utilizes a require and permit statement.
/^[1-9]*[0-9]+\.[0-9][0-9]$/ matches the "OK" values and rejects the "Not OK" values.
Share and enjoy.

Feed Array of Strings into Ruby Loop

I'd like to write a quick method that can help me initialize a few fields in one of my ruby tables. This is what I have so far but it does not work. I would like to be able to feed an array of field names into this function so that I can get the whole initialization done in one loop.
fields =["field1","field2","field3","field4"]
tasks = Task.all
tasks.each do |task|
fields.each do |field|
if task.field.nil?
task.update_attribute :field => true
end
end
end
Maybe this is what you mean:
fields = %w[field1 field2 field3 field4]
tasks = Task.all
tasks.each do |task|
fields.each do |field|
task.update_attribute :"#{field}" => true if task.send(field).nil?
end
end
If this is actually Rails, as it appears to be, you can use hash access:
task[field] = true if task[field].nil?
You would still need to save the modified record.
You may use task.update_attribute(field, true) instead: this will update the database immediately, but will do a transaction for each modified attribute.
Try to always use the least number of queries to the database
fields = ["field1","field2","field3","field4"]
fields.each do |field|
Task.where({field => nil}).update_all({field => true})
end

List dynamic attributes in a Mongoid Model

I have gone over the documentation, and I can't find a specific way to go about this. I have already added some dynamic attributes to a model, and I would like to be able to iterate over all of them.
So, for a concrete example:
class Order
include Mongoid::Document
field :status, type: String, default: "pending"
end
And then I do the following:
Order.new(status: "processed", internal_id: "1111")
And later I want to come back and be able to get a list/array of all the dynamic attributes (in this case, "internal_id" is it).
I'm still digging, but I'd love to hear if anyone else has solved this already.
Just include something like this in your model:
module DynamicAttributeSupport
def self.included(base)
base.send :include, InstanceMethods
end
module InstanceMethods
def dynamic_attributes
attributes.keys - _protected_attributes[:default].to_a - fields.keys
end
def static_attributes
fields.keys - dynamic_attributes
end
end
end
and here is a spec to go with it:
require 'spec_helper'
describe "dynamic attributes" do
class DynamicAttributeModel
include Mongoid::Document
include DynamicAttributeSupport
field :defined_field, type: String
end
it "provides dynamic_attribute helper" do
d = DynamicAttributeModel.new(age: 45, defined_field: 'George')
d.dynamic_attributes.should == ['age']
end
it "has static attributes" do
d = DynamicAttributeModel.new(foo: 'bar')
d.static_attributes.should include('defined_field')
d.static_attributes.should_not include('foo')
end
it "allows creation with dynamic attributes" do
d = DynamicAttributeModel.create(age: 99, blood_type: 'A')
d = DynamicAttributeModel.find(d.id)
d.age.should == 99
d.blood_type.should == 'A'
d.dynamic_attributes.should == ['age', 'blood_type']
end
end
this will give you only the dynamic field names for a given record x:
dynamic_attribute_names = x.attributes.keys - x.fields.keys
if you use additional Mongoid features, you need to subtract the fields associated with those features:
e.g. for Mongoid::Versioning :
dynamic_attribute_names = (x.attributes.keys - x.fields.keys) - ['versions']
To get the key/value pairs for only the dynamic attributes:
make sure to clone the result of attributes(), otherwise you modify x !!
attr_hash = x.attributes.clone #### make sure to clone this, otherwise you modify x !!
dyn_attr_hash = attr_hash.delete_if{|k,v| ! dynamic_attribute_names.include?(k)}
or in one line:
x.attributes.clone.delete_if{|k,v| ! dynamic_attribute_names.include?(k)}
So, what I ended up doing is this. I'm not sure if it's the best way to go about it, but it seems to give me the results I'm looking for.
class Order
def dynamic_attributes
self.attributes.delete_if { |attribute|
self.fields.keys.member? attribute
}
end
end
Attributes appears to be a list of the actual attributes on the object, while fields appears to be a hash of the fields that were predefined. Couldn't exactly find that in the documentation, but I'm going with it for now unless someone else knows of a better way!
try .methods or .instance_variables
Not sure if I liked the clone approach, so I wrote one too. From this you could easily build a hash of the content too. This merely outputs it all the dynamic fields (flat structure)
(d.attributes.keys - d.fields.keys).each {|a| puts "#{a} = #{d[a]}"};
I wasn't able to get any of the above solutions to work (as I didn't want to have to add slabs and slabs of code to each model, and, for some reason, the attributes method does not exist on a model instance, for me. :/), so I decided to write my own helper to do this for me. Please note that this method includes both dynamic and predefined fields.
helpers/mongoid_attribute_helper.rb:
module MongoidAttributeHelper
def self.included(base)
base.extend(AttributeMethods)
end
module AttributeMethods
def get_all_attributes
map = %Q{
function() {
for(var key in this)
{
emit(key, null);
}
}
}
reduce = %Q{
function(key, value) {
return null;
}
}
hashedResults = self.map_reduce(map, reduce).out(inline: true) # Returns an array of Hashes (i.e. {"_id"=>"EmailAddress", "value"=>nil} )
# Build an array of just the "_id"s.
results = Array.new
hashedResults.each do |value|
results << value["_id"]
end
return results
end
end
end
models/user.rb:
class User
include Mongoid::Document
include MongoidAttributeHelper
...
end
Once I've added the aforementioned include (include MongoidAttributeHelper) to each model which I would like to use this method with, I can get a list of all fields using User.get_all_attributes.
Granted, this may not be the most efficient or elegant of methods, but it definitely works. :)

Does a Markdown parser exist that can also generate Markdown in Ruby?

I want to parse a Markdown document so I get a tree structure that I am able to manipulate. Afterwards I want the output to be Markdown again.
Example:
# This is a title
And a short paragraph...
m = SomeLib.parse("# This is a tit...")
m.insert(1, "Here is a new paragraph") # or something simmilar
m.to_md
Should become
# This is a title
Here is a new paragraph
And a short paragraph...
As I want to heavily change the document I do not want to use REGEX or simillar techniques.
I looked into Maruku and BlueCloth but somehow I cannot generate Markdown again.
Probably not out of the box, but using redcarpet you could write a custom renderer to build your tree and then manipulate it.
Though beware in this case you can't reuse the Markdown and Renderer instance and all methods in the custom Renderer subclass are supposed to return a string. Something like this could be a starting point:
class StackRenderer < Redcarpet::Render::Base
attr_reader :items
def initialize
super
#items = []
end
def header(title, level)
items << { :text => title, :level => level, :type => :header }
"#{'#' * level} #{title}\n\n"
end
def paragraph(text)
items << { :text => text, :type => :paragraph }
"#{text}\n\n"
end
end
# example...
sr = StackRenderer.new
md = Redcarpet::Markdown.new(sr)
text = <<-EOF
# This is a title
And a short paragraph...
EOF
md.render(text) # => "# This is a title\n\nAnd a short paragraph...\n\n"
sr.items # => [{:type=>:header, :level=>1, :text=>"This is a title"},
# {:type=>:paragraph, :text=>"And a short paragraph..."}]

Resources