75

I have an existing form which is tied to a model named 'Order', but i want to add new form fields that will capture Credit Card info such as name, cc number, etc to be processed on a 3rd party payment gateway.

But since i don't want to save CC info in our database, there are no corresponding columns of that in my order table. And this gives me an error when submitting the form that those Credit card input fields are not 'part' of the order model.

zeratool
  • 1,271
  • 2
  • 12
  • 17
  • possible duplicate of [Simple\_form without for (non model form)](http://stackoverflow.com/questions/5181143/simple-form-without-for-non-model-form) – CharlesB Feb 02 '13 at 13:54
  • 1
    This question isn't a duplicate of the above linked question. That one is about entire forms not connected to models. This one is about having selected fields not connected to the form's model. – Marcelo De Polli Feb 04 '13 at 17:27
  • 3
    This would give you all sorts of issues with PCI compliance and is extremely dangerous!! Although you're not saving the CC information to your database, if you're submitting it to your server all of the details will be in your logs, if anyone has access to your logs they have access to unencrypted client CC information. You should avoid submitting this data to your server at all. If you _really_ need to, look into client side encryption (https://developers.braintreepayments.com/javascript+ruby/sdk/client/credit-cards). Otherwise use direct post forms or third party drop in tools. Good luck! – Jay Sep 08 '14 at 00:20

5 Answers5

48

If I understand your answer correctly, what you want to do is explained in the official wiki page here: Create a fake input that does NOT read attributes. You can use a field not related to any real database column by Edward's suggestion, however you don't need to define an attribute in your model if the form field is nothing to do with the model.

In summary, the trick explained in the page is defining a custom input called 'FakeInput' and use it like this:

<%= simple_form_for @user do |f| %>
  <%= f.input :agreement, as: :fake %>
  ....

Do not forget to restart your rails server after adding/modifying a custom input as Fitter Man commented.

UPDATE: Please note that the official wiki page has updated and the sample code on the wiki page is not working for those which use older versions of SimpleForm. Use code below instead if you encounter an error like undefined method merge_wrapper_options for.... I'm using 3.0.1 and this code works well.

class FakeInput < SimpleForm::Inputs::StringInput
  # This method only create a basic input without reading any value from object
  def input
    template.text_field_tag(attribute_name, input_options.delete(:value), input_html_options)
  end
end
baxang
  • 3,627
  • 1
  • 29
  • 27
  • Nice. I had never noticed that. – Edward May 04 '13 at 19:13
  • 2
    You may have to restart/reload your web server to have this picked up. – Fitter Man Aug 13 '14 at 00:23
  • For some reason it does not work anymore. `undefined method merge_wrapper_options for #` – Trein Sep 19 '14 at 00:39
  • @Trein What version of Rails/SimpleForm are you using? – baxang Sep 20 '14 at 13:34
  • @baxang I'm using v3.0.2. Does it work on rc versions? I unfortunately cannot use rc version on this project (policies...). – Trein Sep 20 '14 at 19:37
  • @Trein I use SimpleForm v3.0.1 with Rails 4.0.x and don't have the same issue. – baxang Sep 26 '14 at 03:12
  • same problem for me... rails 4.0.3 and I tried simple_form 3.0.0, 3.0.1, 3.0.2. – Jon Kern Oct 11 '14 at 16:19
  • @Trein @jon-kern That wiki post applies to `simple_form` version >= 3.1.0.rc1 only. I posted an explanation at [this related SO question](http://stackoverflow.com/questions/26303091/undefined-method-merge-wrapper-options/26331237) – admgc Oct 13 '14 at 00:01
  • I think the wiki page I linked has updated to reflect recent changes in SimpleForm. I updated my answer that now includes the previous code snippet. @admgc – baxang Oct 13 '14 at 05:34
  • What about I want to add **fake_boolean**? official docu only has **fake_select_input**. Hopefully there are a list of all fake properties. – new2cpp Sep 21 '17 at 06:24
47

You can use attr_accessor

 class Order < ActiveRecord::Base

   attr_accessor :card_number


 end

Now you can do Order.first.card_number = '54421542122' or use it in your form or whatever else you need to do.

See here for ruby docs http://www.ruby-doc.org/core-1.9.3/Module.html#method-i-attr_accessor and here for a useful stackoverflow question What is attr_accessor in Ruby?

Don't get it mixed up with attr_accessible! Difference between attr_accessor and attr_accessible

Community
  • 1
  • 1
Edward
  • 3,429
  • 2
  • 27
  • 43
  • I have a follow-up question, after i submit the form, on the controller, how can i "unset" those CC related fields upon call to 'model.save' because it might throw errors? – zeratool Aug 30 '12 at 02:13
  • 1
    you could use `params.except(:card_number)` in your controller. – Trung Lê Nov 02 '12 at 01:10
44

The best way to handle this is to use simple_fields_for like so:

<%= simple_form_for @user do |f| %>
  <%= f.input :first_name %>
  <%= f.input :last_name %>
  <%= f.input :email %>

  <%= simple_fields_for :other do |o| %>
    <%= o.input :change_password, as: :boolean, label: 'I want to change my password' %>
  <% end %>
<% end %>

In this example, I have added a new field called change_password which is not part of the underlying user model.

The reason this is a good approach, is that it lets you use any of the simple form inputs / wrappers as fields. I don't care for the answer by @baxang, because it doesn't allow you to use different types of inputs. This seems more flexible.

Notice though for this to work, I had to pass :other to simple_fields_for. You can pass any string/symbol as long as there is not a model with that same name.

I.e. unfortunately I can't pass :user, as simple_form would try to instantiate a User model, and we'd get the same error message again...

asgeo1
  • 9,028
  • 6
  • 63
  • 85
14

Also if you're just trying to add something and get it into the params, but leaving it out of the model's hash, you could just do FormTagHelpers. http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html

Example:

    <%= simple_form_for resource, :as => resource_name, :url =>   invitation_path(resource_name), :html => {:method => :post} do |f| %>
       <%= devise_error_messages! %>

    <% resource.class.invite_key_fields.each do |field| -%>
       <%= f.input field %>
       <%= hidden_field_tag :object_name, @object.class.name %>
       <%= hidden_field_tag :object_id, @object.id %>
    <% end -%>
Richard Ortega
  • 495
  • 1
  • 6
  • 11
8

I found a very simple (and somewhat strange) workaround.

Just add the input_html option with any value key inside. E.g:

= simple_form_for @user do |f|
  = f.input :whatever, input_html: {value: ''}

Tested simple_from versions: 3.2.1, 3.5.1

user1201917
  • 1,360
  • 1
  • 14
  • 27