Let's start with slightly modified version of mergeRules, that takes into account fact that options can have symbolic or string names and name -> val is treated the same as "name" -> val:
ClearAll[symbolToName, deleteOptionDuplicates] symbolToName[sym_Symbol] := SymbolName[sym] symbolToName[arg_] := arg deleteOptionDuplicates[opts:OptionsPattern[]] := GatherBy[Flatten[{opts}], symbolToName@First[#] &][[All, 1]] Now we can define an environment providing special option-filtering function:
ClearAll[withOptions, getOptions] withOptions[base_Symbol, opts___] := Function[body, With[{allOptions = deleteOptionDuplicates[opts, Options[base]]}, Block[{getOptions = FilterRules[allOptions, Options[#]] &}, body ] ], HoldFirst ] To make our environment easier to use, in function definitions, let's add some macro tricks stolen from Leonid:
withOptions /: Verbatim[SetDelayed][lhs_, rhs : HoldPattern[withOptions[__][_]]] := Block[{With}, Attributes[With] = {HoldAll}; lhs := Evaluate[rhs] ] withOptions /: Verbatim[SetDelayed][ h_[pre___, optsPatt_OptionsPattern, post___], HoldPattern[withOptions[body_]] ] := h[pre, opts : optsPatt, post] := withOptions[h, opts][body] withOptions /: Verbatim[SetDelayed][ h_[ pre___, namedOptsPatt:Verbatim[Pattern][optsName_, _OptionsPattern], post___ ], HoldPattern[withOptions[body_]] ] := h[pre, namedOptsPatt, post] := withOptions[h, optsName][body] Now we can define functions like this:
ClearAll[f] Options[f] = {"Test" -> 1, ImageSize -> 100, TooltipDelay -> 20}; f[x_, OptionsPattern[{f, Button, Tooltip}]] := withOptions@Column@ {getOptions[Button], getOptions[Tooltip], OptionValue["Test"]} It gives expected results:
f[1, Appearance -> "Palette"] {Appearance->Palette, ImageSize->100}
{TooltipDelay->20} 1 f[1, ImageSize -> 200] {ImageSize->200}
{TooltipDelay->20} 1 f[1] {ImageSize->100}
{TooltipDelay->20} 1 Without duplicates:
f[1, "ImageSize" -> 1, ImageSize -> 2, ImageSize -> 3] {ImageSize->1}
{TooltipDelay->20} 1 Usually I don't need to store or return filtered options, I just want to pass them to appropriate function. In such cases other environment can be useful:
ClearAll[withDefaultOptions] withDefaultOptions[base_Symbol, targets:{__Symbol}, opts___]:= Function[body, With[{allOptions=deleteOptionDuplicates[opts, Options[base]]}, Internal`InheritedBlock[targets, Scan[SetOptions[#, FilterRules[allOptions, Options[#]]]&, targets]; body ] ], HoldFirst ] withDefaultOptions /: Verbatim[SetDelayed][lhs_, rhs:HoldPattern[withDefaultOptions[_,_,___][_]]] := Block[{With}, Attributes[With]={HoldAll}; lhs := Evaluate[rhs] ] withDefaultOptions /: Verbatim[SetDelayed][h_[pre___, optsPatt_OptionsPattern, post___], HoldPattern[withDefaultOptions[body_]]] := h[pre, opts:optsPatt,post] := withDefaultOptions[h, Cases[Flatten[{First[optsPatt]}], Except[h, _Symbol]], opts][body] withDefaultOptions /: Verbatim[SetDelayed][ h_[pre___, namedOptsPatt:Verbatim[Pattern][optsName_, optsPatt_OptionsPattern], post___], HoldPattern[withDefaultOptions[body_]] ] := h[pre, namedOptsPatt, post] := withDefaultOptions[h, Cases[Flatten[{First[optsPatt]}], Except[h,_Symbol]], optsName][body] Let's start with a dummy function to which we'll pass options:
ClearAll[f]; Options[f] = {"optA" -> "valFA", "optB" -> "valFB"}; f[OptionsPattern[]] := OptionValue[{"optA", "optB"}] Now a function the can accept and pass options to f. It has it's own options and some overridden f options with different defaults.
ClearAll[g]; Options[g] = {"optA" -> "valGA", "optC" -> "valGC"}; g[OptionsPattern[{g, f}]] := withDefaultOptions@{OptionValue[{"optA", "optC"}], f[]} Notice that we don't have to pass anything to f in body of g. Proper default options for f are set automatically by withDefaultOptions based on what's is matched and what is inside OptionsPattern.
If option for f is neither given explicitly nor set as default on g then default of f is used.
g[] (* {{"valGA", "valGC"}, {"valGA", "valFB"}} *) g["optA" -> 1] (* {{1, "valGC"}, {1, "valFB"}} *) g["optB" -> 1] (* {{"valGA", "valGC"}, {"valGA", 1}} *) g["optC" -> 1] (* {{"valGA", 1}, {"valGA", "valFB"}} *)