File: filters.rb

package info (click to toggle)
ruby-openid 2.1.8debian-6
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 1,676 kB
  • sloc: ruby: 16,506; xml: 219; sh: 24; makefile: 2
file content (205 lines) | stat: -rw-r--r-- 6,814 bytes parent folder | download | duplicates (8)
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# This file contains functions and classes used for extracting
# endpoint information out of a Yadis XRD file using the REXML
# XML parser.

#
module OpenID
  module Yadis
    class BasicServiceEndpoint
      attr_reader :type_uris, :yadis_url, :uri, :service_element

      # Generic endpoint object that contains parsed service
      # information, as well as a reference to the service element
      # from which it was generated. If there is more than one
      # xrd:Type or xrd:URI in the xrd:Service, this object represents
      # just one of those pairs.
      #
      # This object can be used as a filter, because it implements
      # fromBasicServiceEndpoint.
      #
      # The simplest kind of filter you can write implements
      # fromBasicServiceEndpoint, which takes one of these objects.
      def initialize(yadis_url, type_uris, uri, service_element)
        @type_uris = type_uris
        @yadis_url = yadis_url
        @uri = uri
        @service_element = service_element
      end

      # Query this endpoint to see if it has any of the given type
      # URIs. This is useful for implementing other endpoint classes
      # that e.g. need to check for the presence of multiple
      # versions of a single protocol.
      def match_types(type_uris)
        return @type_uris & type_uris
      end

      # Trivial transform from a basic endpoint to itself. This
      # method exists to allow BasicServiceEndpoint to be used as a
      # filter.
      #
      # If you are subclassing this object, re-implement this function.
      def self.from_basic_service_endpoint(endpoint)
        return endpoint
      end

      # A hack to make both this class and its instances respond to
      # this message since Ruby doesn't support static methods.
      def from_basic_service_endpoint(endpoint)
        return self.class.from_basic_service_endpoint(endpoint)
      end

    end

    # Take a list of basic filters and makes a filter that
    # transforms the basic filter into a top-level filter. This is
    # mostly useful for the implementation of make_filter, which
    # should only be needed for special cases or internal use by
    # this library.
    #
    # This object is useful for creating simple filters for services
    # that use one URI and are specified by one Type (we expect most
    # Types will fit this paradigm).
    #
    # Creates a BasicServiceEndpoint object and apply the filter
    # functions to it until one of them returns a value.
    class TransformFilterMaker
      attr_reader :filter_procs

      # Initialize the filter maker's state
      #
      # filter_functions are the endpoint transformer
      # Procs to apply to the basic endpoint. These are called in
      # turn until one of them does not return nil, and the result
      # of that transformer is returned.
      def initialize(filter_procs)
        @filter_procs = filter_procs
      end

      # Returns an array of endpoint objects produced by the
      # filter procs.
      def get_service_endpoints(yadis_url, service_element)
        endpoints = []

        # Do an expansion of the service element by xrd:Type and
        # xrd:URI
        Yadis::expand_service(service_element).each { |type_uris, uri, _|
          # Create a basic endpoint object to represent this
          # yadis_url, Service, Type, URI combination
          endpoint = BasicServiceEndpoint.new(
                yadis_url, type_uris, uri, service_element)

          e = apply_filters(endpoint)
          if !e.nil?
            endpoints << e
          end
        }
        return endpoints
      end

      def apply_filters(endpoint)
        # Apply filter procs to an endpoint until one of them returns
        # non-nil.
        @filter_procs.each { |filter_proc|
          e = filter_proc.call(endpoint)
          if !e.nil?
            # Once one of the filters has returned an endpoint, do not
            # apply any more.
            return e
          end
        }

        return nil
      end
    end

    class CompoundFilter
      attr_reader :subfilters

      # Create a new filter that applies a set of filters to an
      # endpoint and collects their results.
      def initialize(subfilters)
        @subfilters = subfilters
      end

      # Generate all endpoint objects for all of the subfilters of
      # this filter and return their concatenation.
      def get_service_endpoints(yadis_url, service_element)
        endpoints = []
        @subfilters.each { |subfilter|
          endpoints += subfilter.get_service_endpoints(yadis_url, service_element)
        }
        return endpoints
      end
    end

    # Exception raised when something is not able to be turned into a
    # filter
    @@filter_type_error = TypeError.new(
      'Expected a filter, an endpoint, a callable or a list of any of these.')

    # Convert a filter-convertable thing into a filter
    #
    # parts should be a filter, an endpoint, a callable, or a list of
    # any of these.
    def self.make_filter(parts)
      # Convert the parts into a list, and pass to mk_compound_filter
      if parts.nil?
        parts = [BasicServiceEndpoint]
      end

      if parts.is_a?(Array)
        return mk_compound_filter(parts)
      else
        return mk_compound_filter([parts])
      end
    end

    # Create a filter out of a list of filter-like things
    #
    # Used by make_filter
    #
    # parts should be a list of things that can be passed to make_filter
    def self.mk_compound_filter(parts)

      if !parts.respond_to?('each')
        raise TypeError, "#{parts.inspect} is not iterable"
      end

      # Separate into a list of callables and a list of filter objects
      transformers = []
      filters = []
      parts.each { |subfilter|
        if !subfilter.is_a?(Array)
          # If it's not an iterable
          if subfilter.respond_to?('get_service_endpoints')
            # It's a full filter
            filters << subfilter
          elsif subfilter.respond_to?('from_basic_service_endpoint')
            # It's an endpoint object, so put its endpoint conversion
            # attribute into the list of endpoint transformers
            transformers << subfilter.method('from_basic_service_endpoint')
          elsif subfilter.respond_to?('call')
            # It's a proc, so add it to the list of endpoint
            # transformers
            transformers << subfilter
          else
            raise @@filter_type_error
          end
        else
          filters << mk_compound_filter(subfilter)
        end
      }

      if transformers.length > 0
        filters << TransformFilterMaker.new(transformers)
      end

      if filters.length == 1
        return filters[0]
      else
        return CompoundFilter.new(filters)
      end
    end
  end
end