<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>lib/sunspot/dsl/restriction.rb</filename>
    </added>
    <added>
      <filename>lib/sunspot/dynamic_query.rb</filename>
    </added>
    <added>
      <filename>spec/integration/dynamic_fields_spec.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -8,4 +8,4 @@ end
 
 Dir['tasks/**/*.rake'].each { |t| load t }
 
-task :default =&gt; :spec
+task :default =&gt; 'spec:api'</diff>
      <filename>Rakefile</filename>
    </modified>
    <modified>
      <diff>@@ -2,7 +2,8 @@ gem 'solr-ruby'
 require 'solr'
 require File.join(File.dirname(__FILE__), 'light_config')
 
-%w(adapters restriction configuration setup field data_extractor facets indexer query search facet facet_row session type util dsl).each do |filename|
+%w(adapters restriction configuration setup field data_extractor facets indexer
+   query dynamic_query search facet facet_row session type util dsl).each do |filename|
   require File.join(File.dirname(__FILE__), 'sunspot', filename)
 end
 </diff>
      <filename>lib/sunspot.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,3 +1,3 @@
-%w(fields query scope).each do |file|
+%w(fields scope query restriction).each do |file|
   require File.join(File.dirname(__FILE__), 'dsl', file)
 end</diff>
      <filename>lib/sunspot/dsl.rb</filename>
    </modified>
    <modified>
      <diff>@@ -27,6 +27,11 @@ module Sunspot
         end
       end
 
+      # TODO document
+      def dynamic(name)
+        @setup.add_dynamic_fields(Field::DynamicField.build(name))
+      end
+
       # method_missing is used to provide access to typed fields, because
       # developers should be able to add new Sunspot::Type implementations
       # dynamically and have them recognized inside the Fields DSL. Like #text,</diff>
      <filename>lib/sunspot/dsl/fields.rb</filename>
    </modified>
    <modified>
      <diff>@@ -7,13 +7,7 @@ module Sunspot
     #
     # See Sunspot.search for usage examples
     #
-    class Query
-      NONE = Object.new
-
-      def initialize(query) #:nodoc:
-        @query = query
-      end
-
+    class Query &lt; Scope
       # Specify a phrase that should be searched as fulltext. Only +text+
       # fields are searched - see DSL::Fields.text
       #
@@ -31,100 +25,6 @@ module Sunspot
         @query.keywords = keywords
       end
 
-      # 
-      # Build a positive restriction. With one argument, this method returns
-      # another DSL object which presents methods for attaching various
-      # restriction types. With two arguments, acts as a shorthand for creating
-      # an equality restriction.
-      #
-      # ==== Parameters
-      #
-      # field_name&lt;Symbol&gt;:: Name of the field on which to place the restriction
-      # value&lt;Symbol&gt;::
-      #   If passed, creates an equality restriction with this value
-      #
-      # ==== Returns
-      #
-      # Sunspot::DSL::Restriction::
-      #   Restriction DSL object (if only one argument is passed)
-      #
-      # ==== Examples
-      #
-      # An equality restriction:
-      #
-      #   Sunspot.search do
-      #     with(:blog_id, 1)
-      #   end
-      # 
-      # Other restriction types:
-      #
-      #   Sunspot.search(Post) do
-      #     with(:average_rating).greater_than(3.0)
-      #   end
-      #
-      def with(field_name, value = NONE)
-        if value == NONE
-          DSL::Restriction.new(field_name.to_sym, @query, false)
-        else
-          @query.add_restriction(field_name, Sunspot::Restriction::EqualTo, value, false)
-        end
-      end
-
-      # 
-      # Build a negative restriction (exclusion). This method can take three
-      # forms: equality exclusion, exclusion by another restriction, or identity
-      # exclusion. The first two forms work the same way as the #with method;
-      # the third excludes a specific instance from the search results.
-      #
-      # ==== Parameters (exclusion by field value)
-      #
-      # field_name&lt;Symbol&gt;:: Name of the field on which to place the exclusion
-      # value&lt;Symbol&gt;::
-      #   If passed, creates an equality exclusion with this value
-      #
-      # ==== Parameters (exclusion by identity)
-      #
-      # args&lt;Object&gt;...::
-      #   One or more instances that should be excluded from the results
-      #
-      # ==== Examples
-      #
-      # An equality exclusion:
-      #
-      #   Sunspot.search(Post) do
-      #     without(:blog_id, 1)
-      #   end
-      # 
-      # Other restriction types:
-      #
-      #   Sunspot.search(Post) do
-      #     without(:average_rating).greater_than(3.0)
-      #   end
-      #
-      # Exclusion by identity:
-      #
-      #   Sunspot.search(Post) do
-      #     without(some_post_instance)
-      #   end
-      #
-      def without(*args)
-        case args.first
-        when String, Symbol
-          field_name = args[0]
-          value = args.length &gt; 1 ? args[1] : NONE
-          if value == NONE
-            DSL::Restriction.new(field_name.to_sym, @query, true)
-          else
-            @query.add_negated_restriction(field_name, Sunspot::Restriction::EqualTo, value)
-          end
-        else
-          instances = args
-          for instance in instances.flatten
-            @query.exclude_instance(instance)
-          end
-        end
-      end
-
       # Paginate your search. This works the same way as WillPaginate's
       # paginate().
       #
@@ -148,28 +48,9 @@ module Sunspot
         @query.paginate(page, per_page)
       end
 
-      # Specify the order that results should be returned in. This method can
-      # be called multiple times; precedence will be in the order given.
-      #
-      # ==== Parameters
-      #
-      # field_name&lt;Symbol&gt;:: the field to use for ordering
-      # direction&lt;Symbol&gt;:: :asc or :desc (default :asc)
-      #
-      def order_by(field_name, direction = nil)
-        @query.order_by(field_name, direction)
-      end
-
-      # Request facets on the given field names. See Sunspot::Search#facet and
-      # Sunspot::Facet for information on what is returned.
-      #
-      # ==== Parameters
-      #
-      # field_names...&lt;Symbol&gt;:: fields for which to return field facets
-      def facet(*field_names)
-        for field_name in field_names
-          @query.add_field_facet(field_name)
-        end
+      #TODO document
+      def dynamic(field_name, &amp;block)
+        Scope.new(@query.add_dynamic_query(field_name)).instance_eval(&amp;block)
       end
     end
   end</diff>
      <filename>lib/sunspot/dsl/query.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,24 +1,129 @@
 module Sunspot
   module DSL
-    # 
-    # This class presents an API for building restrictions in the query DSL. The
-    # methods exposed are the snake-cased names of the classes defined in the
-    # Restriction module, with the exception of Base and SameAs. All methods
-    # take a single argument, which is the value to be applied to the
-    # restriction.
-    #
-    class Restriction
-      def initialize(field_name, query, negative)
-        @field_name, @query, @negative = field_name, query, negative
+    class Scope
+      NONE = Object.new
+
+      def initialize(query) #:nodoc:
+        @query = query
+      end
+
+      # 
+      # Build a positive restriction. With one argument, this method returns
+      # another DSL object which presents methods for attaching various
+      # restriction types. With two arguments, acts as a shorthand for creating
+      # an equality restriction.
+      #
+      # ==== Parameters
+      #
+      # field_name&lt;Symbol&gt;:: Name of the field on which to place the restriction
+      # value&lt;Symbol&gt;::
+      #   If passed, creates an equality restriction with this value
+      #
+      # ==== Returns
+      #
+      # Sunspot::DSL::Restriction::
+      #   Restriction DSL object (if only one argument is passed)
+      #
+      # ==== Examples
+      #
+      # An equality restriction:
+      #
+      #   Sunspot.search do
+      #     with(:blog_id, 1)
+      #   end
+      # 
+      # Other restriction types:
+      #
+      #   Sunspot.search(Post) do
+      #     with(:average_rating).greater_than(3.0)
+      #   end
+      #
+      def with(field_name, value = NONE)
+        if value == NONE
+          DSL::Restriction.new(field_name.to_sym, @query, false)
+        else
+          @query.add_restriction(field_name, Sunspot::Restriction::EqualTo, value, false)
+        end
       end
 
-      Sunspot::Restriction.names.each do |class_name|
-        method_name = Util.snake_case(class_name)
-        module_eval(&lt;&lt;-RUBY, __FILE__, __LINE__ + 1)
-          def #{method_name}(value)
-            @query.add_restriction(@field_name, Sunspot::Restriction::#{class_name}, value, @negative)
+      # 
+      # Build a negative restriction (exclusion). This method can take three
+      # forms: equality exclusion, exclusion by another restriction, or identity
+      # exclusion. The first two forms work the same way as the #with method;
+      # the third excludes a specific instance from the search results.
+      #
+      # ==== Parameters (exclusion by field value)
+      #
+      # field_name&lt;Symbol&gt;:: Name of the field on which to place the exclusion
+      # value&lt;Symbol&gt;::
+      #   If passed, creates an equality exclusion with this value
+      #
+      # ==== Parameters (exclusion by identity)
+      #
+      # args&lt;Object&gt;...::
+      #   One or more instances that should be excluded from the results
+      #
+      # ==== Examples
+      #
+      # An equality exclusion:
+      #
+      #   Sunspot.search(Post) do
+      #     without(:blog_id, 1)
+      #   end
+      # 
+      # Other restriction types:
+      #
+      #   Sunspot.search(Post) do
+      #     without(:average_rating).greater_than(3.0)
+      #   end
+      #
+      # Exclusion by identity:
+      #
+      #   Sunspot.search(Post) do
+      #     without(some_post_instance)
+      #   end
+      #
+      def without(*args)
+        case args.first
+        when String, Symbol
+          field_name = args[0]
+          value = args.length &gt; 1 ? args[1] : NONE
+          if value == NONE
+            DSL::Restriction.new(field_name.to_sym, @query, true)
+          else
+            @query.add_negated_restriction(field_name, Sunspot::Restriction::EqualTo, value)
+          end
+        else
+          instances = args
+          for instance in instances.flatten
+            @query.exclude_instance(instance)
           end
-        RUBY
+        end
+      end
+
+      # Specify the order that results should be returned in. This method can
+      # be called multiple times; precedence will be in the order given.
+      #
+      # ==== Parameters
+      #
+      # field_name&lt;Symbol&gt;:: the field to use for ordering
+      # direction&lt;Symbol&gt;:: :asc or :desc (default :asc)
+      #
+      def order_by(field_name, direction = nil)
+        @query.order_by(field_name, direction)
+      end
+
+      # Request facets on the given field names. See Sunspot::Search#facet and
+      # Sunspot::Facet for information on what is returned.
+      #
+      # ==== Parameters
+      #
+      # field_names...&lt;Symbol&gt;:: fields for which to return field facets
+      #
+      def facet(*field_names)
+        for field_name in field_names
+          @query.add_field_facet(field_name)
+        end
       end
     end
   end</diff>
      <filename>lib/sunspot/dsl/scope.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,47 @@
 module Sunspot
   module Field #:nodoc[all]
+    class FieldInstance
+      # The name of the field as it is indexed in Solr. The indexed name
+      # contains a suffix that contains information about the type as well as
+      # whether the field allows multiple values for a document.
+      #
+      # ==== Returns
+      #
+      # String:: The field's indexed name
+      #
+      def indexed_name
+        Solr::Util.query_parser_escape(&quot;#{@type.indexed_name(@name)}#{'m' if @multiple}&quot;)
+      end
+
+      # Convert a value to its representation for Solr indexing. This delegates
+      # to the #to_indexed method on the field's type.
+      #
+      # ==== Parameters
+      #
+      # value&lt;Object&gt;:: Value to convert to Solr representation
+      #
+      # ==== Returns
+      #
+      # String:: Solr representation of the object
+      #
+      # ==== Raises
+      #
+      # ArgumentError::
+      #   the value is an array, but this field does not allow multiple values
+      #
+      def to_indexed(value)
+        if value.is_a? Array
+          if @multiple
+            value.map { |val| to_indexed(val) }
+          else
+            raise ArgumentError, &quot;#{name} is not a multiple-value field, so it cannot index values #{value.inspect}&quot;
+          end
+        else
+          @type.to_indexed(value)
+        end
+      end
+    end
+
     #
     # Field classes encapsulate information about a field that has been configured
     # for search and indexing. They expose methods that are useful for both
@@ -7,7 +49,7 @@ module Sunspot
     #
     # Subclasses of Field::Base must implement the method #value_for
     #
-    class StaticField
+    class StaticField &lt; FieldInstance
       class &lt;&lt;self
         def build(name, type, options = {}, &amp;block)
           data_extractor =
@@ -44,7 +86,7 @@ module Sunspot
       #
       # Hash:: a single key-value pair with the field name and value
       #
-      def pair_for(model)
+      def pairs_for(model)
         unless (value = @data_extractor.value_for(model)).nil?
           { indexed_name.to_sym =&gt; to_indexed(value) }
         else
@@ -52,46 +94,6 @@ module Sunspot
         end
       end
 
-      # The name of the field as it is indexed in Solr. The indexed name
-      # contains a suffix that contains information about the type as well as
-      # whether the field allows multiple values for a document.
-      #
-      # ==== Returns
-      #
-      # String:: The field's indexed name
-      #
-      def indexed_name
-        &quot;#{type.indexed_name(name)}#{'m' if multiple?}&quot;
-      end
-
-      # Convert a value to its representation for Solr indexing. This delegates
-      # to the #to_indexed method on the field's type.
-      #
-      # ==== Parameters
-      #
-      # value&lt;Object&gt;:: Value to convert to Solr representation
-      #
-      # ==== Returns
-      #
-      # String:: Solr representation of the object
-      #
-      # ==== Raises
-      #
-      # ArgumentError::
-      #   the value is an array, but this field does not allow multiple values
-      #
-      def to_indexed(value)
-        if value.is_a? Array
-          if multiple?
-            value.map { |val| to_indexed(val) }
-          else
-            raise ArgumentError, &quot;#{name} is not a multiple-value field, so it cannot index values #{value.inspect}&quot;
-          end
-        else
-          type.to_indexed(value)
-        end
-      end
-
       # Cast the value into the appropriate Ruby class for the field's type
       #
       # ==== Parameters
@@ -103,7 +105,7 @@ module Sunspot
       # Object:: The cast value
       #
       def cast(value)
-        type.cast(value)
+        @type.cast(value)
       end
 
       # ==== Returns
@@ -113,5 +115,53 @@ module Sunspot
         !!@multiple
       end
     end
+
+    #TODO document
+    class DynamicField
+      class &lt;&lt;self
+        def build(name)
+          new(name, DataExtractor::AttributeExtractor.new(name))
+        end
+      end
+
+      attr_accessor :name
+
+      def initialize(name, data_extractor)
+        @name, @data_extractor = name, data_extractor
+      end
+
+      def pairs_for(model)
+        pairs = {}
+        if values = @data_extractor.value_for(model)
+          values.each_pair do |custom_name, value|
+            type = Type::StringType
+            pairs[indexed_name(custom_name, type).to_sym] = type.to_indexed(value)
+          end
+        end
+        pairs
+      end
+
+      def build(custom_name, value)
+        #TODO not here
+        type = case Array(value).first
+        when String
+          Type::StringType
+        end
+        DynamicFieldInstance.new(@name, custom_name, type, @data_extractor)
+      end
+
+      private
+
+      def indexed_name(custom_name, type)
+        type.indexed_name(&quot;#{@name}:#{custom_name}&quot;)
+      end
+    end
+
+    class DynamicFieldInstance &lt; FieldInstance
+      def initialize(field_name, custom_name, type, data_extractor)
+        @name = &quot;#{field_name}:#{custom_name}&quot;
+        @type, @data_extractor = type, data_extractor
+      end
+    end
   end
 end</diff>
      <filename>lib/sunspot/field.rb</filename>
    </modified>
    <modified>
      <diff>@@ -21,7 +21,7 @@ module Sunspot
     def add(model)
       hash = static_hash_for(model)
       for field in @setup.all_fields
-        hash.merge!(field.pair_for(model))
+        hash.merge!(field.pairs_for(model))
       end
       @connection.add(hash)
     end</diff>
      <filename>lib/sunspot/indexer.rb</filename>
    </modified>
    <modified>
      <diff>@@ -12,6 +12,7 @@ module Sunspot
     def initialize(types, configuration) #:nodoc:
       @types, @configuration = types, configuration
       @rows = @configuration.pagination.default_per_page
+      @components = []
     end
 
     # 
@@ -33,7 +34,8 @@ module Sunspot
       if restriction_type.is_a?(Symbol)
         restriction_type = Restriction[restriction_type]
       end
-      add_component(restriction_type.new(field(field_name), value, negated))
+      @components &lt;&lt; restriction = restriction_type.new(field(field_name), value, negated)
+      restriction
     end
 
     # 
@@ -62,7 +64,7 @@ module Sunspot
     # instance&lt;Object&gt;:: instance to exclude from results
     #
     def exclude_instance(instance)
-      add_component(Sunspot::Restriction::SameAs.new(instance, true))
+      @components &lt;&lt; Sunspot::Restriction::SameAs.new(instance, true)
     end
 
     # 
@@ -73,7 +75,13 @@ module Sunspot
     # field_name&lt;Symbol&gt;:: Name of the field on which to get a facet
     #
     def add_field_facet(field_name)
-      add_component(Facets::FieldFacet.new(field(field_name)))
+      @components &lt;&lt; Facets::FieldFacet.new(field(field_name))
+    end
+
+    # TODO document
+    def add_dynamic_query(field_name)
+      @components &lt;&lt; dynamic_query = DynamicQuery.new(dynamic_field(field_name), self)
+      dynamic_query
     end
 
     #
@@ -139,7 +147,7 @@ module Sunspot
       params[:sort] = @sort if @sort
       params[:start] = @start if @start
       params[:rows] = @rows if @rows
-      for component in components
+      for component in @components
         Util.deep_merge!(params, component.to_params)
       end
       params
@@ -204,6 +212,10 @@ module Sunspot
       fields_hash[field_name.to_sym] || raise(UnrecognizedFieldError, &quot;No field configured for #{@types * ', '} with name '#{field_name}'&quot;)
     end
 
+    def dynamic_field(field_name)
+      field = dynamic_fields_hash[field_name.to_sym]
+    end
+
     # 
     # Pass in search options as a hash. This is not the preferred way of
     # building a Sunspot search, but it is made available as experience shows
@@ -261,25 +273,6 @@ module Sunspot
     private
 
     # 
-    # Add a query component
-    #
-    # ==== Parameters
-    #
-    # component&lt;~to_params&gt;::  A restriction query component
-    #
-    def add_component(component)
-      components &lt;&lt; component
-    end
-
-    # ==== Returns
-    #
-    # Array:: Collection of query components
-    #
-    def components
-      @components ||= []
-    end
-
-    # 
     # Boolean phrase that restricts results to objects of the type(s) under
     # query. If this is an open query (no types specified) then it sends a
     # no-op phrase because Solr requires that the :q parameter not be empty.
@@ -310,23 +303,43 @@ module Sunspot
     # Hash:: field names keyed to field objects
     #
     def fields_hash
-      @fields_hash ||= begin
-        fields_hash = @types.inject({}) do |hash, type|
-          Setup.for(type).fields.each do |field|
-            (hash[field.name.to_sym] ||= {})[type.name] = field
+      @fields_hash ||=
+        begin
+          fields_hash = @types.inject({}) do |hash, type|
+            Setup.for(type).fields.each do |field|
+              (hash[field.name.to_sym] ||= {})[type.name] = field
+            end
+            hash
+          end
+          fields_hash.each_pair do |field_name, field_configurations_hash|
+            if @types.any? { |type| field_configurations_hash[type.name].nil? } # at least one type doesn't have this field configured
+              fields_hash.delete(field_name)
+            elsif field_configurations_hash.values.map { |configuration| configuration.indexed_name }.uniq.length != 1 # fields with this name have different configs
+              fields_hash.delete(field_name)
+            else
+              fields_hash[field_name] = field_configurations_hash.values.first
+            end
           end
-          hash
         end
-        fields_hash.each_pair do |field_name, field_configurations_hash|
-          if @types.any? { |type| field_configurations_hash[type.name].nil? } # at least one type doesn't have this field configured
-            fields_hash.delete(field_name)
-          elsif field_configurations_hash.values.map { |configuration| configuration.indexed_name }.uniq.length != 1 # fields with this name have different configs
-            fields_hash.delete(field_name)
-          else
-            fields_hash[field_name] = field_configurations_hash.values.first
+    end
+
+    def dynamic_fields_hash
+      @dynamic_fields_hash ||=
+        begin
+          dynamic_fields_hash = @types.inject({}) do |hash, type|
+            Setup.for(type).dynamic_fields.each do |field|
+              (hash[field.name.to_sym] ||= {})[type.name] = field
+            end
+            hash
+          end
+          dynamic_fields_hash.each_pair do |field_name, field_configurations_hash|
+            if @types.any? { |type| field_configurations_hash[type.name].nil? }
+              dynamic_fields_hash.delete(field_name)
+            else
+              dynamic_fields_hash[field_name] = field_configurations_hash.values.first
+            end
           end
         end
-      end
     end
   end
 end</diff>
      <filename>lib/sunspot/query.rb</filename>
    </modified>
    <modified>
      <diff>@@ -6,7 +6,7 @@ module Sunspot
   class Setup #:nodoc:
     def initialize(clazz)
       @class_name = clazz.name
-      @fields, @text_fields = [], []
+      @fields, @text_fields, @dynamic_fields = [], [], []
       @dsl = DSL::Fields.new(self)
     end
 
@@ -32,6 +32,11 @@ module Sunspot
       @text_fields.concat(Array(fields))
     end
 
+    #TODO document
+    def add_dynamic_fields(fields)
+      @dynamic_fields.concat(Array(fields))
+    end
+
     # 
     # Builder method for evaluating the setup DSL
     #
@@ -75,7 +80,14 @@ module Sunspot
     # Array:: Collection of all text and scope fields associated with this setup
     #
     def all_fields
-      fields + text_fields
+      fields + text_fields + dynamic_fields
+    end
+
+    # TODO document
+    def dynamic_fields
+      dynamic_fields = @dynamic_fields.dup
+      dynamic_fields.concat(parent.dynamic_fields) if parent
+      dynamic_fields
     end
 
     # </diff>
      <filename>lib/sunspot/setup.rb</filename>
    </modified>
    <modified>
      <diff>@@ -157,6 +157,15 @@ describe 'Search' do
     end
   end
 
+  it 'should restrict by dynamic field using block' do
+    connection.should_receive(:query).with('(type:Post)', hash_including(:filter_queries =&gt; ['custom\:test_s:string']))
+    session.search Post do
+      dynamic :custom do
+        with :test, 'string'
+      end
+    end
+  end
+
   it 'should paginate using default per_page when page not provided' do
     connection.should_receive(:query).with('(type:Post)', hash_including(:rows =&gt; 30))
     session.search Post</diff>
      <filename>spec/api/build_search_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -115,6 +115,16 @@ describe 'indexer' do
     end
   end
 
+  describe 'dynamic fields' do
+    #TODO all types, multiple values, virtuals
+
+    it 'should index string data' do
+      post(:custom =&gt; { :test =&gt; 'string' })
+      connection.should_receive(:add).with(hash_including(:&quot;custom:test_s&quot; =&gt; 'string'))
+      session.index(post)
+    end
+  end
+
   it 'should throw a NoMethodError only if a nonexistent type is defined' do
     lambda { Sunspot.setup(Post) { string :author_name }}.should_not raise_error
     lambda { Sunspot.setup(Post) { bogus :journey }}.should raise_error(NoMethodError)</diff>
      <filename>spec/api/indexer_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -25,8 +25,12 @@ class Post &lt; BaseClass
     ids.map { |id| get(id) }.sort_by { |post| post.id } # this is so that results are not ordered by coincidence
   end
 
+  def custom
+    @custom ||= {}
+  end
+
   private
-  attr_writer :category_ids
+  attr_writer :category_ids, :custom
 end
 
 Sunspot.setup(Post) do
@@ -43,4 +47,5 @@ Sunspot.setup(Post) do
   integer :primary_category_id do |post|
     post.category_ids.first
   end
+  dynamic :custom
 end</diff>
      <filename>spec/mocks/post.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>4ca5762b51a9d58f52d15a2af27db99620c7d5b6</id>
    </parent>
  </parents>
  <author>
    <name>Mat Brown</name>
    <email>mat@patch.com</email>
  </author>
  <url>http://github.com/outoftime/sunspot/commit/aafb18a459c3cdd4c2cde9e72998163d8da98359</url>
  <id>aafb18a459c3cdd4c2cde9e72998163d8da98359</id>
  <committed-date>2009-05-15T15:05:35-07:00</committed-date>
  <authored-date>2009-05-15T15:05:35-07:00</authored-date>
  <message>Initial implementation of dynamic fields

Passing basic API and integration tests, but lots of work to do. In particular,
need to refactor out implementation uglies and also handle and spec all types.</message>
  <tree>7743bf2a18e4f55a10862fe6ef585b30f891459e</tree>
  <committer>
    <name>Mat Brown</name>
    <email>mat@patch.com</email>
  </committer>
</commit>
