Unable to extend faker gem - ruby-on-rails

I am following instructions from:
http://cloudspace.com/blog/2013/10/18/extending-faker/#.VLdumx9sY8o
My /config/locales/faker.en.yml looks like:
en:
faker:
girls:
first_name: ["priyanka", "Tanya", "aditi", "Tanvi"]
last_name: ["Acharya", "Agarwal", "Agate", "Aggarwal"]
name:
- "#{first_name} #{last_name}"
And I have following: /lib/faker/girls.rb looks like:
module Faker
class Girl < Base
class << self
def first_name
parse('girls.first_name')
end
def last_name
parse('girls.last_name')
end
def name
fetch('girls.name')
end
end
end
end
Right after starting rails console I run: require Rails.root.join 'lib/faker/girls' to which a true is returned.
After that running following commands do not work as expected.
Output:
2.1.1 :004 > Faker::Girl.first_name => ""
2.1.1 :005 > Faker::Girl.last_name => ""
2.1.1 :006 > Faker::Girl.name => "\#{first_name} \#{last_name}"
Please help me find where I went wrong..

You mixed parse and fetch up: simple properties are to be fetched while composed are to be parsed. Another glitch is that your class name should correspond the yml (by convention):
# ⇓
class Girls < Base
class << self
def first_name
#⇓⇓⇓⇓⇓ it is a simple property
fetch('girls.first_name')
end
def last_name
#⇓⇓⇓⇓⇓ it is a simple property
fetch('girls.last_name')
end
def name
#⇓⇓⇓⇓⇓ it is a composed property
parse('girls.name')
end
...
Hope it helps.

Related

Active Record callbacks throw "undefined method" error in production with classes using STI

I have many instances in my application where I use single table inheritance and everything works fine in my development environment. But when I release to production (using passenger) I get the following error:
undefined method `before_save' for InventoryOrder:Class
(NoMethodError)
Why would this work in my dev environment and not work in production? Both are using Rails 4.2 and Ruby 2.1.5. Could this be a problem with passenger?
Here is the InventoryOrder class:
class InventoryOrder < Order
def self.model_name
Order.model_name
end
before_save :ensure_only_feed_types
def ensure_only_feed_types
order_products.each do |op|
if !ProductTypes::is_mix?(op.product_type.type)
raise Exceptions::FailedValidations, _("Can't have an inventory order for anything but mixes")
end
end
end
def self.check_if_replenishment_order_is_needed(product_type_id)
prod_type = ProductType.find(product_type_id)
return if prod_type.nil? || prod_type.min_system_should_have_on_hand.nil? || prod_type.min_system_should_have_on_hand == 0
amount_free = Inventory::inventory_free_for_type(product_type_id)
if prod_type.min_system_should_have_on_hand > amount_free
if prod_type.is_mix?
InventoryOrder::create_replenishment_order(product_type_id, prod_type.min_system_should_have_on_hand - amount_free)
else
OrderMoreNotification.create({subject: "Running low on #{prod_type.name}", body: "Should have #{prod_type.min_system_should_have_on_hand} of unreserved #{prod_type.name} but only #{amount_free} is left"})
end
end
end
def self.create_replenishment_order(product_type_id, amount)
# first check for current inventory orders
orders = InventoryOrder.joins(:order_products).where("order_products.product_type_id = ? and status <> ? and status <> ?", product_type_id, OrderStatuses::ready[:id], OrderStatuses::completed[:id])
amount_in_current_orders = orders.map {|o| o.order_products.map {|op| op.amount }.sum }.sum
amount_left_to_add = amount - amount_in_current_orders
if amount_left_to_add > 0
InventoryOrder.create({pickup_time: 3.days.from_now, location_id: Location::get_default_location.id, order_products: [OrderProduct.new({product_type_id: product_type_id, amount: amount_left_to_add})]})
end
end
def self.create_order_from_cancelled_order_product(order_product)
InventoryOrder.create({
pickup_time: DateTime.now.change({ min: 0, sec: 0 }) + 1.days,
location_id: Location::get_default_location.id,
order_products: [OrderProduct.new({
product_type_id: order_product.product_type_id,
feed_mill_job_id: order_product.feed_mill_job_id,
ration_id: order_product.ration_id,
amount: order_product.amount
})],
description: "Client Order for #{order_product.amount}kg of #{order_product.product_type.name} was cancelled after the feed mill job started."
})
end
end
And here is it's parent class:
class Order < ActiveRecord::Base
#active record concerns
include OrderProcessingInfo
belongs_to :client
belongs_to :location
has_many :order_products
before_destroy :clear_order_products
after_save :after_order_saved
before_save :on_before_save
accepts_nested_attributes_for :order_products, allow_destroy: true
after_initialize :init #used to set default values
validate :client_order_validations
def client_order_validations
if self.type == OrderTypes::client[:id] && self.client_id.nil?
errors.add(:client_id, _("choose a client"))
end
end
...
end
Thanks,
Eric
After doing some more digging and with the help of Roman's comment I was able to figure out that this issue was a result of me using an older convention for ActiveRecord::Concerns that works fine on windows but not on unix based systems.
According to this RailsCasts you can define your concerns like this:
In ../models/concerns/order/order_processing_info.rb
class Order
module OrderProcessingInfo
extend ActiveSupport::Concern
included do
end
...
end
But according to this the right way to define the concern would be to
1) Put it in ../models/concerns/[FILENAMEHERE] instead of ../models/concerns/[CLASSNAMEHERE]/[FILENAMEHERE]
2) Define the module without wrapping it in the class like this:
module OrderProcessingInfo
extend ActiveSupport::Concern
included do
end
end
Took some digging to get to the bottom of it but hopefully this might help someone else out there.

How to reuse symbols from a hash in another method in Ruby

I have the following method that defines a hash with a number of keys (there are a lot, I just cut it down for this example).
def data
#data ||= {
name: "Some Name",
email: "my#email.com"
}
end
Now, each of those keys I want to use in another method within the same class like so:
[:name, :email].each { |key| define_method("get_#{key}") { data[key] } }
While this works as it should, it doesn't seem to be a very good idea to hardcode the keys - I'd would much rather make them dynamic and have them reused from the hash I created within the first method. Since I am calling upon an Instance of this Class from another Class I get the following error when using the obvious approach:
data.keys.each { |key| define_method("get_#{key}") { data[key] } }
# => undefined local variable or method `data' for #<Class:0x0000000dc55938>
Any ideas how this could be solved?
As both methods are in same class why not use data instead of #data
2.1.2 :001 > def data
2.1.2 :002?> #data ||= {
2.1.2 :003 > name: "Some Name",
2.1.2 :004 > email: "my#email.com"
2.1.2 :005?> }
2.1.2 :006?> end
=> :data
2.1.2 :007 > data.keys.each { |key| define_method("get_#{key}") { data[key] } }
=> [:name, :email]
2.1.2 :008 > get_name
=> "Some Name"
2.1.2 :009 >
You can define method like data_keys. Use this method outside of your class and get keys.
class YourClass
def self.data
...
end
def self.data_keys
#data_keys ||= data.keys
end
end
YourClass.data_keys.each { |key| define_method("get_#{key}") { YourClass.data[key] } }
you can try something like this:--
def data(*args)
options = args.extract_options!
options ||= {
name: "Some Name",
email: "my#email.com"
}
##save it to instance variable for further use
#data=options
##either pass to another method
other_method(options)
##or call any other method to call private methods as well using send
new_obj =OtherObject.new
new_obj.send(:method_name,options)
end
same solution ,,using class variable
def data
##data ||= {
name: "Some Name",
email: "my#email.com"
}
end
in this way..you can even access data in model
def get_data
## ##data is available
end
If i'm understanding your requirements correctly, you want to have methods on each instance of the class that map (with a get/set prefix) to the keys in the data hash?
Although I despise magical logic in classes, could you not just define #method_missing and handle getting the value from the data hash based on that?
def method_missing method_symbol, *args, &block
if method_symbol.to_s.match(/^get_(.+)$/) && data.keys.include?($1.to_sym)
# Using the matching key in the data hash
data[$1.to_sym]
else
# Cannot detect method for current class, bubble method_missing to super class
super
end
end
It should also be noted that when overrideing method missing, you should always override #respond_to? as well

Strange behaviour when returning an array from class_eval'ed method

With Ruby 1.9.2, I'm using class_eval to extend a class.
def slugged(fields)
# assign string to variable only for easier debugging
method = <<-EOS
def slug_fields
#{ fields.is_a?(Array) ? fields.inspect : ":#{ fields }" }
end
EOS
class_eval method
end
This works fine as long as fields is a symbol (e.g. after slugged :name, slug_fields returns :name).
However, calling slugged with an array makes slug_fields returns nil (e.g. after slugged [:kicker, :headline], slug_fields returns nil).
Strangely, when debugging slugged, the string containing the to-be-created method looks exactly the way you would expect them to:
" def slug_fields\n [:kicker, :headline]\n end\n"
" def slug_fields\n :name\n end\n"
edit: as requested, a more complete version of what breaks for me:
module Extensions
module Slugged
extend ActiveSupport::Concern
included do
before_validation { |record| record.slug ||= record.sluggerize }
end
module ClassMethods
def slugged(fields)
# assign string to variable only for easier debugging
method = <<-EOS
def slug_fields
#{ fields.is_a?(Array) ? fields.inspect : ":#{ fields }" }
end
EOS
class_eval method
end
end
module InstanceMethods
def sluggerize
fields = slug_fields
slug_string = case
when fields.is_a?(Array)
fields.map { |f| self.send(f) }.join('-')
else
self.send fields
end
slug_string.parameterize
end
end
end
end
class Article < ActiveRecord::Base
include Extensions::Slugged
slugged [:kicker, :headline]
end
class Station < ActiveRecord::Base
include Extensions::Slugged
slugged :name
end
a = Article.new :headline => "this is a great headline!", :kicker => "attention-drawing kicker"
a.save # works, slug is set
s = Station.new :name => "Great Music"
s.save # TypeError: nil is not a symbol (in sluggerize where "self.send fields" is called)
Your code works fine for me under 1.9.2:
class Foo
class << self
def slugged(fields)
method = <<-EOS
def slug_fields
#{ fields.is_a?(Array) ? fields.inspect : ":#{ fields }" }
end
EOS
class_eval method
end
end
end
Foo.slugged :a
p Foo.new.slug_fields
#=> :a
Foo.slugged [:a,:b]
p Foo.new.slug_fields
#=> [:a, :b]
p RUBY_DESCRIPTION
#=> "ruby 1.9.2p180 (2011-02-18) [i386-mingw32]"
Can you please provide a complete, runnable, standalone test case that breaks for you?

Why isnt' this multiple field key working in Mongoid?

I've added this to my model:
key :name, :random_number
And I am using this callback:
before_create :create_random_number
But random_number is not getting appended to the _id using a method like this:
def create_random_number
rand(99999999999999999999)
end
This is the result that I get:
>> Product.create(name: "foo")
=> <Product _id: foo,
It turns out that you need to use after_initialize. This works for me:
key :slug
after_initialize :create_slug
def create_slug
name = self.name.gsub(' ', '-')
self.slug = "#{name}-#{rand(36**20).to_s(36)}"
end

How to convert a Ruby object to JSON

I would like to do something like this:
require 'json'
class Person
attr_accessor :fname, :lname
end
p = Person.new
p.fname = "Mike"
p.lname = "Smith"
p.to_json
Is it possible?
Yes, you can do it with to_json.
You may need to require 'json' if you're not running Rails.
To make your Ruby class JSON-friendly without touching Rails, you'd define two methods:
to_json, which returns a JSON object
as_json, which returns a hash representation of the object
When your object responds properly to both to_json and as_json, it can behave properly even when it is nested deep inside other standard classes like Array and/or Hash:
#!/usr/bin/env ruby
require 'json'
class Person
attr_accessor :fname, :lname
def as_json(options={})
{
fname: #fname,
lname: #lname
}
end
def to_json(*options)
as_json(*options).to_json(*options)
end
end
p = Person.new
p.fname = "Mike"
p.lname = "Smith"
# case 1
puts p.to_json # output: {"fname":"Mike","lname":"Smith"}
# case 2
puts [p].to_json # output: [{"fname":"Mike","lname":"Smith"}]
# case 3
h = {:some_key => p}
puts h.to_json # output: {"some_key":{"fname":"Mike","lname":"Smith"}}
puts JSON.pretty_generate(h) # output
# {
# "some_key": {
# "fname": "Mike",
# "lname": "Smith"
# }
# }
Also see "Using custom to_json method in nested objects".
Try it. If you're using Ruby on Rails (and the tags say you are), I think this exact code should work already, without requiring anything.
Rails supports JSON output from controllers, so it already pulls in all of the JSON serialization code that you will ever need. If you're planning to output this data through a controller, you might be able to save time by just writing
render :json => #person

Resources