I need to retain the Form data submitted in one view to be used in another view.
I'll be using POST method to submit the data. Is there anyway I can retrieve data from the POST method in Ruby, like in PHP I would use $title=$_POST["title"].
Any ideas?
Thanks and Cheers !
I think you just want the params hash? rubyonrails.org is down at the moment, but when it's back up take a read of the Action Controller Overview (or go to the google cache):
"Rails does not make any distinction between query string parameters and POST parameters, and both are available in the params hash in your controller"
For this, you need to understand the rationale behind the MVC pattern. Depending on whether or not you want to persist your data in the database, you derive your model-class from ActiveRecord, but as persistence seems not be context of your question, here is what you could try:
First, define a model like this
class Foo
# define variables here
attr_accessor :param1 # create reader and writer methods for param1
end
In your controller action:
def action1
#foo = new Foo
# pass parameters by using the params[] hash, e.g.
#foo.param1 = params[:param1]
end
You can then access the #foo object from every other view in your controller.
BTW, just found this screencast around the topic, #193 from railscasts.
Related
We recently started to drag a very old Rails app up to date (or at least out of obsolescence). One of the changes was an update of Cucumber from 1.3.x to 2.99.
Many of our rspec specs on this app used a helper for loading test data which leveraged Cucumber::Ast::DataTable. (The helper declared its own subclass which inherited from Cucumber::Ast::DataTable.) This was deprecated, so as suggested, I replaced the inheritance with Cucumber::MultilineArgument::DataTable.
The subclass looks like this:
class ParsedTable < ::Cucumber::MultilineArgument::DataTable
def initialize(string)
super(string.split("\n").map do |line|
line = line.split('|').map(&:squish)
line.slice(1, line.length)
end)
end
end
Then there are a bunch of test helpers which create test data like this (assume "role" is a model we'll be testing against):
def create_roles(string)
table = ParsedTable.new(string)
table.hashes.each do |hash|
create :role,
name: hash['Name'],
short_name: hash['Short Name'],
contracted_work_day: hash['Contracted workday in minutes']
end
end
These helpers get called like this:
create_roles <<-eos
| Name | Contracted workday in minutes |
| Therapist | 390 |
eos
But when that kind of call goes in, I get ArgumentError: data must be a Core::Ast::DataTable. The stack says this exception is from lib/cucumber/multiline_argument/data_table.rb:93:in 'initialize' which is the super call in the ParsedTable definition.
I've been trying to chase this around the Cucumber source and I can't figure out what I'm doing wrong.
The API documentation for Cucumber states that the constructor for Cucumber::MultilineArgument::DataTable takes a Core::Ast::DataTable object.
Source: https://www.rubydoc.info/gems/cucumber/Cucumber/MultilineArgument/DataTable#constructor_details
You will need an instance of Core::Ast::DataTable rather than custom parsing it from a string.
Creates a new instance. +raw+ should be an Array of Array of String or an Array of Hash You don't typically create your own DataTable objects - Cucumber will do it internally and pass them to your Step Definitions.
Source: https://www.rubydoc.info/gems/cucumber-core/1.3.1/Cucumber/Core/Ast/DataTable#constructor_details
It looks like cucumber should have already parsed the table as a Core::Ast::DataTable object in the step binding, so all you should need to do is pass this along to the constructor of your ParsedTable class.
When this isn't available, then you'll need to provide an array of array of strings instead.
Greg Burghardt's answer has the right cause for this; the Cucumber::MultilineArgument::DataTable class requires a Core::Ast::DataTable as an argument, which wasn't true of the old Cucumber::Ast::DataTable. (This class in general is a bit of a moving target between Cucumber 1.4.x and the current version.)
In the end I solved this by avoiding the problem. Looking carefully at the methods which used the custom ParsedTable class, I saw they didn't really depend on the class, but instead counted on calling hashes on each instance. What I needed was something which responded to hashes with the appropriate response.
So, rather than subclassing Cucumber::MultilineArgument::DataTable simply to get a custom constructor, I replaced the subclass definition with a method which accepted a string, split it into rows, and fed that Array of rows to Cucumber::MultilineArgument::DataTable#from. That returned an instance of Cucumber::MultilineArgument::DataTable which I could call hashes on. And that works.
Say I have a user model. It has an instance method called status. Status is not an association. It doesn't follow any active record pattern because it's a database already in production.
class User < ActiveRecord::Base
def status
Connection.where(machine_user_id: self.id).last
end
end
So I do this.
#users = User.all
First of all I can't eager load the status method.
#users.includes(:status).load
Second of all I can't cache that method within the array of users.
Rails.cache.write("user", #users)
The status method never gets called until the view layer it seems like.
What is the recommended way of caching this method.
Maybe this instance method is not what I want to do. I've looked at scope but it doesn't look like what I want to do.
Maybe I just need an association? Then I get the includes and I can cache.
But can associations handle complex logic. In this case the instance method is a simple query. What if I have complex logic in that instance method?
Thanks for any help.
Have You tried to encapsulate this logic inside some plain Ruby object like this (I wouldn't use this for very large sets though):
class UserStatuses
def self.users_and_statuses
Rails.cache.fetch "users_statuses", :expires_in => 30.minutes do
User.all.inject({}) {|hsh, u| hsh[u.id] = u.status; hsh }
end
end
end
After that You can use some helper method to access cached version
class User < ActiverRecord::Base
def cached_status
UserStatuses.users_and_statuses[id]
end
end
It doesn't solve Your eager loading problem, Rails doesn't have any cache warming up techniques built in. But by extracting like this, it's easily done by running rake task in Cron.
Also in this case I don't see any problems with using association. Rails associations allows You to submit different options including foreign and primary keys.
I know about controller_name returning a string containing the controller's name but how can I retrieve the controller class (or object) from within a helper?
EDIT: The solution should also work when the controller is namespaced (eg. Admin::PostsController)
You can use the constantize method, like:
controller_name.constantize
Though I'm not sure how it will behave if you have a namespaced controller.
Update:
That one won't work for all controller names and/or namespaces. Though one can use the #controller method in combination with #class:
controller.class
A view probably shouldn't need to do this. Ideally whatever you're trying to do in the view that expects this, you would instead do in the controller.
Trying to think about why you'd want to do this, the best answer I can think of is that you want to invoke a helper method you've defined in the controller. There already exists a construct to do this, use helper_method.
For pretty much anything else, the controller should provide that data to the view. Not the view pulling it out of the controller. (e.g. even though you shouldn't need the class, the controller could provide it with #controller_class = self.class, which would then be available to the view)
In pure Ruby, because class names are constants, you can do this to get the class from a string:
classname = 'Posts'
p Kernel.const_get(classname).methods
There is a nice shortcut in Rails, constantize for just this:
p 'Posts'.constantize.methods
If the classname is eg 'editable_file', first call the camelize method:
p 'editable_file'.camelize.constantize # EditableFile
p 'extensions/editable_file'.camelize.constantize # Extensions::EditableFile
EDIT: If you really want to get the controller name un-demodulized, then this code in config/initializers/controller_name.rb should ensure it:
class ActionController::Metal
def self.controller_name
# #controller_name ||= self.name.demodulize.sub(/Controller$/, '').underscore
#controller_name ||= self.name.sub(/Controller$/, '').underscore
end
end
I am using Rails Warden plugin. It defines a helper method 'user' that returns current user. See the link for the source code.
Now I have an business logic object that does not have any reference to the controller. But I would like to get the current user. Is there any way of accessing this?
I have tried
ActionController::Base.helpers.user
or even
RailsWarden::Mixins::HelperMethods.user
with no luck. Thanks.
Now I have an business logic object
that does not have any reference to
the controller. But I would like to
get the current user. Is there any way
of accessing this?
So why can't you just pass the current user to those methods?
Additionally you can mix them in.
I strongly discourage you to write the static helpers (it is not Java, it is Ruby!).
Instead, where you need those helpers include them as a module:
module SuperLogic
def calculate_stuff(current_user=nil)
(current_user || user || self).bills.sum
end
edn
Then include this where you need it:
# user model
class User
include SuperLogic
#it will get the `calculate_stuff` method
end
# controller
include SuperLogic
# so you can use it as one of
calculate_stuff user
calculate_stuff
and so on...
additionally where you access your business logic, you can just create an instance of the class instead of "static" methods (in ruby they are "class" methods):
# controller
def calculate
#result = BusinessLogic.new(user).calculate_stuff
end
This is probably the easiest thing you can do.
Really, you don't need to access whole HTTP context in your business objects (I'm not even talking about testing it).
The way I think of business logic, it's something that sits between the controller and the model. I think it would be ok to pass an instance of the request to the logic methods, and since you're using warden, you can get the user from 'request.env['warden'].user'.
I haven't encountered a good reason not to have logic methods be static (self.) methods of a module. Maybe Dmytrii's suggestion works for you, but I prefer to 'require' than to dynamically include one-off logic bits.
I've run into this problem with testing. Let's assume I have two models, User and Post, where user has_many :posts.
I'm trying to spec out a code block that includes something like this:
user = User.find(123)
post = user.posts.find(456)
I know how to mock out the User.find and user.posts parts. The user.posts mock returns an array of Post objects. And when it get's to .find(456) part, everything breaks down with no block given exception.
So my question here is: what do I return as the result of the user.posts mock, so that .find(456) method works on it? User.first.posts.class says it's Array, but obviously there's something more that makes the AR-style find calls work. I'm not overjoyed by the prospect of mocking out find method on the returned object.
PS Before you suggest the obvious and good answer of stop mocking about and using fixtures/seeding the test database with necessary data, here's the catch: legacy scheme. Both User and Post work on top of database views not tables, and changing it so that they are tables in test database seems wrong to me.
The issue is that user.posts isn't actually a simple Array; it's an association proxy object. The way to stub it is probably something like this (though the exact syntax depends on which mocking framework you're using):
def setup
#user = mock(User)
User.stub(:find).with(123).return(#user)
user_posts = mock(Object)
#user.stub(:posts).return(user_posts)
#post = mock(Post)
user_posts.stub(:find).with(456).return(#post)
end
Then in your test, User.find(123) will return #user and #user.posts.find(456) will return #post. If you need #user.posts to act like more of the Array in your tests you can create a mock(Array) and stub the [](index) method.
You could look into the stub_chain method offered by RSpec.
http://apidock.com/rspec/Spec/Mocks/Methods/stub_chain#855-stub-chain-is-very-useful-when-testing-controller-code
Update: Per ryan2johnson9 the updated documentation is : https://relishapp.com/rspec/rspec-mocks/v/3-2/docs/working-with-legacy-code/message-chains