Ruby/Rails: accessing a variable inside a .each on my instance variable array causing Ruby interpreter to crash - ruby

I've written a mixin (based on something I read in a blog) that seems to be causing a problem
Here is a link to the project: http://www.filehosting.org/file/details/263759/onlinescheduler.zip (or send me an email: aaron.a.ashworth#gmail.com and I'll email it) and I've stripped as much out as I can for it to still cause the problem. The key files to look at are:
/lib/user_role.rb (near line 11)
/app/views/customers/index.html.erb (near line 16)
/app/controllers/customers_controller.rb (near line 47)
I'll layout the important stuff here as well:
/lib/user_role.rb:
module UserRole
def self.included(base)
base.has_one :user, :as => :user_role, :autosave => true
base.validate :user_must_be_valid
base.alias_method_chain :user, :autobuild
base.extend ClassMethods
base.define_user_accessors
end
def user_with_autobuild
user_without_autobuild || build_user
end
def method_missing(meth, *args, &blk)
user.send(meth, *args, &blk)
rescue NoMethodError
super
end
module ClassMethods
def define_user_accessors
all_attributes = User.content_columns.map(&:name) + ["password", "password_confirmation"]
ignored_attributes = ["created_at", "updated_at", "user_role_type"]
attributes_to_delegate = all_attributes - ignored_attributes
attributes_to_delegate.each do |attrib|
class_eval <<-RUBY
def #{attrib}
user.#{attrib}
end
def #{attrib}=(value)
self.user.#{attrib} = value
end
def #{attrib}?
self.user.#{attrib}?
end
RUBY
end
end
end
protected
def user_must_be_valid
Logger.new(STDOUT).info('calling user_must_be_valid')
unless user.valid?
user.errors.each do |attr, message|
errors.add(attr, message)
end
end
end
end
app/views/customers/index.html.erb:
...
<% #customers.each do |customer| %>
<tr>
<td><%= customer.account_id %></td>
...
accessing customer at all causes the problem. I can do anything to #customers
but as soon as I try to access customer.... or even #customers[0].... I get a problem.
Steps to produce
1) After unzipping the file, go into the root directory in terminal and run these commands:
bundle install
bundle exec rake db:drop
bundle exec rake db:migrate
rails s
2) Open your browser to localhost:3000/customers and click New Customer
3) Fill in the form you see like so:
Account: 3
First Name: First
Last Name: Last
Email: first.last#domain.com
Password: 1234
Password confirmation: 1234
4) Click the Create Customer button.
Expected Behavior
You should be redirected to localhost:3000/customers/1
Current Behaviour
The webserver crashes as you get the following message:
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(+0x17b356) [0x7fef4a97e356]
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(+0x1713ee) [0x7fef4a9743ee]
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(+0x177243) [0x7fef4a97a243]
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(rb_vm_invoke_proc+0x9f) [0x7fef4a97b08f]
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(+0x17b644) [0x7fef4a97e644]
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(+0x1713ee) [0x7fef4a9743ee]
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(+0x177243) [0x7fef4a97a243]
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(+0x1784f4) [0x7fef4a97b4f4]
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(+0x178cb5) [0x7fef4a97bcb5]
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(+0x17b50d) [0x7fef4a97e50d]
~/.rvm/rubies/ruby-1.9.2-p290/lib/libruby.so.1.9(+0x1713ee) [0x7fef4a9743ee]
[NOTE]
You may have encountered a bug in the Ruby interpreter or extension libraries.
Bug reports are welcome.
For details: http://www.ruby-lang.org/bugreport.html
Rerunning the webserver and going to localhost:3000/customers sometimes gives you a different error. A segmentation fault and it complains about /lib/user_role.rb:11
Environment
Ruby 1.9.2-p290
Rails 3.0.9
RVM 1.8.1
Ubuntu 11.04
Edit
Something to note: If you try working the same code that bombs in console it seems fine. Example:
(After entering rails c)
#customers = Customer.all
#customers.each do |customer|
p customer.account_id
end
# this doesn't cause an error or crash.
#customer[0].first_name
=> "First"

If you remove:
def method_missing(meth, *args, &blk)
customer.send(meth, *args, &blk)
rescue NoMethodError
super
end
and
def method_missing(meth, *args, &blk)
user.send(meth, *args, &blk)
rescue NoMethodError
super
end
from the x_role files in your lib directory, it should work fine. On a side note look into inherited resource for your controllers and simple form for your forms.

Related

How to fix the problem, When I try authentication on local system its working perfectly, but when uploaded to heroku it comes back with error 500?

I am new to rails and react, this might be a simple one but i cant seem to figure it out.
I am trying to implement a simple jwt authentication using ruby on rails with react as client. I followed the steps that was suggested in :
https://www.pluralsight.com/guides/token-based-authentication-with-ruby-on-rails-5-api
It works as expected on my local system but when i uploaded my app on to heroku it always comes back with error : 500. All the other 'Post' and 'Get' requests work normally. Its only when i try to authenticate and get the auth_token back it runs into 500 error.
this is the request format
post: localhost:3001/api/authenticate
and body:
{
"email": "evin#xyz.com",
"password": "evin"
}
I verified that this data is available on heroku by using get which works perfectly.
I have been working on resolving this for over 2 days now. There is very little information available online on this authentication. There was plenty of recommendations on using auth0. But i could not find much help with this form of authentication.
This is what i have
#Path: /app/controllers/application_controller.rb
class ApplicationController < ActionController::API
before_action :authenticate_request
attr_reader :current_user
private
def authenticate_request
#current_user = AuthorizeApiRequest.call(request.headers).result
render json: { error: 'Not Authorized' }, status: 401 unless #current_user
end
end
#Path: app/controllers/api/authentication_controller.rb
class Api::AuthenticationController < ApplicationController
skip_before_action :authenticate_request
def authenticate
command = AuthenticateUser.call(params[:email], params[:password])
if command.success?
render json: { auth_token: command.result }
else
render json: { error: command.errors }, status: :unauthorized
end
end
end
#Path: /app/commands/authenticate_user.rb
class AuthenticateUser
prepend SimpleCommand
def initialize(email, password)
#email = email
#password = password
end
def call
JsonWebToken.encode(user_id: user.id) if user
end
private
attr_accessor :email, :password
def user
user = User.find_by_email(email)
return user if user && user.authenticate(password)
errors.add :user_authentication, 'invalid credentials'
nil
end
end
#Path: /app/commands/authorize_api_request.rb
class AuthorizeApiRequest
prepend SimpleCommand
def initialize(headers = {})
#headers = headers
end
def call
user
end
private
attr_reader :headers
def user
#user ||= User.find(decoded_auth_token[:user_id]) if decoded_auth_token
#user || errors.add(:token, 'Invalid token') && nil
end
def decoded_auth_token
#decoded_auth_token ||= JsonWebToken.decode(http_auth_header)
end
def http_auth_header
if headers['Authorization'].present?
return headers['Authorization'].split(' ').last
else
errors.add(:token, 'Missing token')
end
nil
end
end
#Path: /lib/json_web_token.rb
class JsonWebToken
class << self
def encode(payload, exp = 24.hours.from_now)
payload[:exp] = exp.to_i
JWT.encode(payload, Rails.application.secrets.secret_key_base)
end
def decode(token)
body = JWT.decode(token, Rails.application.secrets.secret_key_base)[0]
HashWithIndifferentAccess.new body
rescue
nil
end
end
end
#path: /config/application.rb
require_relative 'boot'
require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
require "active_job/railtie"
require "active_record/railtie"
require "active_storage/engine"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
require "action_cable/engine"
# require "sprockets/railtie"
require "rails/test_unit/railtie"
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module Deveycon
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 5.2
# Settings in config/environments/* take precedence over those specified here.
# Application configuration can go into files in config/initializers
# -- all .rb files in that directory are automatically loaded after loading
# the framework and any gems in your application.
#Autoload lib for encrypt and decrypt
config.autoload_paths << Rails.root.join('lib')
# Only loads a smaller set of middleware suitable for API only apps.
# Middleware like session, flash, cookies can be added back manually.
# Skip views, helpers and assets when generating a new resource.
config.api_only = true
end
end
I had similar issues, the API works perfectly on localhost after uploading to Heroku, I still got unauthorized on secure pages even with the token on the headers.
I added
production:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
to config/secrets.yml
Please check the more details log of your heroku application by using Heroku CLI.
heroku logs -t
If the problem with AuthenticateUser::JsonWebToken use auto loaded in your
config/application.rb
class Application < Rails::Application
#.....
config.autoload_paths << Rails.root.join('lib')
#.....
end
I hope that helpful to resolve your issue.
In #lib/JsonWebToken:
Just increase the exp time of token and replace .secrets.secret_key_base with
.credentials.read
class JsonWebToken
class << self
def encode(payload, exp = 1200.hours.from_now)
payload[:exp] = exp.to_i
JWT.encode(payload, Rails.application.credentials.read)
end
def decode(token)
body = JWT.decode(token, Rails.application.credentials.read)[0]
HashWithIndifferentAccess.new body
rescue
nil
end
end
end

How to upgrade redmine plugin to rails 5, alias_method_chain is deprecated now

Story mode
Just started learning RoR, but in short period of time I need to add functionality similar to Loading images from LDAP (incompatible version) into our project. Project is abondoned, and I can't find any related info/docs, so I'm asking for help here. Solution, tutorial, anything could work.
Error log
$ ruby bin/rake redmine:plugins RAILS_ENV="production"
rake aborted!
NoMethodError: undefined method `alias_method_chain' for ApplicationHelper:Module
Did you mean? alias_method
...
Monkey patch that needs update
plugins\redmine_gemavatar\lib\application_helper_gemavatar_patch.rb :
require 'application_helper'
module GemAvatarPlugin
module ApplicationAvatarPatch
def self.included(base)
base.send(:include, InstanceMethods)
base.class_eval do
alias_method_chain :avatar, :gemavatar
end
end
module InstanceMethods
def avatar_with_gemavatar(user, options = { })
if Setting.gravatar_enabled? && user.is_a?(User)
options.merge!({:ssl => (defined?(request) && request.ssl?), :default => Setting.gravatar_default})
options[:size] = "64" unless options[:size]
avatar_url = url_for :controller => :pictures, :action => :delete, :user_id => user
return "<img class=\"gravatar\" width=\"#{options[:size]}\" height=\"#{options[:size]}\" src=\"#{avatar_url}\" />".html_safe
else
''
end
end
end
end
end
My attempts / Articles
I've found good article here How To Replace alias_method_chain, but I'm not quite sure how to apply prepend style to redmine plugin's monkey patch. Just can't get it work :/
Is this related to this plugin?
If so, here is how I would do it:
In the init.rb file, change this:
RedmineApp::Application.config.after_initialize do
ApplicationHelper.send(:include, GemAvatarPlugin::ApplicationAvatarPatch)
end
To this:
RedmineApp::Application.config.after_initialize do
ApplicationHelper.prepend(GemAvatarPlugin::ApplicationAvatarPatch)
end
In lib/application_helper_gemavatar_patch.rb, change this:
require 'application_helper'
module GemAvatarPlugin
module ApplicationAvatarPatch
def self.included(base)
base.send(:include, InstanceMethods)
base.class_eval do
alias_method_chain :avatar, :gemavatar
end
end
module InstanceMethods
def avatar_with_gemavatar(user, options = { })
# method content omitted for clarity
end
end
end
end
to this:
module GemAvatarPlugin
module ApplicationAvatarPatch
def avatar(user, options = { })
# method content omitted for clarity
end
end
end
I would remove the require 'application_helper' because I don't see why it's needed
I also tried to update this plugin. I used https://github.com/alexandermeindl/redmine_local_avatars as an example.
In init.rb changed this:
RedmineApp::Application.config.after_initialize do
ApplicationHelper.send(:include, GemAvatarPlugin::ApplicationAvatarPatch)
end
to this:
RedmineApp::Application.config.after_initialize do
ApplicationHelper.include ApplicationAvatarPatch
end
the patched lib/application_helper_gemavatar_patch.rb, looks like this:
module ApplicationAvatarPatch
def self.included(base)
base.send(:include, InstanceMethods)
base.class_eval do
alias_method :avatar_without_gemavatar, :avatar
alias_method :avatar, :avatar_with_gemavatar
end
end
module InstanceMethods
def avatar_with_gemavatar(user, options = { })
if Setting.gravatar_enabled? && user.is_a?(User)
options.merge!({:ssl => (defined?(request) && request.ssl?), :default => Setting.gravatar_default})
options[:size] = "64" unless options[:size]
avatar_url = url_for :controller => :pictures, :action => :delete, :user_id => user
return "<img class=\"gravatar\" width=\"#{options[:size]}\" height=\"#{options[:size]}\" src=\"#{avatar_url}\" alt=\"Gemavatar text\" />".html_safe
else
avatar_without_gemavatar(user, options)
end
end
end
end
I tried it only with Redmine 4.0.3 but it seems to be working. However there are warnings in the webserver log:
127.0.0.1 - - [06/Mar/2020:20:58:23 CET] "GET /gemavatar/164 HTTP/1.1" 200 3545
http://tredmine1:3000/users/164 -> /gemavatar/164
[2020-03-06 20:58:23] WARN Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
In the redmine_local_avatars plugin there was a different patch for Redmine 4.1.
Update: I set a up a github repository with the changes: https://github.com/pentekl/redmine_gemavatar
You can use alias_method instead of alias_method_chain, but I'm searching for something like prepend solution
alias_method :avatar_without_gemavatar, :avatar
alias_method :avatar, :avatar_with_gemavatar
UPD:
But it throws warnings:
/app/helpers/application_helper.rb:180: warning: already initialized constant ApplicationHelper
::RECORD_LINK
/app/helpers/application_helper.rb:180: warning: previous definition of RECORD_LINK was here
/app/helpers/application_helper.rb:199: warning: already initialized constant ApplicationHelper
::ATTACHMENT_CONTAINER_LINK
/app/helpers/application_helper.rb:199: warning: previous definition of ATTACHMENT_CONTAINER_LI
NK was here
/app/helpers/application_helper.rb:1053: warning: already initialized constant ApplicationHelpe
r::LINKS_RE
/app/helpers/application_helper.rb:1053: warning: previous definition of LINKS_RE was here
Exiting
UPD:
As ste26054 mentioned in his answer and commented here
require 'application_helper' can be deleted to prevent warnings, since it already included in the core.

alternative to method_missing in Chef 13

upgraded the system from chef 12 to 13 facing issue with method_missing
found from some links that method_missing is deprecated is there any alternative for this.
def method_missing(name, *args, &block)
# Build the set of names to check for a valid resource
lookup_path = ["application_#{name}"]
run_context.cookbook_collection.each do |cookbook_name, cookbook_ver|
if cookbook_name.start_with?("application_")
lookup_path << "#{cookbook_name}_#{name}"
end
end
lookup_path << name
resource = nil
lookup_path = ["application_#{name}"]
# Try to find our resource
lookup_path.each do |resource_name|
begin
Chef::Log.debug "Trying to load application resource #{resource_name} for #{name}"
resource = super(resource_name.to_sym, *args, &block)
break
rescue NameError => e
# Works on any MRI ruby
if e.name == resource_name.to_sym || e.inspect =~ /\b#{resource_name}\b/
next
else
raise e
end
end
end
raise NameError, "No resource found for #{name}. Tried #{lookup_path.join(', ')}" unless resource
# Enforce action :nothing in case people forget
resource.action :nothing
# Make this a weakref to prevent a cycle between the application resource and the sub resources
resource.application WeakRef.new(self)
resource.type name
#sub_resources << resource
resource
end
Ahh I see the real question in comments. The application_php cookbook was never upgraded to work with the rest of the newer application cookbook suite. You would have to write your own version of it.
For anyone else finding this question via Google: the method_missing thing is a total red herring. The user was confused because in Chef we removed a method_missing-based syntax for node attributes. This is unrelated to my use of it for subresource management.

Issue stubbing with RSpec

I am trying to understand why the result of these tests, the first test claims the method is not stubbed, however, the 2nd one is.
class Roll
def initialize
install if !installed?
end
def install; puts 'install'; end
end
describe Roll do
before do
class RollTestClass < Roll; end
RollTestClass.any_instance.stub(:install)
end
let(:roll_class) { RollTestClass }
let(:roll) { RollTestClass.new }
context 'when installed is true' do
before do
roll_class.any_instance.stub(:installed?).and_return(true)
end
it 'should not call install' do
expect(roll).to_not have_received(:install)
end
end
context 'when installed is false' do
before do
roll_class.any_instance.stub(:installed?).and_return(false)
end
it 'should call install' do
expect(roll).to have_received(:install)
end
end
end
It's also strange the error says expected to have received install, but I think that is likely just faulty feedback from the RSpec DSL. But maybe worth noting.
1) Roll when installed is true should not call install
Failure/Error: expect(roll).to_not have_received(:install)
#<RollTestClass:0x10f69ef78> expected to have received install, but that method has not been stubbed.
The "spy pattern" of RSpec requires that the objects have been previously stubbed. However, any_instance.stub doesn't actually stub the methods "for real" unless/until the method is invoked on a particular object. As such, the methods appears as being "unstubbed" and you get the error you're getting. Here's some code that demonstrates the change in definition:
class Foo
end
describe "" do
it "" do
Foo.any_instance.stub(:bar)
foo1 = Foo.new
foo2 = Foo.new
print_bars = -> (context) {puts "#{context}, foo1#bar is #{foo1.method(:bar)}, foo2#bar is #{foo2.method(:bar)}"}
print_bars['before call']
foo1.bar
print_bars['after call']
end
end
which produces the following output:
before call, foo1#bar is #<Method: Foo#bar>, foo2#bar is #<Method: Foo#bar>
after call, foo1#bar is #<Method: #<Foo:0x007fc0c3842ef8>.bar>, foo2#bar is #<Method: Foo#bar>
I reported this an issue on RSpec's github site and got this acknowledgement/response.
You can use the following alternative approach, which depends on the recently introduced expect_any_instance_of method.
class Roll
def initialize
install if !installed?
end
def install; puts 'install'; end
end
describe Roll do
before do
class RollTestClass < Roll; end
end
let(:roll_class) { RollTestClass }
let(:roll) { RollTestClass.new }
context 'when installed is true' do
before do
roll_class.any_instance.stub(:installed?).and_return(true)
end
it 'should not call install' do
expect_any_instance_of(roll_class).to_not receive(:install)
roll
end
end
context 'when installed is false' do
before do
roll_class.any_instance.stub(:installed?).and_return(false)
end
it 'should call install' do
expect_any_instance_of(roll_class).to receive(:install)
roll
end
end
end

Testing #current_user method using RSpec

I've been trying to do this for a couple of days now, but I can't figure it out. I have the following code in my controller:
#some_object = #current_user.some_method
In my spec, I want to attach a should_receive hook on that method, but I can't make it work. I've tried all of these, but none of them work:
assigns[:current_user].should_receive(:some_method).at_least(:once) # expected 1, got 0
User.should_receive(:some_method).at_least(:once) # expected 1, got 0
How is the correct way of testing this? I'm running this in my spec, and login is working:
setup :activate_authlogic
...
UserSession.create(users(:rune))
Thanks!
One example comes from the Ruby on Rails Tutorial. Rather than setting and reading #current_user directly, it defines two helper methods:
def current_user=(user)
#current_user = user
end
def current_user
#current_user
end
Later, they access this method in the tests using the controller method:
def test_sign_in(user)
controller.current_user = user
end
Using this methodology, you should be able to use
controller.current_user.should_receive(:some_method).at_least(:once)
You can’t call something like in the controllers:
expect(current_user).to be_present
expect(user_signed_in?).to be_true
So to do so, you can do this :
module ControllerMacros
def current_user
user_session_info = response.request.env['rack.session']['warden.user.user.key']
if user_session_info
user_id = user_session_info[0][0]
User.find(user_id)
else
nil
end
end
def user_signed_in?
!!current_user
end
end
You can either include the ControllerMacros in the top of the controller spec or include it in the spec_helper.rb like so :
RSpec.configure do |config|
config.include ControllerMacros, type: :controller
end

Resources