base.rb 5.4 KB
Newer Older
1 2 3 4 5
require 'active_resource/connection'

module ActiveResource
  class Base
    class << self
6
      attr_reader :site
J
Jeremy Kemper 已提交
7

8 9
      def site=(site)
        @site = site.is_a?(URI) ? site : URI.parse(site)
10 11
        @connection = nil
        @site
12 13 14 15 16 17 18 19 20 21 22 23 24 25
      end

      def connection(refresh = false)
        @connection = Connection.new(site) if refresh || @connection.nil?
        @connection
      end
      
      def element_name
        self.to_s.underscore
      end

      def collection_name
        element_name.pluralize
      end
26 27 28 29

      def prefix(options={})
        default = site.path
        default << '/' unless default[-1..-1] == '/'
30
        self.prefix = default
31 32 33
        prefix(options)
      end
      
34
      def prefix=(value = '/')
35 36 37 38
        prefix_call = value.gsub(/:\w+/) { |s| "\#{options[#{s}]}" }
        method_decl = %(def self.prefix(options={}) "#{prefix_call}" end)
        eval method_decl
      end
39
      alias_method :set_prefix, :prefix=
40
      
41
      def element_name=(value)
42 43 44
        class << self ; attr_reader :element_name ; end
        @element_name = value
      end
45
      alias_method :set_element_name, :element_name=
46
      
47
      def collection_name=(value)
48 49 50
        class << self ; attr_reader :collection_name ; end
        @collection_name = value
      end
51
      alias_method :set_collection_name, :collection_name=
52 53 54 55 56 57 58 59

      def element_path(id, options = {})
        "#{prefix(options)}#{collection_name}/#{id}.xml"
      end
      
      def collection_path(options = {})
        "#{prefix(options)}#{collection_name}.xml"
      end
60
      
61
      def primary_key
62
        self.primary_key = 'id'
63 64
      end
      
65
      def primary_key=(value)
66 67
        class << self ; attr_reader :primary_key ; end
        @primary_key = value
68
      end
69
      alias_method :set_primary_key, :primary_key=
70
      
71 72
      # Person.find(1) # => GET /people/1.xml
      # StreetAddress.find(1, :person_id => 1) # => GET /people/1/street_addresses/1.xml
73
      def find(*arguments)
74 75
        scope   = arguments.slice!(0)
        options = arguments.slice!(0) || {}
76 77

        case scope
78 79 80
          when :all   then find_every(options)
          when :first then find_every(options).first
          else             find_single(scope, options)
81 82
        end
      end
83 84 85 86 87 88 89 90 91 92 93

      private
        # { :people => { :person => [ person1, person2 ] } }
        def find_every(options)
          connection.get(collection_path(options)).values.first.values.first.collect { |element| new(element, options) }
        end
        
        # { :person => person1 }
        def find_single(scope, options)
          new(connection.get(element_path(scope, options)).values.first, options)
        end
94 95 96
    end

    attr_accessor :attributes
97
    attr_accessor :prefix_options
98
    
99
    def initialize(attributes = {}, prefix_options = {})
100 101
      @attributes = {}
      self.load attributes
102
      @prefix_options = prefix_options
103
    end
104

105
    def new?
106 107 108
      id.nil?
    end

109
    def id
110
      attributes[self.class.primary_key]
111 112 113
    end
    
    def id=(id)
114
      attributes[self.class.primary_key] = id
115 116 117
    end
    
    def save
118
      new? ? create : update
119 120 121
    end

    def destroy
122
      connection.delete(self.class.element_path(id, prefix_options))
123
    end
124

125 126 127
    def to_xml
      attributes.to_xml(:root => self.class.element_name)
    end
128 129 130

    # Reloads the attributes of this object from the remote web service.
    def reload
131 132 133 134 135 136
      self.load self.class.find(id, @prefix_options).attributes
    end

    # Manually load attributes from a hash. Recursively loads collections of
    # resources.
    def load(attributes)
J
Jeremy Kemper 已提交
137
      raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
      attributes.each do |key, value|
        @attributes[key.to_s] =
          case value
            when Array
              resource = find_or_create_resource_for_collection(key)
              value.map { |attrs| resource.new(attrs) }
            when Hash
              resource = find_or_create_resource_for(key)
              resource.new(value)
            when ActiveResource::Base
              value.class.new(value.attributes)
            else
              value.dup rescue value
          end
      end
153 154 155
      self
    end

156 157 158 159 160 161
    protected
      def connection(refresh = false)
        self.class.connection(refresh)
      end
    
      def update
162
        connection.put(self.class.element_path(id, prefix_options), to_xml)
163
      end
164 165

      def create
166
        returning connection.post(self.class.collection_path(prefix_options), to_xml) do |resp|
167 168 169 170
          self.id = resp['Location'][/\/([^\/]*?)(\.\w+)?$/, 1]
        end
      end

171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
    private
      def find_or_create_resource_for_collection(name)
        find_or_create_resource_for(name.to_s.singularize)
      end

      def find_or_create_resource_for(name)
        resource_name = name.to_s.camelize
        resource_name.constantize
      rescue NameError
        resource = self.class.const_set(resource_name, Class.new(ActiveResource::Base))
        resource.prefix = self.class.prefix
        resource.site = self.class.site
        resource
      end

186 187 188 189 190 191 192
      def method_missing(method_symbol, *arguments)
        method_name = method_symbol.to_s
        
        case method_name.last
          when "="
            attributes[method_name.first(-1)] = arguments.first
          when "?"
193
            attributes[method_name.first(-1)] == true
194
          else
195
            attributes.has_key?(method_name) ? attributes[method_name] : super
196 197 198
        end
      end
  end
J
Jeremy Kemper 已提交
199
end