Nick Woods
2005-May-08 01:38 UTC
Overriding date_select in local project to use custom value rather than blank for starting option...
I would like to have a date control on a page and I would like a behavior similar to what you get using date_select with the :include_blank => true option where the first value in the dropdown is "- Month -", "- Day -", or "- Year -" rather than a blank value for the respective month, day, and year select fields. I took a look at the ruby source for form_options_helper.rb in gems/actionpack-1.8.1/lib/action_view/helpers on my machine, specifically the to_select_tag function and see that it could be reasonably easy to override this method to do what I''d like (I''d need to set an option at the date_select level for a label to use as well), but I have no idea where in my Rails project (directory, file, class, etc.) that I should put my changes. I''d love it if there was the ability to add extra fields to any automatic select field in the default Rails functionality, but since it isn''t and I''m sure there are other items I''d like to override in the future, what is the best way to go about overriding these types of elements locally with minimal changes and code? Can I overwrite the methods at runtime by using some Ruby override tricks? Should I? I''m still a bit new to the Ruby/Rails game, but I could see this being a bit of a pain since there is the ActionView class which has a Helpers Module within which there is a DateHelper module within which the date_select function is defined and which instantiates and InstanceTag class which has the to_select_tag function which I need to change. I supposed I could add javascript to my pages to insert the value later or just re-write the date_select function altogether, but I like the principle of being able to use these rails functions and make a few changes to them. I guess I could also create a function which calls date_select, takes the resulting string, and then inserts my special options in the right place in the beginning of the string returned by date_select, but it doesn''t feel that clean to me. Any thoughts on the most Rails/Rubyesque simple and clean way to do things here? Thanks, Nick
Sean T Allen
2005-May-08 03:44 UTC
Re: Overriding date_select in local project to use custom value rather than blank for starting option...
I''d suggest one of two things... 1. Write a patch and submit. In the meantime having a library that gets loaded by environment.rb to override the functionality you want... its probably a 10 minute job... I added new functionality and fixed two bugs to date_helper and it took me maybe 30-60 minutes w/ unit test writing 2. Write your own sep functions so you dont lose improvements that come in later versions... do note there are bugs in the date_helper the ones i know of ( and submitted a patch for ) would impact on what you want... you''d be interested in all calls to select_html and select_html''s handling of include_blank, you don''t have to touch any of the to_ blahs... if you follow the code... all options get passed through down to select_* where at that point only include_blank and discard_type are passed as the 3rd and 4th args to select_html... in order to avoid breaking other stuff i''d suggest add a 5th arg that can be a hash with you include_blank options ( value and name )then using as needed in select_html something like :include_blank_fields => { :name => ''Please Select a Year...'', :value => 0 } for a how to override... here is what i am running til said patch makes it into a distrubtion... it should answer questions about overriding methods, adding methods.. etc... in config/environment.rb ( bottom of the file ) require_dependency ''ext/date_helper'' in lib/ext/date_helper.rb module ActionView module Helpers # Returns a set of select tags ( on for hour and minute ) pre-selected for accessing a specified datetime-based # attribute (identified by +method+) on an object assigned to the template (identified by +object+) # Shorthand for # datetime_select("post", "written_on", :discard_year => true, :discard_month => true) module DateHelper def time_select(object, method, options = {}) options[:discard_month], options[:discard_year] = true, true InstanceTag.new(object, method, self).to_datetime_select_tag(options) end # Returns a hidden input tag for the second of datetime. def hidden_second( datetime, options = {} ) value = datetime ? ( datetime.kind_of?(Fixnum) ? datetime : datetime.sec ) : '''' hidden_html( "sec", value, options[:prefix], options[:discard_type]) end # Returns a hidden input tag for the minute of datetime. def hidden_minute( datetime, options = {} ) value = datetime ? ( datetime.kind_of?(Fixnum) ? datetime : datetime.min ) : '''' hidden_html( "minute", value, options[:prefix], options[:discard_type]) end # Returns a hidden input tag for the hour of datetime. def hidden_hour( datetime, options = {} ) value = datetime ? ( datetime.kind_of?(Fixnum) ? datetime : datetime.hour ) : '''' hidden_html( "hour", value, options[:prefix], options[:discard_type]) end # Returns a hidden input tag for the day of date def hidden_day( date, options = {} ) value = date ? ( date.kind_of?(Fixnum) ? date : date.day ) : '''' hidden_html( "day", value, options[:prefix], options[:discard_type]) end # Returns a hidden input tag for the month of date def hidden_month( date, options = {} ) value = date ? ( date.kind_of?(Fixnum) ? date : date.month ) : '''' hidden_html( "month", value, options[:prefix], options[:discard_type]) end # Returns a hidden input tag for the year of date def hidden_year( date, options = {} ) value = date ? ( date.kind_of?(Fixnum) ? date : date.year ) : '''' hidden_html( "year", value, options[:prefix], options[:discard_type]) end private def hidden_html(type, value, prefix = nil, discard_type = false) hidden_html = %(<input type="hidden" name="#{prefix || DEFAULT_PREFIX}) hidden_html << "[#{type}]" unless discard_type hidden_html << %(" value="#{value}">\n) return hidden_html end end class InstanceTag #:nodoc: include DateHelper def to_date_select_tag(options = {}) defaults = { :discard_type => true } options = defaults.merge(options) options_with_prefix = Proc.new { |position| options.merge(:prefix => "#{@object_name}[#{@method_name}(#{position}i)]") } date = options[:include_blank] ? (value || 0) : (value || Date.today) date_select = "" options[:order] = [:month, :year, :day] if options[:month_before_year] # For backwards compatibility options[:order] ||= [:year, :month, :day] position = {:year => 1, :month => 2, :day => 3} discard = {} discard[:year] = true if options[:discard_year] discard[:month] = true if options[:discard_month] discard[:day] = true if options[:discard_day] or options[:discard_month] options[:order].each do |param| meth_prefix = discard[param] ? ''hidden'' : ''select'' date_select << self.send("#{meth_prefix}_#{param}", date, options_with_prefix.call(position[param])) end return date_select end def to_datetime_select_tag(options = {}) defaults = { :discard_type => true } options = defaults.merge(options) options_with_prefix = Proc.new { |position| options.merge(:prefix => "#{@object_name}[#{@method_name}(#{position}i)]") } datetime = options[:include_blank] ? (value || nil) : (value || Time.now) datetime_select = '''' datetime_select << (options[:discard_year] ? hidden_year(datetime, options_with_prefix.call(1)) : select_year(datetime , options_with_prefix.call(1))) datetime_select << (options[:discard_month] ? hidden_month(datetime, options_with_prefix.call(2)) : select_month(datet ime, options_with_prefix.call(2))) datetime_select << ((options[:discard_day] || options[:discard_month]) ? hidden_day(datetime, options_with_prefix.call (3)) : select_day(datetime, options_with_prefix.call(3))) datetime_select << " — " unless options[:discard_hour] or ( options[:discard_year] && options[:discard_month] ) datetime_select << (options[:discard_hour] ? hidden_hour(datetime, options_with_prefix.call(4)) : select_hour(datetime , options_with_prefix.call(4))) datetime_select << ((options[:discard_minute] || options[:discard_hour]) ? hidden_minute(datetime, options_with_prefix .call(5)) : " : " + select_minute(datetime, options_with_prefix.call(5))) return datetime_select end end end end _______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails
Nick Woods
2005-May-08 08:34 UTC
Re: Overriding date_select in local project to use custom value rather than blank for starting option...
Thanks, the examples and pointers for what to change worked out great. I created my own date_helper.rb file in ''lib/ext'' and added options to methods that I needed allowing the specification of the blank values for day month and year. I played around with allowing multiple values for addition, but in the end just allowing the blank name/value to be set seemed to most consistent and in line with what was already going on in the function sets. I also modified the year select box to enumerate the years up or down depending on whether start or end date is greater (always go from start to end date) rather than only allowing lower to higher. I''d like to submit these changes for consideration in the next version of Rails. Any suggestions for someone who hasn''t done so before? It looks like http://dev.rubyonrails.com/ has information for going about making changes so I''m about to get the tree set up on my machine and start to go through the process for submitting a change. Thanks again, Nick On 5/7/05, Sean T Allen <sean-5W9FBhQXBOtBDgjK7y7TUQ@public.gmane.org> wrote:> I''d suggest one of two things... > > 1. Write a patch and submit. In the meantime having a library that gets > loaded by environment.rb > to override the functionality you want... its probably a 10 minute > job... > > I added new functionality and fixed two bugs to date_helper and it > took me maybe 30-60 minutes > w/ unit test writing > > 2. Write your own sep functions so you dont lose improvements that come > in later versions... > > do note there are bugs in the date_helper the ones i know of ( and > submitted a patch for ) > would impact on what you want... > > you''d be interested in all calls to select_html and select_html''s > handling of include_blank, > you don''t have to touch any of the to_ blahs... if you follow the > code... all options get > passed through down to select_* where at that point only include_blank > and discard_type > are passed as the 3rd and 4th args to select_html... > > in order to avoid breaking other stuff i''d suggest add a 5th arg that > can be a hash with > you include_blank options ( value and name )then using as needed in > select_html > > something like :include_blank_fields => { :name => ''Please Select a > Year...'', :value => 0 } > > for a how to override... here is what i am running til said patch makes > it into a distrubtion... > > it should answer questions about overriding methods, adding methods.. etc... > > in config/environment.rb ( bottom of the file ) > > require_dependency ''ext/date_helper'' > > in lib/ext/date_helper.rb > > module ActionView > module Helpers > # Returns a set of select tags ( on for hour and minute ) > pre-selected for accessing a specified datetime-based > # attribute (identified by +method+) on an object assigned to the > template (identified by +object+) > # Shorthand for > # datetime_select("post", "written_on", :discard_year => true, > :discard_month => true) > module DateHelper > def time_select(object, method, options = {}) > options[:discard_month], options[:discard_year] = true, true > InstanceTag.new(object, method, > self).to_datetime_select_tag(options) > end > > # Returns a hidden input tag for the second of datetime. > def hidden_second( datetime, options = {} ) > value = datetime ? ( datetime.kind_of?(Fixnum) ? datetime : > datetime.sec ) : '''' > hidden_html( "sec", value, options[:prefix], options[:discard_type]) > end > > # Returns a hidden input tag for the minute of datetime. > def hidden_minute( datetime, options = {} ) > value = datetime ? ( datetime.kind_of?(Fixnum) ? datetime : > datetime.min ) : '''' > hidden_html( "minute", value, options[:prefix], > options[:discard_type]) > end > > # Returns a hidden input tag for the hour of datetime. > def hidden_hour( datetime, options = {} ) > value = datetime ? ( datetime.kind_of?(Fixnum) ? datetime : > datetime.hour ) : '''' > hidden_html( "hour", value, options[:prefix], > options[:discard_type]) > end > > # Returns a hidden input tag for the day of date > def hidden_day( date, options = {} ) > value = date ? ( date.kind_of?(Fixnum) ? date : date.day ) : '''' > hidden_html( "day", value, options[:prefix], options[:discard_type]) > end > > # Returns a hidden input tag for the month of date > def hidden_month( date, options = {} ) > value = date ? ( date.kind_of?(Fixnum) ? date : date.month ) : '''' > hidden_html( "month", value, options[:prefix], > options[:discard_type]) > end > > # Returns a hidden input tag for the year of date > def hidden_year( date, options = {} ) > value = date ? ( date.kind_of?(Fixnum) ? date : date.year ) : '''' > hidden_html( "year", value, options[:prefix], > options[:discard_type]) > end > > private > def hidden_html(type, value, prefix = nil, discard_type = false) > hidden_html = %(<input type="hidden" name="#{prefix || > DEFAULT_PREFIX}) > hidden_html << "[#{type}]" unless discard_type > hidden_html << %(" value="#{value}">\n) > > return hidden_html > end > end > > class InstanceTag #:nodoc: > include DateHelper > > def to_date_select_tag(options = {}) > defaults = { :discard_type => true } > options = defaults.merge(options) > options_with_prefix = Proc.new { |position| > options.merge(:prefix => "#{@object_name}[#{@method_name}(#{position}i)]") > } > date = options[:include_blank] ? (value || 0) : (value || > Date.today) > > date_select = "" > options[:order] = [:month, :year, :day] if > options[:month_before_year] # For backwards compatibility > options[:order] ||= [:year, :month, :day] > > position = {:year => 1, :month => 2, :day => 3} > > discard = {} > discard[:year] = true if options[:discard_year] > discard[:month] = true if options[:discard_month] > discard[:day] = true if options[:discard_day] or > options[:discard_month] > > options[:order].each do |param| > meth_prefix = discard[param] ? ''hidden'' : ''select'' > date_select << self.send("#{meth_prefix}_#{param}", date, > options_with_prefix.call(position[param])) > end > return date_select > end > > def to_datetime_select_tag(options = {}) > defaults = { :discard_type => true } > options = defaults.merge(options) > options_with_prefix = Proc.new { |position| > options.merge(:prefix => "#{@object_name}[#{@method_name}(#{position}i)]") > } > datetime = options[:include_blank] ? (value || nil) : (value || > Time.now) > > datetime_select = '''' > datetime_select << (options[:discard_year] ? > hidden_year(datetime, options_with_prefix.call(1)) : select_year(datetime > , options_with_prefix.call(1))) > datetime_select << (options[:discard_month] ? > hidden_month(datetime, options_with_prefix.call(2)) : select_month(datet > ime, options_with_prefix.call(2))) > datetime_select << ((options[:discard_day] || > options[:discard_month]) ? hidden_day(datetime, options_with_prefix.call > (3)) : select_day(datetime, options_with_prefix.call(3))) > datetime_select << " — " unless options[:discard_hour] or > ( options[:discard_year] && options[:discard_month] ) > datetime_select << (options[:discard_hour] ? > hidden_hour(datetime, options_with_prefix.call(4)) : select_hour(datetime > , options_with_prefix.call(4))) > datetime_select << ((options[:discard_minute] || > options[:discard_hour]) ? hidden_minute(datetime, options_with_prefix > .call(5)) : " : " + select_minute(datetime, options_with_prefix.call(5))) > > return datetime_select > end > end > end > end > > > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails > > > >
Sean T Allen
2005-May-08 17:46 UTC
Re: Overriding date_select in local project to use custom value rather than blank for starting option...
Dont forget the unit tests... that would be my big piece of advice otherwise the website has all the info you need... Nick Woods wrote:>Thanks, the examples and pointers for what to change worked out great. > I created my own date_helper.rb file in ''lib/ext'' and added options >to methods that I needed allowing the specification of the blank >values for day month and year. I played around with allowing multiple >values for addition, but in the end just allowing the blank name/value >to be set seemed to most consistent and in line with what was already >going on in the function sets. > >I also modified the year select box to enumerate the years up or down >depending on whether start or end date is greater (always go from >start to end date) rather than only allowing lower to higher. > >I''d like to submit these changes for consideration in the next version >of Rails. Any suggestions for someone who hasn''t done so before? It >looks like http://dev.rubyonrails.com/ has information for going about >making changes so I''m about to get the tree set up on my machine and >start to go through the process for submitting a change. > >Thanks again, > >Nick > >On 5/7/05, Sean T Allen <sean-5W9FBhQXBOtBDgjK7y7TUQ@public.gmane.org> wrote: > > >>I''d suggest one of two things... >> >>1. Write a patch and submit. In the meantime having a library that gets >>loaded by environment.rb >> to override the functionality you want... its probably a 10 minute >>job... >> >> I added new functionality and fixed two bugs to date_helper and it >>took me maybe 30-60 minutes >> w/ unit test writing >> >>2. Write your own sep functions so you dont lose improvements that come >>in later versions... >> >>do note there are bugs in the date_helper the ones i know of ( and >>submitted a patch for ) >>would impact on what you want... >> >>you''d be interested in all calls to select_html and select_html''s >>handling of include_blank, >>you don''t have to touch any of the to_ blahs... if you follow the >>code... all options get >>passed through down to select_* where at that point only include_blank >>and discard_type >>are passed as the 3rd and 4th args to select_html... >> >>in order to avoid breaking other stuff i''d suggest add a 5th arg that >>can be a hash with >>you include_blank options ( value and name )then using as needed in >>select_html >> >>something like :include_blank_fields => { :name => ''Please Select a >>Year...'', :value => 0 } >> >>for a how to override... here is what i am running til said patch makes >>it into a distrubtion... >> >>it should answer questions about overriding methods, adding methods.. etc... >> >>in config/environment.rb ( bottom of the file ) >> >>require_dependency ''ext/date_helper'' >> >>in lib/ext/date_helper.rb >> >>module ActionView >> module Helpers >> # Returns a set of select tags ( on for hour and minute ) >>pre-selected for accessing a specified datetime-based >> # attribute (identified by +method+) on an object assigned to the >>template (identified by +object+) >> # Shorthand for >> # datetime_select("post", "written_on", :discard_year => true, >>:discard_month => true) >> module DateHelper >> def time_select(object, method, options = {}) >> options[:discard_month], options[:discard_year] = true, true >> InstanceTag.new(object, method, >>self).to_datetime_select_tag(options) >> end >> >> # Returns a hidden input tag for the second of datetime. >> def hidden_second( datetime, options = {} ) >> value = datetime ? ( datetime.kind_of?(Fixnum) ? datetime : >>datetime.sec ) : '''' >> hidden_html( "sec", value, options[:prefix], options[:discard_type]) >> end >> >> # Returns a hidden input tag for the minute of datetime. >> def hidden_minute( datetime, options = {} ) >> value = datetime ? ( datetime.kind_of?(Fixnum) ? datetime : >>datetime.min ) : '''' >> hidden_html( "minute", value, options[:prefix], >>options[:discard_type]) >> end >> >> # Returns a hidden input tag for the hour of datetime. >> def hidden_hour( datetime, options = {} ) >> value = datetime ? ( datetime.kind_of?(Fixnum) ? datetime : >>datetime.hour ) : '''' >> hidden_html( "hour", value, options[:prefix], >>options[:discard_type]) >> end >> >> # Returns a hidden input tag for the day of date >> def hidden_day( date, options = {} ) >> value = date ? ( date.kind_of?(Fixnum) ? date : date.day ) : '''' >> hidden_html( "day", value, options[:prefix], options[:discard_type]) >> end >> >> # Returns a hidden input tag for the month of date >> def hidden_month( date, options = {} ) >> value = date ? ( date.kind_of?(Fixnum) ? date : date.month ) : '''' >> hidden_html( "month", value, options[:prefix], >>options[:discard_type]) >> end >> >> # Returns a hidden input tag for the year of date >> def hidden_year( date, options = {} ) >> value = date ? ( date.kind_of?(Fixnum) ? date : date.year ) : '''' >> hidden_html( "year", value, options[:prefix], >>options[:discard_type]) >> end >> >> private >> def hidden_html(type, value, prefix = nil, discard_type = false) >> hidden_html = %(<input type="hidden" name="#{prefix || >>DEFAULT_PREFIX}) >> hidden_html << "[#{type}]" unless discard_type >> hidden_html << %(" value="#{value}">\n) >> >> return hidden_html >> end >> end >> >> class InstanceTag #:nodoc: >> include DateHelper >> >> def to_date_select_tag(options = {}) >> defaults = { :discard_type => true } >> options = defaults.merge(options) >> options_with_prefix = Proc.new { |position| >>options.merge(:prefix => "#{@object_name}[#{@method_name}(#{position}i)]") >>} >> date = options[:include_blank] ? (value || 0) : (value || >>Date.today) >> >> date_select = "" >> options[:order] = [:month, :year, :day] if >>options[:month_before_year] # For backwards compatibility >> options[:order] ||= [:year, :month, :day] >> >> position = {:year => 1, :month => 2, :day => 3} >> >> discard = {} >> discard[:year] = true if options[:discard_year] >> discard[:month] = true if options[:discard_month] >> discard[:day] = true if options[:discard_day] or >>options[:discard_month] >> >> options[:order].each do |param| >> meth_prefix = discard[param] ? ''hidden'' : ''select'' >> date_select << self.send("#{meth_prefix}_#{param}", date, >>options_with_prefix.call(position[param])) >> end >> return date_select >> end >> >> def to_datetime_select_tag(options = {}) >> defaults = { :discard_type => true } >> options = defaults.merge(options) >> options_with_prefix = Proc.new { |position| >>options.merge(:prefix => "#{@object_name}[#{@method_name}(#{position}i)]") >>} >> datetime = options[:include_blank] ? (value || nil) : (value || >>Time.now) >> >> datetime_select = '''' >> datetime_select << (options[:discard_year] ? >>hidden_year(datetime, options_with_prefix.call(1)) : select_year(datetime >>, options_with_prefix.call(1))) >> datetime_select << (options[:discard_month] ? >>hidden_month(datetime, options_with_prefix.call(2)) : select_month(datet >>ime, options_with_prefix.call(2))) >> datetime_select << ((options[:discard_day] || >>options[:discard_month]) ? hidden_day(datetime, options_with_prefix.call >>(3)) : select_day(datetime, options_with_prefix.call(3))) >> datetime_select << " — " unless options[:discard_hour] or >>( options[:discard_year] && options[:discard_month] ) >> datetime_select << (options[:discard_hour] ? >>hidden_hour(datetime, options_with_prefix.call(4)) : select_hour(datetime >>, options_with_prefix.call(4))) >> datetime_select << ((options[:discard_minute] || >>options[:discard_hour]) ? hidden_minute(datetime, options_with_prefix >>.call(5)) : " : " + select_minute(datetime, options_with_prefix.call(5))) >> >> return datetime_select >> end >> end >> end >>end >> >> >>_______________________________________________ >>Rails mailing list >>Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org >>http://lists.rubyonrails.org/mailman/listinfo/rails >> >> >> >> >> >> >_______________________________________________ >Rails mailing list >Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org >http://lists.rubyonrails.org/mailman/listinfo/rails > > > >_______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails