File: resource.rb

package info (click to toggle)
ruby-sawyer 0.9.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 280 kB
  • sloc: ruby: 1,224; sh: 17; makefile: 4
file content (160 lines) | stat: -rw-r--r-- 4,166 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
require 'forwardable'

module Sawyer
  class Resource
    SPECIAL_METHODS = Set.new(%w(agent rels fields))
    attr_reader :_agent, :_rels, :_fields
    attr_reader :attrs
    include Enumerable
    extend Forwardable

    # Initializes a Resource with the given data.
    #
    # agent - The Sawyer::Agent that made the API request.
    # data  - Hash of key/value properties.
    def initialize(agent, data = {})
      @_agent  = agent
      data, links = agent.parse_links(data)
      @_rels = Relation.from_links(agent, links)
      @_fields = Set.new
      @_metaclass = (class << self; self; end)
      @attrs = {}
      data.each do |key, value|
        @_fields << key
        @attrs[key.to_sym] = process_value(value)
      end
      @_metaclass.send(:attr_accessor, *data.keys)
    end

    # Processes an individual value of this resource.  Hashes get exploded
    # into another Resource, and Arrays get their values processed too.
    #
    # value - An Object value of a Resource's data.
    #
    # Returns an Object to set as the value of a Resource key.
    def process_value(value)
      case value
      when Hash  then self.class.new(@_agent, value)
      when Array then value.map { |v| process_value(v) }
      else value
      end
    end

    # Checks to see if the given key is in this resource.
    #
    # key - A Symbol key.
    #
    # Returns true if the key exists, or false.
    def key?(key)
      @_fields.include? key
    end

    # Allow fields to be retrieved via Hash notation
    #
    # method - key name
    #
    # Returns the value from attrs if exists
    def [](method)
      send(method.to_sym)
    rescue NoMethodError
      nil
    end

    # Allow fields to be set via Hash notation
    #
    # method - key name
    # value - value to set for the attr key
    #
    # Returns - value
    def []=(method, value)
      send("#{method}=", value)
    rescue NoMethodError
      nil
    end

    def_delegators :attrs, :dig, :fetch

    ATTR_SETTER    = '='.freeze
    ATTR_PREDICATE = '?'.freeze

    # Provides access to a resource's attributes.
    def method_missing(method, *args)
      attr_name, suffix = method.to_s.scan(/([a-z0-9\_]+)(\?|\=)?$/i).first
      if suffix == ATTR_SETTER
        @_metaclass.send(:attr_accessor, attr_name)
        @_fields << attr_name.to_sym
        send(method, args.first)
      elsif attr_name && @_fields.include?(attr_name.to_sym)
        value = @attrs[attr_name.to_sym]
        case suffix
        when nil
          @_metaclass.send(:attr_accessor, attr_name)
          value
        when ATTR_PREDICATE then !!value
        end
      elsif suffix.nil? && SPECIAL_METHODS.include?(attr_name)
        instance_variable_get "@_#{attr_name}"
      elsif attr_name && !@_fields.include?(attr_name.to_sym)
        nil
      else
        super
      end
    end

    # Wire up accessor methods to pull from attrs
    def self.attr_accessor(*attrs)
      attrs.each do |attribute|
        class_eval do
          define_method attribute do
            @attrs[attribute.to_sym]
          end

          define_method "#{attribute}=" do |value|
            @attrs[attribute.to_sym] = value
          end

          define_method "#{attribute}?" do
            !!@attrs[attribute.to_sym]
          end
        end
      end
    end

    def inspect
      to_attrs.respond_to?(:pretty_inspect) ? to_attrs.pretty_inspect : to_attrs.inspect
    end

    def each(&block)
      @attrs.each(&block)
    end

    # private
    def to_yaml_properties
      [:@attrs, :@_fields, :@_rels]
    end

    def to_attrs
      hash = self.attrs.clone
      hash.keys.each do |k|
        if hash[k].is_a?(Sawyer::Resource)
          hash[k] = hash[k].to_attrs
        elsif hash[k].is_a?(Array) && hash[k].all?{|el| el.is_a?(Sawyer::Resource)}
          hash[k] = hash[k].collect{|el| el.to_attrs}
        end
      end
      hash
    end

    alias to_hash to_attrs
    alias to_h to_attrs

    def marshal_dump
      [@attrs, @_fields, @_rels]
    end

    def marshal_load(dumped)
      @attrs, @_fields, @_rels = *dumped.shift(3)
      @_metaclass = (class << self; self; end)
    end
  end
end