Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

undefined method to_table, to_ul, to_p #3

Closed
activestylus opened this issue May 11, 2012 · 17 comments
Closed

undefined method to_table, to_ul, to_p #3

activestylus opened this issue May 11, 2012 · 17 comments

Comments

@activestylus
Copy link

Quite perplexing - the README shows these handy rendering methods. But there are no such methods anywhere in the source code.

Wassup wit dat?

@tizoc
Copy link
Owner

tizoc commented May 11, 2012

The README file is outdated, I have to fix that. Forms used to have methods to render all the form fields in one call, but thats not supported anymore. The preferred way of doing it now is to just use some templating system (ERB, HAML, or anything) and iterate over the fields.

For example:

<% form.each do |field| %>
  <tr><td><%= field.label_tag %></td><td><%= field %></td></tr>
<% end %>

@activestylus
Copy link
Author

Gotya - I agree with the deprecation. Makes more sense to have people define their own templates.

I do wish there was a way to access the fields individually however. On many forms the flow of the fields are interrupted by other types of random dom elements. Sometimes fields are grouped together differently. More flexibility is needed

I suppose in an ideal world I would be able to do this

<% form = MyForm.new %>
<tr><td><%= form[:title].label_tag %></td><td><%= form[:title] %></td></tr>
</div>

@tizoc
Copy link
Owner

tizoc commented May 11, 2012

That works, and is how I render fields 99% of the time. Sorry, I know the documentation is really lacking, but the documentation for Django forms is a good place to start, most of the stuff there applies to Bureaucrat too.

There are some docs here too: http://rubydoc.info/github/tizoc/bureaucrat/frames

@tizoc
Copy link
Owner

tizoc commented May 11, 2012

Ok, rubydoc.info is a bit broken right now, it doesn't show the documentation for the Form class, but in Form you have this:

  # Iterates over the fields
  def each
    @fields.each do |name, field|
      yield BoundField.new(self, field, name)
    end
  end

  # Access a named field
  def [](name)
    field = @fields[name] or return nil
    BoundField.new(self, field, name)
  end

@activestylus
Copy link
Author

There we go!

% form = SiteForm.new

{{ form[:domain_name].label_tag }}
{{ form[:domain_name].to_s }}

This is really beautiful man. I've also enjoyed picking at your code as it's a lovely example of pure, compositional OO

The last question I have is how to deal with existing models. Right now I am playing with the Cuba framework so my new route looks like this:

on "new" do
  res.write view("sites/new", title: "New Site", site: Site.new, form: SiteForm.new)
end

What is the recommended way to set this up with an edit action? SiteForm.new(site.put_atts_here)

Overall I am curious to see how you integrate this with a working, RESTful app. Perhaps in addition to cleaning up the docs a sample app would be a welcome addition. I can sort out the docs myself if you like and shoot you a pull request. Let me know

In the meantime I'm closing this issue as its been resolved. Thanks for the prompt replies! :)

@activestylus
Copy link
Author

Oh one more thing - the widgets are outputting flat name attributes:

<input type="text" name="title" id="id_realname" />

Is there a convention for namespacing the attributes? I'm looking for name="post[title]"

No biggie if there isn't I can hack something up myself. Just trying not to reinvent the wheel here

@tizoc
Copy link
Owner

tizoc commented May 11, 2012

Untested example (based on actual code I wrote for an application):

class Base < Bureaucrat::Forms::Form
  extend Bureaucrat::Quickfields

  attr_accessor :edited_object # So that we can access it

  # This method calculates the hash of "initial" values for the form
  # it is useful for forms that "edit" an already existing object.
  def self.initial(object)
    o = Bureaucrat::Utils::StringAccessHash.new
    base_fields.keys.inject(o) do |h, field|
      if object.respond_to?(field)
        value = object.send(field)
        value = value.to_s if value.is_a?(Numeric)
        h[field] = value
      end
      h
    end
  end

  # This method will be used instead of new when we want to "edit"
  # an already existing object. e.g. Forms::User.new_for(Models::User)
  def self.new_for(object, options={})
    options[:initial] = initial(object)
    new(nil, options).tap do |f|
      f.edited_object = object
      f.initialize_formsets(nil)
    end
  end

  # Alternatively, you can override initialize so that it accepts
  # an :object option
  def initialize(data = nil, options = {})
    if self.edited_object = options.fetch(:object, nil)
      options = {
        initial: self.class.initial(self.edited_object)
      }.merge(options)
    end

    super(data, options)
  end

  # On POST routes, we will use this to save changes to the object:
  # if form.valid?
  #   user = form.save(user)
  #   redirect
  # else
  #   .. handle error
  # end
  #
  # or for new records
  #
  # user = form.save(User.new)
  #
  def save(record)
    DB.transaction do # this example assumes Sequel
      populate_object(record) # See Form#populate_object and Field#populate_object
      record.save
    end

    record
  end
end

As for namespacing, there is no convention, and right now Bureaucrat doesn't take advantage of how Rack interprets nested attributes (I don't think it is likely to happen, at least not by default).

@tizoc
Copy link
Owner

tizoc commented May 11, 2012

Btw, forms support a "prefix" option, thats something you can use to differentiate fields that belong to one form or another (thats the intended use).

@tizoc
Copy link
Owner

tizoc commented May 11, 2012

Sorry, one correction:

Forms::User.new_for(Models::User)

should have been

Forms::User.new_for(Models::User[params[:id]])

What new_for expects is an instance.

@activestylus
Copy link
Author

Thanks a bunch, the example is very helpful. I can see myself taking a lot of that above code and factoring into a module for re-use.

I will definitely be kicking the tires a bit more on this as its a lot more robust than I originally thought. If I run into any gotchas I will post em here. Hopefully my questions will help drive your reboot of documentation :)

BTW you really could use a proper promotional page for this once the docs are somewhat sorted. If you need a hand I got some design skillz. You can see my handiwork on the Slim site http://www.slim-lang.com, Merb http://www.merbivore.com (holy crap they really butchered my design :)) I can help you with logos/icons / layout if you need it

Cheers

@tizoc
Copy link
Owner

tizoc commented May 11, 2012

Awesome, the slim site in particular looks great.

And yes, I would say Bureaucrat is pretty robust by now, I have been using it in every project as a very-core component. I don't use 100% of the implemented features, but for the stuff I use, it is pretty well battle-tested.

Regarding promotion, I don't promote it because it lacks documentation. It lacks documentation because there are some things I want to change (so I don't want to document things that I will not support in the future), and a few missing components. Then I haven't done those things because I'm lazy, and also spent time on other stuff, oh well. With Fiasco it is the opposite, I focused on documenting things first, made a homepage, etc, but then I decided I wanted to change things and never got to finishing it. I have been slowly leaving Ruby behind for doing work, so, that also affects my motivation.

@activestylus
Copy link
Author

Leaving Ruby?? Blasphemy!

Don't tell me the community has lost yet another decent programmer to nodejs :p

@tizoc
Copy link
Owner

tizoc commented May 11, 2012

Btw, you may also be interested in Scrivener: http://github.com/soveran/scrivener

It was made by a coworker (the same guy behind cuba, ohm, etc) and is a more lightweight version of the concept behind Bureaucrat (but it focuses only on validation, anything related to HTML is left out).

@tizoc
Copy link
Owner

tizoc commented May 11, 2012

No, no nodejs here. For web stuff I have always been a Python guy. The only reason I do Ruby is that it is the main language being used at my Job. And well, Ruby isn't bad, just not my tool of choice.

@activestylus
Copy link
Author

I like Python - a lot actually. It's the closest thing to Ruby I have worked with.

I dig Ruby because it's so flexible. The features I like from other languages (like Python decorators) are actually pretty easy to implement. for example http://yehudakatz.com/2009/07/11/python-decorators-in-ruby/

Thanks a lot for the scrivener link. Looks nice and lightweight. Since you plan on migrating I may just dig up the guts of this gem and put together a stripped down version for html rendering

@tizoc
Copy link
Owner

tizoc commented May 11, 2012

I really don't like Yehuda's implementation of decorators (both in terms of implementation, interface, performance, etc they are also global).

Ruby is flexible, but I think a lot of its flexibility is wrong-headed, thats why I try to write Ruby code in the most plain way possible. Anyway, it just came to me that this is a github issue and not my inbox, so, not the place for rants :)

re: migrating, thats not what is happening, I don't belong to tribes! I just learn and use tools to do my work.

@activestylus
Copy link
Author

Not the place for rants? Have you seen this? :p

rails/rails@3756a3f

That's about all the geekery I'm good for today. Time to enjoy the weekend.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants