Spree::OrderPopulator accessing outside of store - ruby-on-rails

On Rails 4.1 .. being new to Spree 2-3-stable and experimenting with various things. My test store works fine and now I would am trying and integrate it into my application which is a booking site.
On my Bookings calendar I would like users to click on a booking link and have that auto populate their cart, then show their cart directly.
So assuming I have a spree product, and a variant existing in the database, and the current_user is logged in (ie spree_current_user exists) .. I thought this would be possible but it seems it will not even create the populator.
class BookingsController < ApplicationController
def add_booking_to_cart
populator = Spree::OrderPopulator.new Spree::Order.current_order(create_order_if_necessary: true), current_currency
# .. get variant related to booking
# .. add varient to populator
end
#... rest of bookings controller
end
Errors out with:
NoMethodError - undefined method `current_order' for #<Class:0x00000007d4fc68>:
I am attempting to do a similar thing to what Will is doing here but am getting the error undefined method on current_order object. My understanding of the code is that it should create a new order if necessary, ie if one does not exist. No sure what do to here? Any pointer would be greatly aprecieated.
https://github.com/spree/spree/blob/2-3-stable/frontend/app/controllers/spree/orders_controller.rb#L46

For this to work your controller needs to extend Spree::StoreController
class BookingController < Spree::StoreController
This is where the method is defined in Spree:
https://github.com/spree/spree/blob/master/core/lib/spree/core/controller_helpers/order.rb
Also, you might need to have a before_filter :set_current_order.

Related

Access current user within liquid drop rails 5

I'm currently implementing liquid templates in my application. As part of that I have created a set of liquid drop (https://github.com/Shopify/liquid/wiki/Trying-to-Understand-Drops) classes to act as intermediates between my models and my templates. I'm currently using devise for authentication on rails 5.
In my product drop class I would like to be able to check if my current user owns the product:
class ProductDrop < Liquid::Drop
def initialize(model)
#model = model
end
def owned_by_user?
#somehow access the current_user provided by devise.
end
end
But haven't been able to figure out how to access the user.
I notice in this method on shopify: https://help.shopify.com/en/themes/liquid/objects/variant#variant-selected
They are able to access the current url to work out if the variant is selected. I thought perhaps it might be possible if they can access the url, to access the session and get the user identifier to look up the user.
So I can do something like:
def owned_by_user?
User.find_by_id(session[:user_id]).owns_product?(#model.id)
end
I'm not having any luck accessing the session. Does anyone have any suggestions or ideas? Or am I going about this completely the wrong way?
So after digging around in the liquid drop source code. I noticed that the context is accessible to the drop (https://github.com/Shopify/liquid/blob/master/lib/liquid/drop.rb). I totally missed it the first time I looked.
So the solution ended up being:
First add the user so it is available to the controller action the view is rendered for. this then gets added to the context by the liquid template handler (and therefore exists in the context)
class ApplicationController < ActionController::Base
before_action :set_common_variables
def set_common_variables
#user = current_user # Or how ever you access your currently logged in user
end
end
Add the method to the product to get the user from the liquid context
class ProductDrop < Liquid::Drop
def initialize(model)
#model = model
end
def name
#model.name
end
def user_owned?
return #context['user'].does_user_own_product?(#model.id)
end
end
Then add the method to the user to check if the user owns the product or not:
class UserDrop < Liquid::Drop
def initialize(model)
#model = model
end
def nick_name
#model.nick_name
end
def does_user_own_product?(id)
#model.products.exists?(id: id)
end
end
Obviously this needs error handling and so on. but hopefully that helps someone. Also if anyone knows of a better way, keen to hear it.

Rails 5: Calling a service class - relation mapping

I am trying to learn how to write a service class in my rails 5 app.
When a user registers with devise, I'm trying to incorporate a service class that makes models associated with the user's account on creation of the user account.
In my devise registrations controller, I have:
class Users::RegistrationsController < Devise::RegistrationsController
before_action :configure_permitted_parameters, if: :devise_controller?
def create
super do |user|
if user.persisted?
User::CompleteRegistration.call(user: user)
end
end
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :last_name, :email])
devise_parameter_sanitizer.permit(:account_update, keys: [:first_name, :last_name, :email, ])
end
private
end
In my app / services/ user folder, I have a file called: complete_registration.rb:
class User::CompleteRegistration #< ActiveRecord::Base
def self.call(user: u)
new(user: user).call
end
def initialize(user: u)
self.user = user
end
def call
Profile::SetupService.call(user: user)
Setting::SetupService.call(user: user)
User::OrganisationMapperService.new(user: user).call
end
# def join_an_organisation
# render "organisation_requests/new"
# end
private
attr_accessor :user
end
When I try to save this and then sign up as a user, I can see an error message that says:
NameError - uninitialized constant User::CompleteRegistrations:
I note that it has pluralised the word 'registration'. I don't know if this has something to do with the problem. I tried saving the file as a plural, and renaming the class as a plural, but that didn't work.
The actual error rendered says:
PG::UndefinedTable at /users
ERROR: relation "complete_registration_services" does not exist
LINE 8: WHERE a.attrelid = '"complete_registration_se...
I cant expand the message to get more detail, but there is no relation to be found, because it isnt an active record table that I'm trying to call.
I also tried adding a callback to my user model as an alternative solution:
class User < ApplicationRecord
after_update :after_confirmation_setup
def after_confirmation_setup
return unless !self.confirmed_at.blank?
User::CompleteRegistration.call(user: #user)
end
But that doesnt work either.
Can anyone see how I can setup my app to call a service class on user create (from the registration controller, or any other method)?
By the looks of things, this seems like a naming issue.
From first appearances (I can't say definatively without actually using your code base in console and debugging) but based on a quick scan and looking at the errors you are receiving this appears to be one of Ruby's quirks which I have bumped into a few times.
You have a User class in the global scope (In your models directory), and you have a service class User::CompleteRegistration, which when you call from somewhere is perfectly logical to assume will point to the defined User::CompleteRegistration class. However, Ruby doesn't see it like that.
User::CompleteRegistration can be split into two parts, first User:: Is evaluated, and Ruby searches for a User class, and grabs your model. It then evaluates the CompleteRegistration, and looks for a defined CompleteRegistration that can be used in your User class. This is may be why you are getting the error about a relation. It is searching for a relation within the User model scope.
So essentially, when you write User::CompleteRegistration Ruby doesnt say Right! lets grab a User::CompleteRegistration class! Ruby says Right! lets grab a CompleteRegistration class that can be used within the User scope!
To prevent this, I would perhaps change the naming of your service to something more simple, like: Registration::Complete and avoid any overlapping of class / module names within the global scope.
also on a side note, I too love using services in code, and I don't really want to write a shameless plug on SO but perhaps it your case it can help? I have written a gem to provide easy to use services that has an implementation not too dissimilar to what you are using, perhaps it could be of use, whether you use it as a gem or just scan the code in it, I hope somehow maybe it can help.

Custom url_for for non rolling ids

Right now if I create a URL for a model show action I simply call:
link_to model_instance
which creates something like this the when model is User and the id is 1:
/user/1
I like to customize that behavior without having to go through all instances in the codebase where a URL for such a model is generated. The motivation behind the change is avoiding rolling id's in the URL that lets anybody discover other entries by simply increasing the id. So something like
/user/88x11bc1200
Is there a place where I can simply override how a URL for selected models are generated? I am using RoR 4.x
There are essentially two places you'll have to update.
In the model
class User < ActiveRecord::Base
# Override the to_param method
def to_param
# Whatever your field is called
non_rolling_id
end
end
In the controller
class UsersController < ApplicationController
def show
# Can't use `find` anymore, but will still come over as `id`
#user = User.find_by_non_rolling_id(params[:id])
end
end

Can't use `find_by` method while `where` works in ActiveRecord::Base

I have a method like this, that works fine.
class User < ActiveRecord::Base
def self.find_or_create_from_twitter_id(twitter_id)
user = where(twitter_id: twitter_id).first
user ||= create_from_twitter_id(twitter_id)
end
end
Then I changed where(...).first to find_by, because I thought the two expression are basically same.
def self.find_or_create_from_twitter_id(twitter_id)
user = find_by(twitter_id: twitter_id)
user ||= create_from_twitter_id(twitter_id)
end
But I get undefined method `find_by' for #<Class:0x007fbb2b674970> error when I try to create a User.
I have no idea why find_by doesn't work here. I'll be very grateful if you tell me what is wrong.
find_by method is introduced in Rails 4. If you're using Rails 3.x or lesser version then use: find_by_<attribute_name> instead:
find_by_twitter_id(twitter_id)
But, then there's another method which find and create by the attributes passed to it, which you can use if it fits your needs:
find_or_create_by_twitter_id(twitter_id)

Controller 'new' method error

I am following a tutorial
and I am getting an error for my new action. I am using the tutorial as a guideline and my database differs. Here is my code:
def new
#skill = Skill.new
#skills = Skill.find(:all)
end
and here is my error message:
uninitialized constant SkillsController::Skill
#skill = Skill.new is the highlighted line so my mistake should be there somewhere. Thank you and I will keep trying to fix it with the power of research!
^^^^^^^^^^^^^^^solved^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Now I am getting a new error and it seems my database is not quite built right for the skill id's. I am trying to go to localhost:3000/skills/list, Here is my code:
class SkillsController < ApplicationController
def list
#skills = Skill.find(:all)
end
def show
#skills = Skill.find(params[:id])
end
and my error is:
Couldn't find Skill without an ID
and it highlights:
#skills = Skill.find(params[:id])
as the problem area. Thank you for anyone who knows how to solve this issue and also, If I should re-post as a different question let me know and I will do so. Thanks again and huzzah for the web dev community!
Syntax
Firstly, Rails 4 syntax should read as:
def new
#skills = Skill.all
end
--
Error
In regards to your error, as described in the comments, you really need to ensure you have the Skill class available in your application.
You must remember Rails is basically just a series of classes & modules, which means that if you're trying to call a "model", you're essentially calling a Ruby Class which needs to be loaded (as constants).
The problem is your application hasn't got the Skill class (model) loaded. This is most likely the result of not having it in your app/models directory:
#app/models/skill.rb
Class Skill < ActiveRecord::Base
...
end
What concerns me, though, is how your error is trying to wrap this model in another class - SkillsController. This would typically be the case if you've namespaced the controller; but either way you should create the model, restart your server & test again
I think you are messing up with rails conventions. Rails expect Controller class names to be pluralized, such that SkillsController would be the controller class for the skills table. Rails will then look for the class definition in a file called skills_controller.rb in the /app/controllers directory. For more information on naming conventions refer here.
Also since your error says uninitialized constant SkillsController::Skill. I think you don't have your skills table setup with Skill.rb model notice your table name will be plural and your model name will be singular

Resources