Ruby require loop - ruby

I have the following code (simplified):
decorator.rb
require 'decoratable'
class Decorator < SimpleDelegator
include Decoratable
end
decoratable.rb
require 'decorator_builder'
module Decoratable
def decorate(*decorators)
decorators.inject(DecoratorBuilder.new(self)) do |builder, decorator|
builder.public_send(decorator)
end.build
end
end
decorator_builder.rb
require 'rare_decorator'
class DecoratorBuilder
def initialize(card)
#card = card
#decorators = []
end
def rare
#decorators << ->(card) { RareDecorator.new(card) }
self
end
def build
#decorators.inject(#card) do |card, decorator|
decorator.call(card)
end
end
end
rare_decorator.rb
require 'decorator'
class RareDecorator < Decorator
# Stuff here
end
When I require decorator.rb, it causes RareDecorator to be declared before Decorator is declared, which is a problem since RareDecorator inherits from Decorator.
A possible solution is to split up decorator.rb like so:
class Decorator < SimpleDelegator; end
require 'decoratable'
class Decorator
include Decoratable
end
However, declaring dependencies in the middle of a file doesn't seem doesn't seem like a very clean solution to me.
Is there a better solution to this problem?

Instead of specifying requirements within every file, create one file which will require all the the application's requirements. Call it for example environment.rb:
require 'decoratable'
require 'decorator'
require 'decorator_builder'
require 'rare_decorator'
You don't need to worry about Decoratable not knowing what DecoratorBuilder is, as it is used within the method and the check for the constant will be executed when this method is called. Since you require decorator moment later, all will work.

Related

Handling a method from outside of a mixin module in ruby and rspec

I have a class that looks like this:
module ReusableBitlyLinks
def shorten_url url, *args
ShortenedUrl.shorten_url_with_bitly( url, email.user )
end
end
I have a test that looks like this:
require File.expand_path("../../../../app/decorators/mixins/reusable_bitly_links", __FILE__)
include ReusableBitlyLinks
describe ReusableBitlyLinks do
describe "shorten_url" do
it "works" do
ReusableBitlyLinks.shorten_url('asdf').should == 'asdf'
end
end
end
When I run the test I get an error that says:
uninitialized constant ReusableBitlyLinks::ShortenedUrl
How do I mock stub ReusableBitlyLinks::ShortenedUrl?
Is ShortenedUrl defined inside ReusableBitlyLinks module? If not - try to access it with ::ShortenedUrl.shorten_url_with_bitly.
Not sure what stubbing has to do with it...
In your mixin module, you need to tell it that ShortenedUrl is from outside the module by prepending :::
module ReusableBitlyLinks
def shorten_url(url, *args)
::ShortenedUrl.shorten_url_with_bitly(url, email.user)
end
end
You may also need to do a require inside the mixin file to load whatever file it is that defines ShortenedUrl.
Further, in your test file, the line:
require File.expand_path("../../../../app/decorators/mixins/reusable_bitly_links", __FILE__)
could be simplified to:
require_relative '../../../../app/decorators/mixins/reusable_bitly_links'

Unable to use ActiveModel::MassAssignmentSecurity

I am trying to use some functionality in ActiveModel but I'm having trouble making everything work. I've included my class file and the test I'm running.
The test is failing with:
': undefined method `attr_accessible
I really don't know why, since MassAssignmentSecurity will bring that in and it is in fact running. I've also tried to include all of ActiveModel as well but that's doesn't work either. It doesn't seem to matter if I use include or extend to bring in the MassAssignmentSecurity.
If I pass in some attributes in my test to exercise "assign_attributes" in the initialize, that fails as well. I'm fairly new to rails, so I'm hoping I'm just missing something really simple.
TIA.
Using rails 3.2.12
my_class.rb
class MyClass
include ActiveModel::MassAssignmentSecurity
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
extend ActiveSupport::Callbacks
attr_accessible :persisted, :creds
def initialize(attributes = nil, options = {})
#persisted = false
assign_attributes(attributes, options) if attributes
yield self if block_given?
end
end
my_class_spec.rb
require 'spec_helper'
describe MyClass do
before do
#testcase = MyClass.new
end
subject { #testcase }
it_should_behave_like "ActiveModel"
it { MyClass.should include(ActiveModel::MassAssignmentSecurity) }
it { should respond_to(:persisted) }
end
support/active_model.rb
shared_examples_for "ActiveModel" do
include ActiveModel::Lint::Tests
# to_s is to support ruby-1.9
ActiveModel::Lint::Tests.public_instance_methods.map{|m| m.to_s}.grep(/^test/).each do |m|
example m.gsub('_',' ') do
send m
end
end
def model
subject
end
end
Yikes! What a mess I was yesterday. Might as well answer my own question since I figured out my issues.
attr_accessible in MassAssignmentSecurity does not work like it does with ActiveRecord. It does not create getters and setters. You still have to use attr_accessor if you those created.
assign_attributes is a connivence function that someone wrote to wrap around mass_assignment_sanitizer and isn't something baked into in MassAssignment Security. An example implementation is below:
def assign_attributes(values, options = {})
sanitize_for_mass_assignment(values, options[:as]).each do |k, v|
send("#{k}=", v)
end
end

Monkey patch class methods

I'm trying to do some monkey patching in ActiveShipping UPS class .
I need to add a class level method (starting with .self), so here it's what I'm trying to do:
module ActiveMerchant
module Shipping
class UPS < Carrier
def self.process_request(receiver, sender, packages, options = {})
# some code
end
def regular_method
"foobar"
end
end
end
end
Unfortunately when I'm trying to use it:
ActiveMerchant::Shipping::UPS.process_request(receiver etc)
I get an error:
NoMethodError: undefined method `process_request' for ActiveMerchant::Shipping::UPS:Class
from (irb):6
from C:/Ruby19/bin/irb.bat:19:in `<main>'
There is no class method named process_request in original class.
In original UPS class provided in gem there is one static method defined self.retry_safe = true
and I can use it without errors.
I can also use regular_method after creating instance of UPS class.
More details provided:
I'm working with Rails 2.3 ( :-( ) and Ruby 1.9.2. I have no influce on environment.
Monkey patched code is under plugins/my_plugin/lib/active_shipping/ext/carriers/ups.rb
In /active_shipping I have file named extensions.rb in which i have:
require 'active_shipping'
require_relative 'ext/carriers'
require_relative 'ext/carriers/ups'
It deals with loading everything properly (I suppose basing on regular_method beheaviour from first chunk of code in my question).
I try to invoke process_request in one of my Controllers. This part is little tricky, beacuse i'm using sth like this:
MyModel.courier_service.process_request(parameters)
where courier_service, in this case holds the ActiveMerchant::Shipping::UPS class.
I'm still a newbie in Ruby and don't know what sort of details i should provide.
Maybe you want to do it in another way
File patch_classes.rb:
module ActiveMerchantExpand
module Shipping
module ClassMethods
def self.process_request(receiver, sender, packages, options = {})
# some code
end
end
module InstanceMethods
def regular_method
"foobar"
end
end
def self.included(receiver)
receiver.extend ClassMethods
receiver.send :include, InstanceMethods
end
end
end
Then you have to load your class "ActiveMerchant::Shipping::UPS"
and after that you can attach your methods to your class via
Rails.configuration.to_prepare do
require_dependency [[file for ActiveMerchant::Shipping::UPS]]
require 'patch_classes' )
ActiveMerchant::Shipping::UPS.send(:include, ::ActiveMerchantExpand::Shipping)
end
This is from rails plugin writing, i hope this helps.
regards tingel2k
Do you explicitly require file with your monkey patch? If you just put it under your app or lib path without requiring, it wouldn't load because constant ActiveMerchant::Shipping::UPS is defined in gem and it doesn't trigger dependency resolution mechanism.

With Test::Unit, how can I run a bit of code before all tests (but not each test)?

In my test app, which uses test::unit, I need to start by pulling a bunch of data from various sources. I'd like to only do this once - the data is only read, not written, and doesn't change between tests, and the loading (and error checking for the loading), takes some time.
There are values that I DO want reset every time, and those are easy enough, but what if I want persistant accessible values? What's the best way to do this?
I'm especially interested in solutions that would let my push those assignments to some module that can be included in all my tests, since they all need access to this data.
Why do you need it inside the test? You could define it gloabl:
gem 'test-unit'#, '>= 2.1.1' #startup
require 'test/unit'
GLOBAL_DATA = 11
class My_Tests < Test::Unit::TestCase
def test_1()
puts "Testing startup 1"
assert_equal(11, GLOBAL_DATA)
end
end
GLOBAL_DATA could be a (singleton)-class (respective an instance).
If you have only one testclass, you may use TestCase.startup:
gem 'test-unit'#, '>= 2.1.1' #startup
require 'test/unit'
class My_Tests < Test::Unit::TestCase
def self.startup
puts "Define global_data "
##global_data = 11
end
def test_1()
puts "Testing 1"
assert_equal(11, ##global_data = 11)
end
def test_2()
puts "Testing 2"
assert_equal(11, ##global_data = 11)
end
end
You can just put them at the top of the class. They will get executed, and then your tests will get executed.
You could do this in the setup method:
def setup
if !defined?(##initial_data)
# Whatever you need to do to get your initial data
##initial_data = foo
end
#other_data = bar
end

Should the Applicant class "require 'mad_skills'" or "include 'mad_skills'"?

Also, what does "self.send attr" do? Is attr assumed to be a private instance variable of the ActiveEngineer class? Are there any other issues with this code in terms of Ruby logic?
class Applicant < ActiveEngineer
require 'ruby'
require 'mad_skills'
require 'oo_design'
require 'mysql'
validates :bachelors_degree
def qualified?
[:smart, :highly_productive, :curious, :driven, :team_player ].all? do
|attr|
self.send attr
end
end
end
class Employer
include TopTalent
has_millions :subscribers, :include=>:mostly_women
has_many :profits, :revenue
has_many :recent_press, :through=>[:today_show, :good_morning_america,
:new_york_times, :oprah_magazine]
belongs_to :south_park_sf
has_many :employees, :limit=>10
def apply(you)
unless you.build_successful_startups
raise "Not wanted"
end
unless you.enjoy_working_at_scale
raise "Don't bother"
end
end
def work
with small_team do
our_offerings.extend you
subscribers.send :thrill
[:scaling, :recommendation_engines, : ].each do |challenge|
assert intellectual_challenges.include? challenge
end
%w(analytics ui collaborative_filtering scraping).each{|task|
task.build }
end
end
end
def to_apply
include CoverLetter
include Resume
end
require 'mad_skills' loads the code in mad_skills.rb (or it loads mad_skills.so/.dll depending on which one exists). You need to require a file before being able to use classes, methods etc. defined in that file (though in rails files are automatically loaded when trying to access classes that have the same name as the file). Putting require inside a class definition, does not change its behaviour at all (i.e. putting it at the top of the file would not make a difference).
include MadSkills takes the module MadSkills and includes it into Applicant's inheritance chain, i.e. it makes all the methods in MadSkills available to instances of Applicant.
self.send attr executes the method with the name specified in attr on self and returns its return value. E.g. attr = "hello"; self.send(attr) will be the same as self.hello. In this case it executes the methods smart, highly_productive, curious, driven, and team_player and checks that all of them return true.

Resources