ginjo-rfm 1.4.4 → 2.0.pre31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +107 -0
 - data/README.md +378 -133
 - data/lib/rfm.rb +51 -19
 - data/lib/rfm/VERSION +1 -1
 - data/lib/rfm/base.rb +416 -0
 - data/lib/rfm/database.rb +14 -9
 - data/lib/rfm/layout.rb +148 -96
 - data/lib/rfm/metadata/field.rb +5 -5
 - data/lib/rfm/metadata/field_control.rb +52 -51
 - data/lib/rfm/metadata/script.rb +7 -5
 - data/lib/rfm/record.rb +71 -56
 - data/lib/rfm/resultset.rb +45 -26
 - data/lib/rfm/server.rb +21 -17
 - data/lib/rfm/utilities/complex_query.rb +64 -0
 - data/lib/rfm/utilities/config.rb +115 -0
 - data/lib/rfm/utilities/core_ext.rb +90 -0
 - data/lib/rfm/utilities/factory.rb +100 -17
 - data/lib/rfm/utilities/xml_parser.rb +94 -0
 - data/lib/rfm/version.rb +1 -1
 - data/lib/rfm/xml_mini/hpricot.rb +133 -0
 - metadata +87 -30
 
    
        data/lib/rfm/metadata/script.rb
    CHANGED
    
    | 
         @@ -9,12 +9,14 @@ module Rfm 
     | 
|
| 
       9 
9 
     | 
    
         
             
                #
         
     | 
| 
       10 
10 
     | 
    
         
             
                # If you want to _run_ a script, see the Layout object instead.
         
     | 
| 
       11 
11 
     | 
    
         
             
                class Script
         
     | 
| 
       12 
     | 
    
         
            -
                  def initialize(name,  
     | 
| 
      
 12 
     | 
    
         
            +
                  def initialize(name, db_obj)
         
     | 
| 
       13 
13 
     | 
    
         
             
                    @name = name
         
     | 
| 
       14 
     | 
    
         
            -
                     
     | 
| 
      
 14 
     | 
    
         
            +
                    self.db = db_obj
         
     | 
| 
       15 
15 
     | 
    
         
             
                  end
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
      
 17 
     | 
    
         
            +
                  meta_attr_accessor :db
         
     | 
| 
       17 
18 
     | 
    
         
             
                  attr_reader :name
         
     | 
| 
       18 
     | 
    
         
            -
                end
         
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
            end
         
     | 
| 
      
 19 
     | 
    
         
            +
                end # Script
         
     | 
| 
      
 20 
     | 
    
         
            +
                
         
     | 
| 
      
 21 
     | 
    
         
            +
              end # Metadata
         
     | 
| 
      
 22 
     | 
    
         
            +
            end # Rfm
         
     | 
    
        data/lib/rfm/record.rb
    CHANGED
    
    | 
         @@ -1,5 +1,4 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Rfm
         
     | 
| 
       2 
     | 
    
         
            -
              
         
     | 
| 
       3 
2 
     | 
    
         
             
              # The Record object represents a single FileMaker record. You typically get them from ResultSet objects.
         
     | 
| 
       4 
3 
     | 
    
         
             
              # For example, you might use a Layout object to find some records:
         
     | 
| 
       5 
4 
     | 
    
         
             
              #
         
     | 
| 
         @@ -69,7 +68,14 @@ module Rfm 
     | 
|
| 
       69 
68 
     | 
    
         
             
              #   }
         
     | 
| 
       70 
69 
     | 
    
         
             
              #
         
     | 
| 
       71 
70 
     | 
    
         
             
              # This code iterates through the rows of the _Orders_ portal.
         
     | 
| 
       72 
     | 
    
         
            -
              # 
     | 
| 
      
 71 
     | 
    
         
            +
              #
         
     | 
| 
      
 72 
     | 
    
         
            +
              #	As a convenience, you can call a specific portal as a method on your record, if the table occurrence name does
         
     | 
| 
      
 73 
     | 
    
         
            +
              # not have any characters that are prohibited in ruby method names, just as you can call a field with a method:
         
     | 
| 
      
 74 
     | 
    
         
            +
            	# 	
         
     | 
| 
      
 75 
     | 
    
         
            +
            	#   myRecord.orders.each {|portal_row|
         
     | 
| 
      
 76 
     | 
    
         
            +
            	#   	puts portal_row["Order Number"]
         
     | 
| 
      
 77 
     | 
    
         
            +
            	#   }
         
     | 
| 
      
 78 
     | 
    
         
            +
            	#   
         
     | 
| 
       73 
79 
     | 
    
         
             
              # =Field Types and Ruby Types
         
     | 
| 
       74 
80 
     | 
    
         
             
              #
         
     | 
| 
       75 
81 
     | 
    
         
             
              # RFM automatically converts data from FileMaker into a Ruby object with the most reasonable type possible. The 
         
     | 
| 
         @@ -100,41 +106,51 @@ module Rfm 
     | 
|
| 
       100 
106 
     | 
    
         
             
              #   copy of the same record
         
     | 
| 
       101 
107 
     | 
    
         
             
              class Record < Rfm::CaseInsensitiveHash
         
     | 
| 
       102 
108 
     | 
    
         | 
| 
      
 109 
     | 
    
         
            +
                attr_accessor :layout, :resultset
         
     | 
| 
       103 
110 
     | 
    
         
             
                attr_reader :record_id, :mod_id, :portals
         
     | 
| 
      
 111 
     | 
    
         
            +
                def_delegators :resultset, :field_meta
         
     | 
| 
      
 112 
     | 
    
         
            +
                def_delegators :layout, :db, :database, :server
         
     | 
| 
       104 
113 
     | 
    
         | 
| 
       105 
     | 
    
         
            -
                def initialize(record,  
     | 
| 
       106 
     | 
    
         
            -
                  @record_id = record['record-id']
         
     | 
| 
       107 
     | 
    
         
            -
                  @mod_id    = record['mod-id']
         
     | 
| 
       108 
     | 
    
         
            -
                  @mods      = {}
         
     | 
| 
       109 
     | 
    
         
            -
                  @layout    = layout
         
     | 
| 
       110 
     | 
    
         
            -
                  @portals ||= Rfm::CaseInsensitiveHash.new
         
     | 
| 
      
 114 
     | 
    
         
            +
                def initialize(record, resultset_obj, field_meta, layout_obj, portal=nil)
         
     | 
| 
       111 
115 
     | 
    
         | 
| 
       112 
     | 
    
         
            -
                   
     | 
| 
       113 
     | 
    
         
            -
                  
         
     | 
| 
       114 
     | 
    
         
            -
                  record 
     | 
| 
      
 116 
     | 
    
         
            +
                  @layout        = layout_obj
         
     | 
| 
      
 117 
     | 
    
         
            +
                  @resultset     = resultset_obj
         
     | 
| 
      
 118 
     | 
    
         
            +
                  @record_id     = record['record-id']
         
     | 
| 
      
 119 
     | 
    
         
            +
                  @mod_id        = record['mod-id']
         
     | 
| 
      
 120 
     | 
    
         
            +
                  @mods          = {}
         
     | 
| 
      
 121 
     | 
    
         
            +
                  @portals     ||= Rfm::CaseInsensitiveHash.new
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                  relatedsets = !portal && resultset_obj.instance_variable_get(:@include_portals) ? record['relatedset'].rfm_force_array : []
         
     | 
| 
      
 124 
     | 
    
         
            +
                        
         
     | 
| 
      
 125 
     | 
    
         
            +
                  record['field'].rfm_force_array.each do |field|
         
     | 
| 
      
 126 
     | 
    
         
            +
                  	next unless field
         
     | 
| 
       115 
127 
     | 
    
         
             
                    field_name = field['name']
         
     | 
| 
       116 
128 
     | 
    
         
             
                    field_name.gsub!(Regexp.new(portal + '::'), '') if portal
         
     | 
| 
       117 
129 
     | 
    
         
             
                    datum = []
         
     | 
| 
       118 
130 
     | 
    
         | 
| 
       119 
     | 
    
         
            -
                    field 
     | 
| 
       120 
     | 
    
         
            -
             
     | 
| 
       121 
     | 
    
         
            -
             
     | 
| 
      
 131 
     | 
    
         
            +
                    data = field['data']; data = data.is_a?(Hash) ? [data] : data
         
     | 
| 
      
 132 
     | 
    
         
            +
                    data.each do |x|
         
     | 
| 
      
 133 
     | 
    
         
            +
                    	next unless field_meta[field_name]
         
     | 
| 
      
 134 
     | 
    
         
            +
                      datum.push(field_meta[field_name].coerce(x['__content__'], resultset_obj))
         
     | 
| 
      
 135 
     | 
    
         
            +
                    end if data
         
     | 
| 
       122 
136 
     | 
    
         | 
| 
       123 
137 
     | 
    
         
             
                    if datum.length == 1
         
     | 
| 
       124 
     | 
    
         
            -
                       
     | 
| 
      
 138 
     | 
    
         
            +
                      rfm_super[field_name] = datum[0]
         
     | 
| 
       125 
139 
     | 
    
         
             
                    elsif datum.length == 0
         
     | 
| 
       126 
     | 
    
         
            -
                       
     | 
| 
      
 140 
     | 
    
         
            +
                      rfm_super[field_name] = nil
         
     | 
| 
       127 
141 
     | 
    
         
             
                    else
         
     | 
| 
       128 
     | 
    
         
            -
                       
     | 
| 
      
 142 
     | 
    
         
            +
                      rfm_super[field_name] = datum
         
     | 
| 
       129 
143 
     | 
    
         
             
                    end
         
     | 
| 
       130 
144 
     | 
    
         
             
                  end
         
     | 
| 
       131 
145 
     | 
    
         | 
| 
       132 
146 
     | 
    
         
             
                  unless relatedsets.empty?
         
     | 
| 
       133 
147 
     | 
    
         
             
                    relatedsets.each do |relatedset|
         
     | 
| 
      
 148 
     | 
    
         
            +
                    	next if relatedset.blank?
         
     | 
| 
       134 
149 
     | 
    
         
             
                      tablename, records = relatedset['table'], []
         
     | 
| 
       135 
150 
     | 
    
         | 
| 
       136 
     | 
    
         
            -
                      relatedset 
     | 
| 
       137 
     | 
    
         
            -
             
     | 
| 
      
 151 
     | 
    
         
            +
                      relatedset['record'].rfm_force_array.each do |record|
         
     | 
| 
      
 152 
     | 
    
         
            +
                      	next unless record
         
     | 
| 
      
 153 
     | 
    
         
            +
                        records << self.class.new(record, resultset_obj, resultset_obj.portal_meta[tablename], layout_obj, tablename)
         
     | 
| 
       138 
154 
     | 
    
         
             
                      end
         
     | 
| 
       139 
155 
     | 
    
         | 
| 
       140 
156 
     | 
    
         
             
                      @portals[tablename] = records
         
     | 
| 
         @@ -144,9 +160,9 @@ module Rfm 
     | 
|
| 
       144 
160 
     | 
    
         
             
                  @loaded = true
         
     | 
| 
       145 
161 
     | 
    
         
             
                end
         
     | 
| 
       146 
162 
     | 
    
         | 
| 
       147 
     | 
    
         
            -
                def self.build_records(records,  
     | 
| 
      
 163 
     | 
    
         
            +
                def self.build_records(records, resultset_obj, field_meta, layout_obj, portal=nil)
         
     | 
| 
       148 
164 
     | 
    
         
             
                  records.each do |record|
         
     | 
| 
       149 
     | 
    
         
            -
                     
     | 
| 
      
 165 
     | 
    
         
            +
                    resultset_obj << self.new(record, resultset_obj, field_meta, layout_obj, portal)
         
     | 
| 
       150 
166 
     | 
    
         
             
                  end
         
     | 
| 
       151 
167 
     | 
    
         
             
                end
         
     | 
| 
       152 
168 
     | 
    
         | 
| 
         @@ -164,16 +180,15 @@ module Rfm 
     | 
|
| 
       164 
180 
     | 
    
         
             
                # to optimize on your end. Just save, and if you've changed the record it will be saved. If not, no
         
     | 
| 
       165 
181 
     | 
    
         
             
                # server hit is incurred.
         
     | 
| 
       166 
182 
     | 
    
         
             
                def save
         
     | 
| 
       167 
     | 
    
         
            -
                  self.merge!( 
     | 
| 
      
 183 
     | 
    
         
            +
                  self.merge!(layout.edit(self.record_id, @mods)[0]) if @mods.size > 0
         
     | 
| 
       168 
184 
     | 
    
         
             
                  @mods.clear
         
     | 
| 
       169 
185 
     | 
    
         
             
                end
         
     | 
| 
       170 
     | 
    
         
            -
                
         
     | 
| 
       171 
186 
     | 
    
         | 
| 
       172 
187 
     | 
    
         
             
                # Like Record::save, except it fails (and raises an error) if the underlying record in FileMaker was
         
     | 
| 
       173 
188 
     | 
    
         
             
                # modified after the record was fetched but before it was saved. In other words, prevents you from
         
     | 
| 
       174 
189 
     | 
    
         
             
                # accidentally overwriting changes someone else made to the record.
         
     | 
| 
       175 
190 
     | 
    
         
             
                def save_if_not_modified
         
     | 
| 
       176 
     | 
    
         
            -
                  self.merge!( 
     | 
| 
      
 191 
     | 
    
         
            +
                  self.merge!(layout.edit(@record_id, @mods, {:modification_id => @mod_id})[0]) if @mods.size > 0
         
     | 
| 
       177 
192 
     | 
    
         
             
                  @mods.clear
         
     | 
| 
       178 
193 
     | 
    
         
             
                end
         
     | 
| 
       179 
194 
     | 
    
         | 
| 
         @@ -190,43 +205,43 @@ module Rfm 
     | 
|
| 
       190 
205 
     | 
    
         
             
                #
         
     | 
| 
       191 
206 
     | 
    
         
             
                # When you do, the change is noted, but *the data is not updated in FileMaker*. You must call
         
     | 
| 
       192 
207 
     | 
    
         
             
                # Record::save or Record::save_if_not_modified to actually save the data.
         
     | 
| 
       193 
     | 
    
         
            -
             
     | 
| 
       194 
     | 
    
         
            -
             
     | 
| 
       195 
     | 
    
         
            -
             
     | 
| 
       196 
     | 
    
         
            -
             
     | 
| 
       197 
     | 
    
         
            -
             
     | 
| 
       198 
     | 
    
         
            -
                  @mods[name] = value
         
     | 
| 
       199 
     | 
    
         
            -
                  self.merge! @mods
         
     | 
| 
       200 
     | 
    
         
            -
                end
         
     | 
| 
       201 
     | 
    
         
            -
             
     | 
| 
       202 
     | 
    
         
            -
                alias :_old_hash_reader :[]
         
     | 
| 
       203 
     | 
    
         
            -
                def [](value)
         
     | 
| 
       204 
     | 
    
         
            -
                  read_attribute(value)
         
     | 
| 
       205 
     | 
    
         
            -
                end
         
     | 
| 
      
 208 
     | 
    
         
            +
              	def [](key)
         
     | 
| 
      
 209 
     | 
    
         
            +
              		return fetch(key.to_s.downcase)
         
     | 
| 
      
 210 
     | 
    
         
            +
              	rescue IndexError
         
     | 
| 
      
 211 
     | 
    
         
            +
                	raise Rfm::ParameterError, "#{key} does not exists as a field in the current Filemaker layout." unless key.to_s == '' #unless (!layout or self.key?(key_string))
         
     | 
| 
      
 212 
     | 
    
         
            +
              	end
         
     | 
| 
       206 
213 
     | 
    
         | 
| 
       207 
214 
     | 
    
         
             
                def respond_to?(symbol, include_private = false)
         
     | 
| 
       208 
215 
     | 
    
         
             
                  return true if self.include?(symbol.to_s)
         
     | 
| 
       209 
216 
     | 
    
         
             
                  super
         
     | 
| 
       210 
217 
     | 
    
         
             
                end
         
     | 
| 
      
 218 
     | 
    
         
            +
                
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
                def []=(key, value)
         
     | 
| 
      
 221 
     | 
    
         
            +
                  key_string = key.to_s.downcase
         
     | 
| 
      
 222 
     | 
    
         
            +
                  return super unless @loaded # is this needed?
         
     | 
| 
      
 223 
     | 
    
         
            +
                  raise Rfm::ParameterError, "You attempted to modify a field that does not exist in the current Filemaker layout." unless self.key?(key_string)
         
     | 
| 
      
 224 
     | 
    
         
            +
                  @mods[key_string] = value
         
     | 
| 
      
 225 
     | 
    
         
            +
                  super(key, value)
         
     | 
| 
      
 226 
     | 
    
         
            +
                end
         
     | 
| 
      
 227 
     | 
    
         
            +
                
         
     | 
| 
      
 228 
     | 
    
         
            +
            	  def field_names
         
     | 
| 
      
 229 
     | 
    
         
            +
                	resultset.field_names rescue layout.field_names
         
     | 
| 
      
 230 
     | 
    
         
            +
                end
         
     | 
| 
       211 
231 
     | 
    
         | 
| 
       212 
     | 
    
         
            -
                private
         
     | 
| 
       213 
232 
     | 
    
         | 
| 
       214 
     | 
    
         
            -
             
     | 
| 
       215 
     | 
    
         
            -
                  	key_string = key.to_s.downcase
         
     | 
| 
       216 
     | 
    
         
            -
                    raise NoMethodError, 
         
     | 
| 
       217 
     | 
    
         
            -
                      "#{key_string} does not exists as a field in the current Filemaker layout." unless (!@layout or self.key?(key_string))   #!self.keys.grep(/#{key_string}/i).empty?
         
     | 
| 
       218 
     | 
    
         
            -
                    self._old_hash_reader(key).to_s.empty? ? nil : self._old_hash_reader(key) if self._old_hash_reader(key)
         
     | 
| 
       219 
     | 
    
         
            -
                  end
         
     | 
| 
      
 233 
     | 
    
         
            +
              private
         
     | 
| 
       220 
234 
     | 
    
         | 
| 
       221 
     | 
    
         
            -
             
     | 
| 
       222 
     | 
    
         
            -
             
     | 
| 
       223 
     | 
    
         
            -
             
     | 
| 
       224 
     | 
    
         
            -
             
     | 
| 
       225 
     | 
    
         
            -
             
     | 
| 
       226 
     | 
    
         
            -
             
     | 
| 
       227 
     | 
    
         
            -
             
     | 
| 
       228 
     | 
    
         
            -
             
     | 
| 
       229 
     | 
    
         
            -
             
     | 
| 
       230 
     | 
    
         
            -
             
     | 
| 
       231 
     | 
    
         
            -
             
     | 
| 
       232 
     | 
    
         
            -
            end
         
     | 
| 
      
 235 
     | 
    
         
            +
              	def method_missing (symbol, *attrs, &block)
         
     | 
| 
      
 236 
     | 
    
         
            +
              	  method = symbol.to_s
         
     | 
| 
      
 237 
     | 
    
         
            +
              	  return self[method] if self.key?(method)
         
     | 
| 
      
 238 
     | 
    
         
            +
              	  return @portals[method] if @portals and @portals.key?(method)
         
     | 
| 
      
 239 
     | 
    
         
            +
             
     | 
| 
      
 240 
     | 
    
         
            +
              	  if method =~ /(=)$/
         
     | 
| 
      
 241 
     | 
    
         
            +
              	    return self[$`] = attrs.first if self.key?($`)
         
     | 
| 
      
 242 
     | 
    
         
            +
              	  end
         
     | 
| 
      
 243 
     | 
    
         
            +
              	  super
         
     | 
| 
      
 244 
     | 
    
         
            +
            		end
         
     | 
| 
      
 245 
     | 
    
         
            +
              	
         
     | 
| 
      
 246 
     | 
    
         
            +
              end # Record
         
     | 
| 
      
 247 
     | 
    
         
            +
            end # Rfm
         
     | 
    
        data/lib/rfm/resultset.rb
    CHANGED
    
    | 
         @@ -5,7 +5,7 @@ 
     | 
|
| 
       5 
5 
     | 
    
         
             
            # Author::    Geoff Coffey  (mailto:gwcoffey@gmail.com)
         
     | 
| 
       6 
6 
     | 
    
         
             
            # Copyright:: Copyright (c) 2007 Six Fried Rice, LLC and Mufaddal Khumri
         
     | 
| 
       7 
7 
     | 
    
         
             
            # License::   See MIT-LICENSE for details
         
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
       9 
9 
     | 
    
         
             
            require 'bigdecimal'
         
     | 
| 
       10 
10 
     | 
    
         
             
            require 'rfm/record'
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
         @@ -40,7 +40,8 @@ module Rfm 
     | 
|
| 
       40 
40 
     | 
    
         
             
                attr_reader :layout, :server
         
     | 
| 
       41 
41 
     | 
    
         
             
                attr_reader :field_meta, :portal_meta
         
     | 
| 
       42 
42 
     | 
    
         
             
                attr_reader :date_format, :time_format, :timestamp_format
         
     | 
| 
       43 
     | 
    
         
            -
                attr_reader :total_count, :foundset_count
         
     | 
| 
      
 43 
     | 
    
         
            +
                attr_reader :total_count, :foundset_count, :table
         
     | 
| 
      
 44 
     | 
    
         
            +
                def_delegators :layout, :db, :database
         
     | 
| 
       44 
45 
     | 
    
         | 
| 
       45 
46 
     | 
    
         
             
                # Initializes a new ResultSet object. You will probably never do this your self (instead, use the Layout
         
     | 
| 
       46 
47 
     | 
    
         
             
                # object to get various ResultSet obejects).
         
     | 
| 
         @@ -64,62 +65,80 @@ module Rfm 
     | 
|
| 
       64 
65 
     | 
    
         
             
                #   layout contains portals, you can find out what fields they contain here. Again, if it's the data you're
         
     | 
| 
       65 
66 
     | 
    
         
             
                #   after, you want to look at the Record object.
         
     | 
| 
       66 
67 
     | 
    
         | 
| 
       67 
     | 
    
         
            -
                def initialize( 
     | 
| 
       68 
     | 
    
         
            -
                  @layout           =  
     | 
| 
       69 
     | 
    
         
            -
                  @server           =  
     | 
| 
      
 68 
     | 
    
         
            +
                def initialize(server_obj, xml_response, layout_obj, portals=nil)
         
     | 
| 
      
 69 
     | 
    
         
            +
                  @layout           = layout_obj
         
     | 
| 
      
 70 
     | 
    
         
            +
                  @server           = server_obj
         
     | 
| 
       70 
71 
     | 
    
         
             
                  @field_meta     ||= Rfm::CaseInsensitiveHash.new
         
     | 
| 
       71 
72 
     | 
    
         
             
                  @portal_meta    ||= Rfm::CaseInsensitiveHash.new
         
     | 
| 
       72 
73 
     | 
    
         
             
                  @include_portals  = portals 
         
     | 
| 
       73 
74 
     | 
    
         | 
| 
       74 
     | 
    
         
            -
                  doc =  
     | 
| 
      
 75 
     | 
    
         
            +
                  doc = XmlParser.new(xml_response, :namespace=>false, :parser=>server.state[:parser])
         
     | 
| 
       75 
76 
     | 
    
         | 
| 
       76 
     | 
    
         
            -
                  error = doc 
     | 
| 
      
 77 
     | 
    
         
            +
                  error = doc['fmresultset']['error']['code'].to_i
         
     | 
| 
       77 
78 
     | 
    
         
             
                  check_for_errors(error, server.state[:raise_on_401])
         
     | 
| 
       78 
79 
     | 
    
         | 
| 
       79 
     | 
    
         
            -
                  datasource        = doc 
     | 
| 
       80 
     | 
    
         
            -
                  meta              = doc 
     | 
| 
       81 
     | 
    
         
            -
                  resultset         = doc 
     | 
| 
       82 
     | 
    
         
            -
             
     | 
| 
       83 
     | 
    
         
            -
                  @date_format      = convert_date_time_format(datasource.attribute('date-format').value)
         
     | 
| 
       84 
     | 
    
         
            -
                  @time_format      = convert_date_time_format(datasource.attribute('time-format').value)
         
     | 
| 
       85 
     | 
    
         
            -
                  @timestamp_format = convert_date_time_format(datasource.attribute('timestamp-format').value)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  datasource        = doc['fmresultset']['datasource']
         
     | 
| 
      
 81 
     | 
    
         
            +
                  meta              = doc['fmresultset']['metadata']
         
     | 
| 
      
 82 
     | 
    
         
            +
                  resultset         = doc['fmresultset']['resultset']
         
     | 
| 
       86 
83 
     | 
    
         | 
| 
       87 
     | 
    
         
            -
                  @ 
     | 
| 
       88 
     | 
    
         
            -
                  @ 
     | 
| 
      
 84 
     | 
    
         
            +
                  @date_format      = convert_date_time_format(datasource['date-format'].to_s)
         
     | 
| 
      
 85 
     | 
    
         
            +
                  @time_format      = convert_date_time_format(datasource['time-format'].to_s)
         
     | 
| 
      
 86 
     | 
    
         
            +
                  @timestamp_format = convert_date_time_format(datasource['timestamp-format'].to_s)
         
     | 
| 
       89 
87 
     | 
    
         | 
| 
      
 88 
     | 
    
         
            +
                  @foundset_count   = resultset['count'].to_s.to_i
         
     | 
| 
      
 89 
     | 
    
         
            +
                  @total_count      = datasource['total-count'].to_s.to_i
         
     | 
| 
      
 90 
     | 
    
         
            +
                  @table            = datasource['table']
         
     | 
| 
      
 91 
     | 
    
         
            +
                  
         
     | 
| 
      
 92 
     | 
    
         
            +
                  (layout.table = @table) if layout and layout.table_no_load.blank?
         
     | 
| 
      
 93 
     | 
    
         
            +
                  
         
     | 
| 
       90 
94 
     | 
    
         
             
                  parse_fields(meta)
         
     | 
| 
       91 
     | 
    
         
            -
                  parse_portals(meta) if @include_portals
         
     | 
| 
       92 
95 
     | 
    
         | 
| 
       93 
     | 
    
         
            -
                   
     | 
| 
      
 96 
     | 
    
         
            +
                  # This will always load portal meta, even if :include_portals was not specified.
         
     | 
| 
      
 97 
     | 
    
         
            +
                  # See Record for control of portal data loading.
         
     | 
| 
      
 98 
     | 
    
         
            +
                  parse_portals(meta)
         
     | 
| 
       94 
99 
     | 
    
         | 
| 
      
 100 
     | 
    
         
            +
                  return if resultset['record'].nil?
         
     | 
| 
      
 101 
     | 
    
         
            +
                  Rfm::Record.build_records(resultset['record'].rfm_force_array, self, @field_meta, @layout)
         
     | 
| 
       95 
102 
     | 
    
         
             
                end
         
     | 
| 
      
 103 
     | 
    
         
            +
                    
         
     | 
| 
      
 104 
     | 
    
         
            +
                def field_names
         
     | 
| 
      
 105 
     | 
    
         
            +
                	field_meta.collect{|k,v| v.name}
         
     | 
| 
      
 106 
     | 
    
         
            +
              	end
         
     | 
| 
      
 107 
     | 
    
         
            +
              	
         
     | 
| 
      
 108 
     | 
    
         
            +
              	def portal_names
         
     | 
| 
      
 109 
     | 
    
         
            +
              		portal_meta.keys
         
     | 
| 
      
 110 
     | 
    
         
            +
              	end
         
     | 
| 
      
 111 
     | 
    
         
            +
                
         
     | 
| 
       96 
112 
     | 
    
         | 
| 
       97 
113 
     | 
    
         
             
                private
         
     | 
| 
       98 
     | 
    
         
            -
                  def remove_namespace(xml)
         
     | 
| 
       99 
     | 
    
         
            -
                    xml.gsub('xmlns="http://www.filemaker.com/xml/fmresultset" version="1.0"', '')
         
     | 
| 
       100 
     | 
    
         
            -
                  end
         
     | 
| 
       101 
114 
     | 
    
         | 
| 
       102 
115 
     | 
    
         
             
                  def check_for_errors(code, raise_401)
         
     | 
| 
       103 
116 
     | 
    
         
             
                    raise Rfm::Error.getError(code) if code != 0 && (code != 401 || raise_401)
         
     | 
| 
       104 
117 
     | 
    
         
             
                  end
         
     | 
| 
       105 
118 
     | 
    
         | 
| 
       106 
119 
     | 
    
         
             
                  def parse_fields(meta)
         
     | 
| 
       107 
     | 
    
         
            -
             
     | 
| 
      
 120 
     | 
    
         
            +
                  	return if meta['field-definition'].blank?
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                    meta['field-definition'].rfm_force_array.each do |field|
         
     | 
| 
       108 
123 
     | 
    
         
             
                      @field_meta[field['name']] = Rfm::Metadata::Field.new(field)
         
     | 
| 
       109 
124 
     | 
    
         
             
                    end
         
     | 
| 
      
 125 
     | 
    
         
            +
                    (layout.field_names = field_names) if layout and layout.field_names_no_load.blank?
         
     | 
| 
       110 
126 
     | 
    
         
             
                  end
         
     | 
| 
       111 
127 
     | 
    
         | 
| 
       112 
128 
     | 
    
         
             
                  def parse_portals(meta)
         
     | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
       114 
     | 
    
         
            -
             
     | 
| 
      
 129 
     | 
    
         
            +
                  	return if meta['relatedset-definition'].blank?
         
     | 
| 
      
 130 
     | 
    
         
            +
                    meta['relatedset-definition'].rfm_force_array.each do |relatedset|
         
     | 
| 
      
 131 
     | 
    
         
            +
                    	next if relatedset.blank?
         
     | 
| 
      
 132 
     | 
    
         
            +
                      table, fields = relatedset['table'], {}
         
     | 
| 
       115 
133 
     | 
    
         | 
| 
       116 
     | 
    
         
            -
                      relatedset 
     | 
| 
       117 
     | 
    
         
            -
                        name = field 
     | 
| 
      
 134 
     | 
    
         
            +
                      relatedset['field-definition'].rfm_force_array.each do |field|
         
     | 
| 
      
 135 
     | 
    
         
            +
                        name = field['name'].to_s.gsub(Regexp.new(table + '::'), '')
         
     | 
| 
       118 
136 
     | 
    
         
             
                        fields[name] = Rfm::Metadata::Field.new(field)
         
     | 
| 
       119 
137 
     | 
    
         
             
                      end
         
     | 
| 
       120 
138 
     | 
    
         | 
| 
       121 
139 
     | 
    
         
             
                      @portal_meta[table] = fields
         
     | 
| 
       122 
140 
     | 
    
         
             
                    end
         
     | 
| 
      
 141 
     | 
    
         
            +
                    (layout.portal_meta = @portal_meta) if layout and layout.portal_meta_no_load.blank?
         
     | 
| 
       123 
142 
     | 
    
         
             
                  end
         
     | 
| 
       124 
143 
     | 
    
         | 
| 
       125 
144 
     | 
    
         
             
                  def convert_date_time_format(fm_format)
         
     | 
    
        data/lib/rfm/server.rb
    CHANGED
    
    | 
         @@ -107,13 +107,13 @@ module Rfm 
     | 
|
| 
       107 
107 
     | 
    
         
             
              # * *name* is the name of this database
         
     | 
| 
       108 
108 
     | 
    
         
             
              # * *state* is a hash of all server options used to initialize this server
         
     | 
| 
       109 
109 
     | 
    
         
             
              class Server
         
     | 
| 
       110 
     | 
    
         
            -
             
     | 
| 
      
 110 
     | 
    
         
            +
              
         
     | 
| 
       111 
111 
     | 
    
         
             
                # To create a Server object, you typically need at least a host name:
         
     | 
| 
       112 
112 
     | 
    
         
             
                # 
         
     | 
| 
       113 
113 
     | 
    
         
             
                #   myServer = Rfm::Server.new({:host => 'my.host.com'})
         
     | 
| 
       114 
114 
     | 
    
         
             
                #
         
     | 
| 
       115 
     | 
    
         
            -
                # Several other options are supported 
     | 
| 
       116 
     | 
    
         
            -
                #
         
     | 
| 
      
 115 
     | 
    
         
            +
                # ===Several other options are supported
         
     | 
| 
      
 116 
     | 
    
         
            +
                # 
         
     | 
| 
       117 
117 
     | 
    
         
             
                # * *host* the hostname of the Web Publishing Engine (WPE) server (defaults to 'localhost')
         
     | 
| 
       118 
118 
     | 
    
         
             
                #
         
     | 
| 
       119 
119 
     | 
    
         
             
                # * *port* the port number the WPE is listening no (defaults to 80 unless *ssl* +true+ which sets it to 443)
         
     | 
| 
         @@ -138,13 +138,13 @@ module Rfm 
     | 
|
| 
       138 
138 
     | 
    
         
             
                #   ignores FileMaker's 401 error (no records found) and returns an empty record set instead; if you
         
     | 
| 
       139 
139 
     | 
    
         
             
                #   prefer a raised error when a find produces no errors, set this option to +true+
         
     | 
| 
       140 
140 
     | 
    
         
             
                #
         
     | 
| 
       141 
     | 
    
         
            -
                # ===SSL Options (SSL AND CERTIFICATE VERIFICATION ARE ON BY DEFAULT) 
     | 
| 
       142 
     | 
    
         
            -
                #
         
     | 
| 
      
 141 
     | 
    
         
            +
                # ===SSL Options (SSL AND CERTIFICATE VERIFICATION ARE ON BY DEFAULT)
         
     | 
| 
      
 142 
     | 
    
         
            +
                # 
         
     | 
| 
       143 
143 
     | 
    
         
             
                # * *ssl* +false+ if you want to turn SSL (HTTPS) off when connecting to connect to FileMaker (default is +true+)
         
     | 
| 
       144 
144 
     | 
    
         
             
                #
         
     | 
| 
       145 
     | 
    
         
            -
                # If you are using SSL and want to verify the certificate use the following options:
         
     | 
| 
      
 145 
     | 
    
         
            +
                # If you are using SSL and want to verify the certificate, use the following options:
         
     | 
| 
       146 
146 
     | 
    
         
             
                #
         
     | 
| 
       147 
     | 
    
         
            -
                # * *root_cert* + 
     | 
| 
      
 147 
     | 
    
         
            +
                # * *root_cert* +true+ is the default. If you do not want to verify your SSL session, set this to +false+. 
         
     | 
| 
       148 
148 
     | 
    
         
             
                #   You will want to turn this off if you are using a self signed certificate and do not have a certificate authority cert file.
         
     | 
| 
       149 
149 
     | 
    
         
             
                #   If you choose this option you will need to provide a cert *root_cert_name* and *root_cert_path* (if not in root directory).
         
     | 
| 
       150 
150 
     | 
    
         
             
                #
         
     | 
| 
         @@ -154,8 +154,8 @@ module Rfm 
     | 
|
| 
       154 
154 
     | 
    
         
             
                #
         
     | 
| 
       155 
155 
     | 
    
         
             
                # * *root_cert_path* path to cert file. (defaults to '/' if no path given)
         
     | 
| 
       156 
156 
     | 
    
         
             
                #
         
     | 
| 
       157 
     | 
    
         
            -
                # ===Configuration Examples 
     | 
| 
       158 
     | 
    
         
            -
                #
         
     | 
| 
      
 157 
     | 
    
         
            +
                # ===Configuration Examples    
         
     | 
| 
      
 158 
     | 
    
         
            +
                # 
         
     | 
| 
       159 
159 
     | 
    
         
             
                # Example to turn off SSL:
         
     | 
| 
       160 
160 
     | 
    
         
             
                # 
         
     | 
| 
       161 
161 
     | 
    
         
             
                #   myServer = Rfm::Server.new({
         
     | 
| 
         @@ -192,8 +192,9 @@ module Rfm 
     | 
|
| 
       192 
192 
     | 
    
         
             
                #            :root_cert_name => 'example.pem'
         
     | 
| 
       193 
193 
     | 
    
         
             
                #            :root_cert_path => '/usr/cert_file/'
         
     | 
| 
       194 
194 
     | 
    
         
             
                #            })
         
     | 
| 
       195 
     | 
    
         
            -
                
         
     | 
| 
       196 
195 
     | 
    
         
             
                def initialize(options)
         
     | 
| 
      
 196 
     | 
    
         
            +
                	raise Rfm::Error::RfmError.new(0, "New instance of Rfm::Server has no host name.") if options[:host].to_s == ''
         
     | 
| 
      
 197 
     | 
    
         
            +
                  
         
     | 
| 
       197 
198 
     | 
    
         
             
                  @state = {
         
     | 
| 
       198 
199 
     | 
    
         
             
                    :host => 'localhost',
         
     | 
| 
       199 
200 
     | 
    
         
             
                    :port => 80,
         
     | 
| 
         @@ -205,17 +206,18 @@ module Rfm 
     | 
|
| 
       205 
206 
     | 
    
         
             
                    :password => '',
         
     | 
| 
       206 
207 
     | 
    
         
             
                    :log_actions => false,
         
     | 
| 
       207 
208 
     | 
    
         
             
                    :log_responses => false,
         
     | 
| 
      
 209 
     | 
    
         
            +
                    :log_parser => false,
         
     | 
| 
       208 
210 
     | 
    
         
             
                    :warn_on_redirect => true,
         
     | 
| 
       209 
211 
     | 
    
         
             
                    :raise_on_401 => false,
         
     | 
| 
       210 
212 
     | 
    
         
             
                    :timeout => 60
         
     | 
| 
       211 
213 
     | 
    
         
             
                  }.merge(options)
         
     | 
| 
       212 
214 
     | 
    
         | 
| 
       213 
215 
     | 
    
         
             
                  @state.freeze
         
     | 
| 
       214 
     | 
    
         
            -
             
     | 
| 
      
 216 
     | 
    
         
            +
                      
         
     | 
| 
       215 
217 
     | 
    
         
             
                  @host_name = @state[:host]
         
     | 
| 
       216 
218 
     | 
    
         
             
                  @scheme = @state[:ssl] ? "https" : "http"
         
     | 
| 
       217 
219 
     | 
    
         
             
                  @port = @state[:ssl] && options[:port].nil? ? 443 : @state[:port]
         
     | 
| 
       218 
     | 
    
         
            -
             
     | 
| 
      
 220 
     | 
    
         
            +
                  
         
     | 
| 
       219 
221 
     | 
    
         
             
                  @db = Rfm::Factory::DbFactory.new(self)
         
     | 
| 
       220 
222 
     | 
    
         
             
                end
         
     | 
| 
       221 
223 
     | 
    
         | 
| 
         @@ -231,11 +233,13 @@ module Rfm 
     | 
|
| 
       231 
233 
     | 
    
         
             
                # get no error at this point if the database you access doesn't exist. Instead, you'll
         
     | 
| 
       232 
234 
     | 
    
         
             
                # receive an error when you actually try to perform some action on a layout from this
         
     | 
| 
       233 
235 
     | 
    
         
             
                # database.
         
     | 
| 
       234 
     | 
    
         
            -
             
     | 
| 
       235 
     | 
    
         
            -
             
     | 
| 
       236 
     | 
    
         
            -
             
     | 
| 
      
 236 
     | 
    
         
            +
            		#     def [](dbname, acnt=nil, pass=nil)
         
     | 
| 
      
 237 
     | 
    
         
            +
            		#       self.db[dbname, acnt, pass]
         
     | 
| 
      
 238 
     | 
    
         
            +
            		#     end
         
     | 
| 
      
 239 
     | 
    
         
            +
                def_delegator :db, :[]
         
     | 
| 
       237 
240 
     | 
    
         | 
| 
       238 
241 
     | 
    
         
             
                attr_reader :db, :host_name, :port, :scheme, :state
         
     | 
| 
      
 242 
     | 
    
         
            +
                alias_method :databases, :db
         
     | 
| 
       239 
243 
     | 
    
         | 
| 
       240 
244 
     | 
    
         
             
                # Performs a raw FileMaker action. You will generally not call this method directly, but it
         
     | 
| 
       241 
245 
     | 
    
         
             
                # is exposed in case you need to do something "under the hood."
         
     | 
| 
         @@ -272,12 +276,12 @@ module Rfm 
     | 
|
| 
       272 
276 
     | 
    
         
             
                def load_layout(layout)
         
     | 
| 
       273 
277 
     | 
    
         
             
                  post = {'-db' => layout.db.name, '-lay' => layout.name, '-view' => ''}
         
     | 
| 
       274 
278 
     | 
    
         
             
                  resp = http_fetch(@host_name, @port, "/fmi/xml/FMPXMLLAYOUT.xml", layout.db.account_name, layout.db.password, post)
         
     | 
| 
       275 
     | 
    
         
            -
                  remove_namespace(resp.body)
         
     | 
| 
      
 279 
     | 
    
         
            +
                  #remove_namespace(resp.body)
         
     | 
| 
       276 
280 
     | 
    
         
             
                end
         
     | 
| 
       277 
281 
     | 
    
         | 
| 
       278 
282 
     | 
    
         
             
                # Removes namespace from fmpxmllayout, so xpath will work
         
     | 
| 
       279 
283 
     | 
    
         
             
              	def remove_namespace(xml)
         
     | 
| 
       280 
     | 
    
         
            -
                  xml.gsub( 
     | 
| 
      
 284 
     | 
    
         
            +
                  xml.gsub(/xmlns=\"[^\"]*\"/, '')
         
     | 
| 
       281 
285 
     | 
    
         
             
                end
         
     | 
| 
       282 
286 
     | 
    
         | 
| 
       283 
287 
     | 
    
         
             
                private
         
     | 
| 
         @@ -0,0 +1,64 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Rfm
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
              module ComplexQuery # @private :nodoc:
         
     | 
| 
      
 4 
     | 
    
         
            +
            		# Methods for Rfm::Layout to build complex queries
         
     | 
| 
      
 5 
     | 
    
         
            +
            		# Perform RFM find using complex boolean logic (multiple value options for a single field)
         
     | 
| 
      
 6 
     | 
    
         
            +
            		# Mimics creation of multiple find requests for "or" logic
         
     | 
| 
      
 7 
     | 
    
         
            +
            		# Use: rlayout_object.query({'fieldOne'=>['val1','val2','val3'], 'fieldTwo'=>'someValue', ...})
         
     | 
| 
      
 8 
     | 
    
         
            +
            		def query(hash_or_recid, options = {})
         
     | 
| 
      
 9 
     | 
    
         
            +
            		  if hash_or_recid.kind_of? Hash
         
     | 
| 
      
 10 
     | 
    
         
            +
            		    get_records('-findquery', assemble_query(hash_or_recid), options)
         
     | 
| 
      
 11 
     | 
    
         
            +
            		  else
         
     | 
| 
      
 12 
     | 
    
         
            +
            		    get_records('-find', {'-recid' => hash_or_recid.to_s}, options)
         
     | 
| 
      
 13 
     | 
    
         
            +
            		  end
         
     | 
| 
      
 14 
     | 
    
         
            +
            		end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            		# Build ruby params to send to -query action via RFM
         
     | 
| 
      
 17 
     | 
    
         
            +
            		def assemble_query(query_hash)
         
     | 
| 
      
 18 
     | 
    
         
            +
            			key_values, query_map = build_key_values(query_hash)
         
     | 
| 
      
 19 
     | 
    
         
            +
            			key_values.merge("-query"=>query_translate(array_mix(query_map)))
         
     | 
| 
      
 20 
     | 
    
         
            +
            		end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            		# Build key-value definitions and query map  '-q1...'
         
     | 
| 
      
 23 
     | 
    
         
            +
            		def build_key_values(qh)
         
     | 
| 
      
 24 
     | 
    
         
            +
            			key_values = {}
         
     | 
| 
      
 25 
     | 
    
         
            +
            			query_map = []
         
     | 
| 
      
 26 
     | 
    
         
            +
            			counter = 0
         
     | 
| 
      
 27 
     | 
    
         
            +
            			qh.each_with_index do |ha,i|
         
     | 
| 
      
 28 
     | 
    
         
            +
            				ha[1] = ha[1].to_a
         
     | 
| 
      
 29 
     | 
    
         
            +
            				query_tag = []
         
     | 
| 
      
 30 
     | 
    
         
            +
            				ha[1].each do |v|
         
     | 
| 
      
 31 
     | 
    
         
            +
            					key_values["-q#{counter}"] = ha[0]
         
     | 
| 
      
 32 
     | 
    
         
            +
            					key_values["-q#{counter}.value"] = v
         
     | 
| 
      
 33 
     | 
    
         
            +
            					query_tag << "q#{counter}"
         
     | 
| 
      
 34 
     | 
    
         
            +
            					counter += 1
         
     | 
| 
      
 35 
     | 
    
         
            +
            				end
         
     | 
| 
      
 36 
     | 
    
         
            +
            				query_map << query_tag
         
     | 
| 
      
 37 
     | 
    
         
            +
            			end
         
     | 
| 
      
 38 
     | 
    
         
            +
            			return key_values, query_map
         
     | 
| 
      
 39 
     | 
    
         
            +
            		end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
            		# Build query request logic for FMP requests  '-query...'
         
     | 
| 
      
 42 
     | 
    
         
            +
            		def array_mix(ary, line=[], rslt=[])
         
     | 
| 
      
 43 
     | 
    
         
            +
            			ary[0].to_a.each_with_index do |v,i|
         
     | 
| 
      
 44 
     | 
    
         
            +
            				array_mix(ary[1,ary.size], (line + [v]), rslt)
         
     | 
| 
      
 45 
     | 
    
         
            +
            				rslt << (line + [v]) if ary.size == 1
         
     | 
| 
      
 46 
     | 
    
         
            +
            			end
         
     | 
| 
      
 47 
     | 
    
         
            +
            			return rslt
         
     | 
| 
      
 48 
     | 
    
         
            +
            		end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            		# Translate query request logic to string
         
     | 
| 
      
 51 
     | 
    
         
            +
            		def query_translate(mixed_ary)
         
     | 
| 
      
 52 
     | 
    
         
            +
            			rslt = ""
         
     | 
| 
      
 53 
     | 
    
         
            +
            			sub = mixed_ary.collect {|a| "(#{a.join(',')})"}
         
     | 
| 
      
 54 
     | 
    
         
            +
            			sub.join(";")
         
     | 
| 
      
 55 
     | 
    
         
            +
            		end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
            	end # ComplexQuery
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
            	#   class Layout
         
     | 
| 
      
 60 
     | 
    
         
            +
            	#   	require 'rfm/layout'
         
     | 
| 
      
 61 
     | 
    
         
            +
            	#     include ComplexQuery
         
     | 
| 
      
 62 
     | 
    
         
            +
            	#   end
         
     | 
| 
      
 63 
     | 
    
         
            +
              
         
     | 
| 
      
 64 
     | 
    
         
            +
            end # Rfm
         
     |