diaspora_federation 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,12 +1,10 @@
1
1
  module DiasporaFederation
2
- ##
3
- # logging module for the diaspora federation engine
2
+ # logging module for the diaspora federation
4
3
  #
5
4
  # it uses the logging-gem if available
6
5
  module Logging
7
6
  private
8
7
 
9
- ##
10
8
  # get the logger for this class
11
9
  #
12
10
  # use the logging-gem if available, else use a default logger
@@ -17,7 +15,8 @@ module DiasporaFederation
17
15
 
18
16
  # fallback logger
19
17
  @logger = Logger.new(STDOUT)
20
- @logger.level = Logger.const_get(Rails.configuration.log_level.to_s.upcase)
18
+ loglevel = defined?(::Rails) ? ::Rails.configuration.log_level.to_s.upcase : "INFO"
19
+ @logger.level = Logger.const_get(loglevel)
21
20
  @logger
22
21
  end
23
22
  end
@@ -0,0 +1,106 @@
1
+ module DiasporaFederation
2
+ # Provides a simple DSL for specifying {Entity} properties during class
3
+ # definition.
4
+ #
5
+ # @example
6
+ # property :prop
7
+ # property :optional, default: false
8
+ # property :dynamic_default, default: -> { Time.now }
9
+ # entity :nested, NestedEntity
10
+ # entity :multiple, [OtherEntity]
11
+ module PropertiesDSL
12
+ # @return [Array<Hash>] hash of declared entity properties
13
+ def class_props
14
+ @class_props ||= []
15
+ end
16
+
17
+ # Define a generic (string-type) property
18
+ # @param [Symbol] name property name
19
+ # @param [Hash] opts further options
20
+ # @option opts [Object, #call] :default a default value, making the
21
+ # property optional
22
+ def property(name, opts={})
23
+ define_property name, String, opts
24
+ end
25
+
26
+ # Define a property that should contain another Entity or an array of
27
+ # other Entities
28
+ # @param [Symbol] name property name
29
+ # @param [Entity, Array<Entity>] type Entity subclass or
30
+ # Array with exactly one Entity subclass constant inside
31
+ # @param [Hash] opts further options
32
+ # @option opts [Object, #call] :default a default value, making the
33
+ # property optional
34
+ def entity(name, type, opts={})
35
+ raise InvalidType unless type_valid?(type)
36
+
37
+ define_property name, type, opts
38
+ end
39
+
40
+ # Return array of missing required property names
41
+ # @return [Array<Symbol>] missing required property names
42
+ def missing_props(args)
43
+ class_prop_names - default_props.keys - args.keys
44
+ end
45
+
46
+ # Return a new hash of default values, with dynamic values
47
+ # resolved on each call
48
+ # @return [Hash] default values
49
+ def default_values
50
+ default_props.each_with_object({}) { |(name, prop), hash|
51
+ hash[name] = prop.respond_to?(:call) ? prop.call : prop
52
+ }
53
+ end
54
+
55
+ # Returns all nested Entities
56
+ # @return [Array<Hash>] nested properties
57
+ def nested_class_props
58
+ @nested_class_props ||= class_props.select {|p| p[:type] != String }
59
+ end
60
+
61
+ # Returns all property names
62
+ # @return [Array] property names
63
+ def class_prop_names
64
+ @class_prop_names ||= class_props.map {|p| p[:name] }
65
+ end
66
+
67
+ private
68
+
69
+ def define_property(name, type, opts={})
70
+ raise InvalidName unless name_valid?(name)
71
+
72
+ class_props << {name: name, type: type}
73
+ default_props[name] = opts[:default] if opts.has_key? :default
74
+
75
+ instance_eval { attr_reader name }
76
+ end
77
+
78
+ # checks if the name is a +Symbol+ or a +String+
79
+ # @param [String, Symbol] name the name to check
80
+ # @return [Boolean]
81
+ def name_valid?(name)
82
+ name.instance_of?(Symbol) || name.instance_of?(String)
83
+ end
84
+
85
+ # checks if the type extends {Entity}
86
+ # @param [Class] type the type to check
87
+ # @return [Boolean]
88
+ def type_valid?(type)
89
+ [type].flatten.all? { |type|
90
+ type.respond_to?(:ancestors) && type.ancestors.include?(Entity)
91
+ }
92
+ end
93
+
94
+ def default_props
95
+ @default_props ||= {}
96
+ end
97
+
98
+ # Raised, if the name is of an unexpected type
99
+ class InvalidName < RuntimeError
100
+ end
101
+
102
+ # Raised, if the type is of an unexpected type
103
+ class InvalidType < RuntimeError
104
+ end
105
+ end
106
+ end
@@ -1,5 +1,4 @@
1
1
  module DiasporaFederation
2
- ##
3
2
  # the gem version
4
- VERSION = "0.0.1"
3
+ VERSION = "0.0.2"
5
4
  end
@@ -1,5 +1,4 @@
1
1
  module DiasporaFederation
2
- ##
3
2
  # This module provides the namespace for the various classes implementing
4
3
  # WebFinger and other protocols used for metadata discovery on remote servers
5
4
  # in the Diaspora* network.
@@ -1,18 +1,14 @@
1
1
  module DiasporaFederation
2
2
  module WebFinger
3
- ##
4
3
  # Raised, if the XML structure is invalid
5
4
  class InvalidDocument < RuntimeError
6
5
  end
7
6
 
8
- ##
9
7
  # Raised, if something is wrong with the webfinger data
10
8
  #
11
9
  # * if the +webfinger_url+ is missing or malformed in {HostMeta.from_base_url} or {HostMeta.from_xml}
12
- # * if the +data+ given to {WebFinger.from_person} is an invalid type or doesn't contain all required entries
13
10
  # * if the parsed XML from {WebFinger.from_xml} is incomplete
14
- # * if the params passed to {HCard.from_profile} or {HCard.from_html}
15
- # are in some way malformed, invalid or incomplete.
11
+ # * if the html passed to {HCard.from_html} in some way is malformed, invalid or incomplete.
16
12
  class InvalidData < RuntimeError
17
13
  end
18
14
  end
@@ -1,6 +1,5 @@
1
1
  module DiasporaFederation
2
2
  module WebFinger
3
- ##
4
3
  # This class provides the means of generating an parsing account data to and
5
4
  # from the hCard format.
6
5
  # hCard is based on +RFC 2426+ (vCard) which got superseded by +RFC 6350+.
@@ -14,20 +13,20 @@ module DiasporaFederation
14
13
  # correctly nested according to the hCard standard and class names are
15
14
  # partially wrong. Also, apart from that, it's just ugly.
16
15
  #
17
- # @example Creating a hCard document from account data
18
- # hc = HCard.from_profile({
19
- # guid: "0123456789abcdef",
20
- # nickname: "user",
21
- # full_name: "User Name",
22
- # url: "https://server.example/",
23
- # photo_large_url: "https://server.example/uploads/l.jpg",
24
- # photo_medium_url: "https://server.example/uploads/m.jpg",
25
- # photo_small_url: "https://server.example/uploads/s.jpg",
26
- # pubkey: "-----BEGIN PUBLIC KEY-----\nABCDEF==\n-----END PUBLIC KEY-----",
27
- # searchable: true,
28
- # first_name: "User",
29
- # last_name: "Name"
30
- # })
16
+ # @example Creating a hCard document from a person hash
17
+ # hc = HCard.new(
18
+ # guid: "0123456789abcdef",
19
+ # nickname: "user",
20
+ # full_name: "User Name",
21
+ # seed_url: "https://server.example/",
22
+ # photo_large_url: "https://server.example/uploads/l.jpg",
23
+ # photo_medium_url: "https://server.example/uploads/m.jpg",
24
+ # photo_small_url: "https://server.example/uploads/s.jpg",
25
+ # serialized_public_key: "-----BEGIN PUBLIC KEY-----\nABCDEF==\n-----END PUBLIC KEY-----",
26
+ # searchable: true,
27
+ # first_name: "User",
28
+ # last_name: "Name"
29
+ # )
31
30
  # html_string = hc.to_html
32
31
  #
33
32
  # @example Create a HCard instance from an hCard document
@@ -40,65 +39,77 @@ module DiasporaFederation
40
39
  # @see http://microformats.org/wiki/h-card "h-card" (draft)
41
40
  # @see http://www.ietf.org/rfc/rfc2426.txt "vCard MIME Directory Profile" (obsolete)
42
41
  # @see http://www.ietf.org/rfc/rfc6350.txt "vCard Format Specification"
43
- class HCard
44
- private_class_method :new
45
-
46
- # This is just the guid. When a user creates an account on a pod, the pod
47
- # MUST assign them a guid - a random hexadecimal string of at least 8
48
- # hexadecimal digits.
49
- # @return [String] guid
50
- attr_reader :guid
51
-
52
- # the first part of the diaspora handle
53
- # @return [String] nickname
54
- attr_reader :nickname
55
-
56
- # @return [String] display name of the user
57
- attr_reader :full_name
58
-
59
- # @deprecated should be changed to the profile url. The pod url is in
60
- # the WebFinger (see {WebFinger#seed_url}, will affect older Diaspora*
61
- # installations).
42
+ class HCard < Entity
43
+ # @!attribute [r] guid
44
+ # This is just the guid. When a user creates an account on a pod, the pod
45
+ # MUST assign them a guid - a random hexadecimal string of at least 8
46
+ # hexadecimal digits.
47
+ # @return [String] guid
48
+ property :guid
49
+
50
+ # @!attribute [r] nickname
51
+ # the first part of the diaspora handle
52
+ # @return [String] nickname
53
+ property :nickname
54
+
55
+ # @!attribute [r] full_name
56
+ # @return [String] display name of the user
57
+ property :full_name
58
+
59
+ # @!attribute [r] url
60
+ # @deprecated should be changed to the profile url. The pod url is in
61
+ # the WebFinger (see {WebFinger#seed_url}, will affect older Diaspora*
62
+ # installations).
62
63
  #
63
- # @return [String] link to the pod
64
- attr_reader :url
65
-
66
- # When a user is created on the pod, the pod MUST generate a pgp keypair
67
- # for them. This key is used for signing messages. The format is a
68
- # DER-encoded PKCS#1 key beginning with the text
69
- # "-----BEGIN PUBLIC KEY-----" and ending with "-----END PUBLIC KEY-----".
70
- # @return [String] public key
71
- attr_reader :pubkey
72
-
73
- # @return [String] url to the big avatar (300x300)
74
- attr_reader :photo_large_url
75
- # @return [String] url to the medium avatar (100x100)
76
- attr_reader :photo_medium_url
77
- # @return [String] url to the small avatar (50x50)
78
- attr_reader :photo_small_url
79
-
80
- # @deprecated We decided to only use one name field, these should be removed
81
- # in later iterations (will affect older Diaspora* installations).
64
+ # @return [String] link to the pod
65
+ property :url
66
+
67
+ # @!attribute [r] public_key
68
+ # When a user is created on the pod, the pod MUST generate a pgp keypair
69
+ # for them. This key is used for signing messages. The format is a
70
+ # DER-encoded PKCS#1 key beginning with the text
71
+ # "-----BEGIN PUBLIC KEY-----" and ending with "-----END PUBLIC KEY-----".
82
72
  #
83
- # @see #full_name
84
- # @return [String] first name
85
- attr_reader :first_name
73
+ # @note the public key is new in the hcard and is optional now.
74
+ #
75
+ # @return [String] public key
76
+ property :public_key, default: nil
77
+
78
+ # @!attribute [r] photo_large_url
79
+ # @return [String] url to the big avatar (300x300)
80
+ property :photo_large_url
81
+ # @!attribute [r] photo_medium_url
82
+ # @return [String] url to the medium avatar (100x100)
83
+ property :photo_medium_url
84
+ # @!attribute [r] photo_small_url
85
+ # @return [String] url to the small avatar (50x50)
86
+ property :photo_small_url
87
+
88
+ # @!attribute [r] first_name
89
+ # @deprecated We decided to only use one name field, these should be removed
90
+ # in later iterations (will affect older Diaspora* installations).
91
+ #
92
+ # @see #full_name
93
+ # @return [String] first name
94
+ property :first_name
86
95
 
87
- # @deprecated We decided to only use one name field, these should be removed
88
- # in later iterations (will affect older Diaspora* installations).
96
+ # @!attribute [r] last_name
97
+ # @deprecated We decided to only use one name field, these should be removed
98
+ # in later iterations (will affect older Diaspora* installations).
89
99
  #
90
- # @see #full_name
91
- # @return [String] last name
92
- attr_reader :last_name
93
-
94
- # @deprecated As this is a simple property, consider move to WebFinger instead
95
- # of HCard. vCard has no comparable field for this information, but
96
- # Webfinger may declare arbitrary properties (will affect older Diaspora*
97
- # installations).
100
+ # @see #full_name
101
+ # @return [String] last name
102
+ property :last_name
103
+
104
+ # @!attribute [r] searchable
105
+ # @deprecated As this is a simple property, consider move to WebFinger instead
106
+ # of HCard. vCard has no comparable field for this information, but
107
+ # Webfinger may declare arbitrary properties (will affect older Diaspora*
108
+ # installations).
98
109
  #
99
- # flag if a user is searchable by name
100
- # @return [Boolean] searchable flag
101
- attr_reader :searchable
110
+ # flag if a user is searchable by name
111
+ # @return [Boolean] searchable flag
112
+ property :searchable
102
113
 
103
114
  # CSS selectors for finding all the hCard fields
104
115
  SELECTORS = {
@@ -128,7 +139,7 @@ module DiasporaFederation
128
139
  add_simple_property(content, :searchable, "searchable", @searchable)
129
140
 
130
141
  add_property(content, :key) do |html|
131
- html.pre(@pubkey.to_s, class: "key")
142
+ html.pre(@public_key.to_s, class: "key")
132
143
  end
133
144
 
134
145
  # TODO: remove me! ###################
@@ -145,33 +156,6 @@ module DiasporaFederation
145
156
  builder.doc.to_xhtml(indent: 2, indent_text: " ")
146
157
  end
147
158
 
148
- # Creates a new HCard instance from the given Hash containing profile data
149
- # @param [Hash] data account data
150
- # @return [HCard] HCard instance
151
- # @raise [InvalidData] if the account data Hash is invalid or incomplete
152
- def self.from_profile(data)
153
- raise InvalidData unless account_data_complete?(data)
154
-
155
- hc = allocate
156
- hc.instance_eval {
157
- @guid = data[:guid]
158
- @nickname = data[:nickname]
159
- @full_name = data[:full_name]
160
- @url = data[:url]
161
- @photo_large_url = data[:photo_large_url]
162
- @photo_medium_url = data[:photo_medium_url]
163
- @photo_small_url = data[:photo_small_url]
164
- @pubkey = data[:pubkey]
165
- @searchable = data[:searchable]
166
-
167
- # TODO: remove me! ###################
168
- @first_name = data[:first_name]
169
- @last_name = data[:last_name]
170
- #######################################
171
- }
172
- hc
173
- end
174
-
175
159
  # Creates a new HCard instance from the given HTML string.
176
160
  # @param html_string [String] HTML string
177
161
  # @return [HCard] HCard instance
@@ -179,24 +163,24 @@ module DiasporaFederation
179
163
  def self.from_html(html_string)
180
164
  doc = parse_html_and_validate(html_string)
181
165
 
182
- hc = allocate
183
- hc.instance_eval {
184
- @guid = content_from_doc(doc, :uid)
185
- @nickname = content_from_doc(doc, :nickname)
186
- @full_name = content_from_doc(doc, :fn)
187
- @url = element_from_doc(doc, :url)["href"]
188
- @photo_large_url = photo_from_doc(doc, :photo)
189
- @photo_medium_url = photo_from_doc(doc, :photo_medium)
190
- @photo_small_url = photo_from_doc(doc, :photo_small)
191
- @pubkey = content_from_doc(doc, :key) unless element_from_doc(doc, :key).nil?
192
- @searchable = content_from_doc(doc, :searchable) == "true"
166
+ data = {
167
+ guid: content_from_doc(doc, :uid),
168
+ nickname: content_from_doc(doc, :nickname),
169
+ full_name: content_from_doc(doc, :fn),
170
+ url: element_from_doc(doc, :url)["href"],
171
+ photo_large_url: photo_from_doc(doc, :photo),
172
+ photo_medium_url: photo_from_doc(doc, :photo_medium),
173
+ photo_small_url: photo_from_doc(doc, :photo_small),
174
+ searchable: (content_from_doc(doc, :searchable) == "true"),
193
175
 
194
176
  # TODO: change me! ###################
195
- @first_name = content_from_doc(doc, :given_name)
196
- @last_name = content_from_doc(doc, :family_name)
177
+ first_name: content_from_doc(doc, :given_name),
178
+ last_name: content_from_doc(doc, :family_name)
197
179
  #######################################
198
180
  }
199
- hc
181
+ # TODO: public key is new and can be missing
182
+ data[:public_key] = content_from_doc(doc, :key) unless element_from_doc(doc, :key).nil?
183
+ new(data)
200
184
  end
201
185
 
202
186
  private
@@ -271,19 +255,6 @@ module DiasporaFederation
271
255
  end
272
256
  end
273
257
 
274
- # Checks the given account data Hash for correct type and completeness.
275
- # @param [Hash] data account data
276
- # @return [Boolean] validation result
277
- def self.account_data_complete?(data)
278
- data.instance_of?(Hash) &&
279
- %i(
280
- guid nickname full_name url
281
- photo_large_url photo_medium_url photo_small_url
282
- pubkey searchable first_name last_name
283
- ).all? {|k| data.key? k }
284
- end
285
- private_class_method :account_data_complete?
286
-
287
258
  # Make sure some of the most important elements are present in the parsed
288
259
  # HTML document.
289
260
  # @param [LibXML::XML::Document] doc HTML document
@@ -303,17 +274,20 @@ module DiasporaFederation
303
274
  end
304
275
  private_class_method :parse_html_and_validate
305
276
 
306
- def element_from_doc(doc, selector)
277
+ def self.element_from_doc(doc, selector)
307
278
  doc.at_css(SELECTORS[selector])
308
279
  end
280
+ private_class_method :element_from_doc
309
281
 
310
- def content_from_doc(doc, content_selector)
282
+ def self.content_from_doc(doc, content_selector)
311
283
  element_from_doc(doc, content_selector).content
312
284
  end
285
+ private_class_method :content_from_doc
313
286
 
314
- def photo_from_doc(doc, photo_selector)
287
+ def self.photo_from_doc(doc, photo_selector)
315
288
  element_from_doc(doc, photo_selector)["src"]
316
289
  end
290
+ private_class_method :photo_from_doc
317
291
  end
318
292
  end
319
293
  end