diaspora_federation 0.0.1 → 0.0.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 35ffb28db27478325eaf2f2d61290b125f2e20e2
4
- data.tar.gz: cfaca21b0289d178044895bbb91326b51520fed7
3
+ metadata.gz: 3b3836cd6caf68ffba2a101eb90c98932a7cfc1e
4
+ data.tar.gz: 87fcf932a0f7c2b9151af3bd12c72a1acaf6c935
5
5
  SHA512:
6
- metadata.gz: 0ef15d9756b8174572d54d926388e8dffbeabea94f342ceb62ef536f5c52f6c9003e904b208c22a277c4c4906c1723ea7bf7f82181dae3726a9b5aeaa51b77a0
7
- data.tar.gz: 1e77ba30e7745f64927b58ab0c110fa9467f0b8981e539ce24500754d5a61f548ac63e362c84ad5cfb7b6bbbca92ac065b1aa1e4a9debf1599150dbad27a1f5b
6
+ metadata.gz: 94783e21640104926dac495d63976435dee0e47f99cf0d6e9d71a07615cd645e2aecee0a3cb73f98f46fc974e4a6835d32999906824610a607fd598557cc0848
7
+ data.tar.gz: 13a49b3cb39df94c54c67289da76fb41f2c8a9db3ec44ad865ce88a4439cd9487cdf3f796a341c85293437b3a330fd40b2510f48908d8b422eb927a59ca857e0
data/README.md CHANGED
@@ -1,18 +1,68 @@
1
- # diaspora* federation rails engine
1
+ # diaspora* federation library
2
2
 
3
- #### A rails engine that adds the diaspora* federation protocol to a rails app
3
+ **A library that provides functionalities needed for the diaspora* federation protocol**
4
4
 
5
5
  [![Build Status](https://travis-ci.org/SuperTux88/diaspora_federation.svg?branch=master)](https://travis-ci.org/SuperTux88/diaspora_federation)
6
6
  [![Code Climate](https://codeclimate.com/github/SuperTux88/diaspora_federation/badges/gpa.svg)](https://codeclimate.com/github/SuperTux88/diaspora_federation)
7
7
  [![Test Coverage](https://codeclimate.com/github/SuperTux88/diaspora_federation/badges/coverage.svg)](https://codeclimate.com/github/SuperTux88/diaspora_federation/coverage)
8
8
  [![Dependency Status](https://gemnasium.com/SuperTux88/diaspora_federation.svg)](https://gemnasium.com/SuperTux88/diaspora_federation)
9
9
  [![Inline docs](https://inch-ci.org/github/SuperTux88/diaspora_federation.svg?branch=master)](https://inch-ci.org/github/SuperTux88/diaspora_federation)
10
+ [![Gem Version](https://badge.fury.io/rb/diaspora_federation.svg)](https://badge.fury.io/rb/diaspora_federation)
10
11
 
11
- [Documentation](http://www.rubydoc.info/github/SuperTux88/diaspora_federation/master) |
12
- [Project site](https://diasporafoundation.org) |
13
- [Wiki](https://wiki.diasporafoundation.org) |
12
+ [Documentation](http://www.rubydoc.info/gems/diaspora_federation/) |
14
13
  [Bugtracker](https://github.com/SuperTux88/diaspora_federation/issues)
15
14
 
15
+ ## Library
16
+
17
+ The ```diaspora_federation``` gem provides the functionality for de-/serialization and de-/encryption of Entities
18
+ in the protocols used for communication among the various installations of Diaspora*
19
+
20
+ ## Rails Engine
21
+
22
+ The ```diaspora_federation-rails``` gem is a rails engine that adds the diaspora* federation protocol to a rails app.
23
+
24
+ ### Usage
25
+
26
+ Add the gem to your ```Gemfile```:
27
+
28
+ ```ruby
29
+ gem "diaspora_federation-rails"
30
+ ```
31
+
32
+ Mount the routes in your ```config/routes.rb```:
33
+
34
+ ```ruby
35
+ mount DiasporaFederation::Engine => "/"
36
+ ```
37
+
38
+ Configure the engine in ```config/initializers/diaspora_federation.rb```:
39
+
40
+ ```ruby
41
+ DiasporaFederation.configure do |config|
42
+ # the pod url
43
+ config.server_uri = AppConfig.pod_uri
44
+
45
+ # the class to be used for a person
46
+ config.person_class = Person
47
+ end
48
+ ```
49
+
50
+ ## Development
51
+
52
+ **!!! This gem is currently under heavy development, so every release can contain breaking changes !!!**
53
+
54
+ If you want to help, please contact me, help is welcome.
55
+
56
+ After the first stable release, this repo will be moved to the [diaspora organization](https://github.com/diaspora/).
57
+
58
+ ## Diaspora
59
+
60
+ A privacy-aware, distributed, open source social network
61
+
62
+ Links:
63
+ [Project site](https://diasporafoundation.org) |
64
+ [Wiki](https://wiki.diasporafoundation.org)
65
+
16
66
  ## License
17
67
 
18
68
  This gem is published under the terms of the "GNU Affero General Public License". See the LICENSE file for the exact wording.
@@ -1,113 +1,77 @@
1
- require "diaspora_federation/engine"
2
1
  require "diaspora_federation/logging"
3
2
 
3
+ require "diaspora_federation/callbacks"
4
+ require "diaspora_federation/properties_dsl"
5
+ require "diaspora_federation/entity"
6
+
4
7
  require "diaspora_federation/web_finger"
5
8
 
6
- ##
7
- # diaspora* federation rails engine
9
+ # diaspora* federation library
8
10
  module DiasporaFederation
9
11
  extend Logging
10
12
 
13
+ @callbacks = Callbacks.new %i(
14
+ person_webfinger_fetch
15
+ person_hcard_fetch
16
+ )
17
+
11
18
  class << self
12
- ##
19
+ # {Callbacks} instance with defined callbacks
20
+ # @see Callbacks#on
21
+ # @see Callbacks#trigger
22
+ #
23
+ attr_reader :callbacks
24
+
13
25
  # the pod url
14
26
  #
15
- # Example:
27
+ # @example with uri
16
28
  # config.server_uri = URI("http://localhost:3000/")
17
- # or
29
+ # @example with configured pod_uri
18
30
  # config.server_uri = AppConfig.pod_uri
19
31
  attr_accessor :server_uri
20
32
 
21
- ##
22
- # the class to use as +Person+
23
- #
24
- # Example:
25
- # config.person_class = Person.to_s
26
- #
27
- # This class must have the following methods:
33
+ # configure the federation library
28
34
  #
29
- # *find_local_by_diaspora_handle*
30
- # This should return a +Person+, which is on this pod and the account is not closed.
31
- #
32
- # *find_local_by_guid*
33
- # This should return a +Person+, which is on this pod and the account is not closed.
34
- #
35
- # *webfinger_hash*
36
- # This should return a +Hash+ with the following information:
37
- # {
38
- # acct_uri: "acct:user@server.example",
39
- # alias_url: "https://server.example/people/0123456789abcdef",
40
- # hcard_url: "https://server.example/hcard/users/0123456789abcdef",
41
- # seed_url: "https://server.example/",
42
- # profile_url: "https://server.example/u/user",
43
- # atom_url: "https://server.example/public/user.atom",
44
- # salmon_url: "https://server.example/receive/users/0123456789abcdef",
45
- # guid: "0123456789abcdef",
46
- # pubkey: "-----BEGIN PUBLIC KEY-----\nABCDEF==\n-----END PUBLIC KEY-----"
47
- # }
35
+ # @example
36
+ # DiasporaFederation.configure do |config|
37
+ # config.server_uri = URI("http://localhost:3000/")
48
38
  #
49
- # *hcard_profile_hash*
50
- # This should return a +Hash+ with the following information:
51
- # {
52
- # guid: "0123456789abcdef",
53
- # nickname: "user",
54
- # full_name: "User Name",
55
- # url: "https://server.example/",
56
- # photo_large_url: "https://server.example/uploads/f.jpg",
57
- # photo_medium_url: "https://server.example/uploads/m.jpg",
58
- # photo_small_url: "https://server.example/uploads/s.jpg",
59
- # pubkey: "-----BEGIN PUBLIC KEY-----\nABCDEF==\n-----END PUBLIC KEY-----",
60
- # searchable: true,
61
- # first_name: "User",
62
- # last_name: "Name"
63
- # }
64
- attr_accessor :person_class
65
- def person_class
66
- const_get(@person_class)
39
+ # config.define_callbacks do
40
+ # # callback configuration
41
+ # end
42
+ # end
43
+ def configure
44
+ yield self
67
45
  end
68
46
 
69
- ##
70
- # configure the federation engine
47
+ # define the callbacks
71
48
  #
72
- # DiasporaFederation.configure do |config|
73
- # config.server_uri = "http://localhost:3000/"
49
+ # @example
50
+ # config.define_callbacks do
51
+ # on :some_event do |arg1|
52
+ # # do something
53
+ # end
74
54
  # end
75
- def configure
76
- yield self
55
+ #
56
+ # @param [Proc] block the callbacks to define
57
+ def define_callbacks(&block)
58
+ @callbacks.instance_eval(&block)
77
59
  end
78
60
 
79
- ##
80
61
  # validates if the engine is configured correctly
81
62
  #
82
63
  # called from after_initialize
83
64
  # @raise [ConfigurationError] if the configuration is incomplete or invalid
84
65
  def validate_config
85
- configuration_error "missing server_uri" unless @server_uri.respond_to? :host
86
- validate_class(@person_class, "person_class", %i(
87
- find_local_by_diaspora_handle
88
- find_local_by_guid
89
- webfinger_hash
90
- hcard_profile_hash
91
- ))
92
- logger.info "successfully configured the federation engine"
66
+ configuration_error "Missing server_uri" unless @server_uri.respond_to? :host
67
+ unless @callbacks.definition_complete?
68
+ configuration_error "Missing handlers for #{@callbacks.missing_handlers.join(', ')}"
69
+ end
70
+ logger.info "successfully configured the federation library"
93
71
  end
94
72
 
95
73
  private
96
74
 
97
- def validate_class(klass, name, methods)
98
- configuration_error "missing #{name}" unless klass
99
- entity = const_get(klass)
100
-
101
- return logger.warn "table for #{entity} doesn't exist, skip validation" unless entity.table_exists?
102
-
103
- methods.each {|method| entity_respond_to?(entity, name, method) }
104
- end
105
-
106
- def entity_respond_to?(entity, name, method)
107
- valid = entity.respond_to?(method) || entity.column_names.include?(method.to_s) || entity.method_defined?(method)
108
- configuration_error "the configured class #{entity} for #{name} doesn't respond to #{method}" unless valid
109
- end
110
-
111
75
  def configuration_error(message)
112
76
  logger.fatal("diaspora federation configuration error: #{message}")
113
77
  raise ConfigurationError, message
@@ -0,0 +1,62 @@
1
+ module DiasporaFederation
2
+ # Callbacks are used to communicate with the application. They are called to
3
+ # fetch data and after data is received.
4
+ class Callbacks
5
+ # Initializes a new Callbacks object with the event-keys that need to be defined.
6
+ #
7
+ # @example
8
+ # Callbacks.new %i(
9
+ # some_event
10
+ # another_event
11
+ # )
12
+ #
13
+ # @param [Hash] events event keys
14
+ def initialize(events)
15
+ @events = events
16
+ @handlers = {}
17
+ end
18
+
19
+ # defines a callback
20
+ #
21
+ # @example
22
+ # callbacks.on :some_event do |arg1|
23
+ # # do something
24
+ # end
25
+ #
26
+ # @param [Symbol] event the event key
27
+ # @param [Proc] callback the callback block
28
+ # @raise [ArgumentError] if the event key is undefined or has already a handler
29
+ def on(event, &callback)
30
+ raise ArgumentError, "Undefined event #{event}" unless @events.include? event
31
+ raise ArgumentError, "Already defined event #{event}" if @handlers.has_key? event
32
+
33
+ @handlers[event] = callback
34
+ end
35
+
36
+ # triggers a callback
37
+ #
38
+ # @example
39
+ # callbacks.trigger :some_event, "foo"
40
+ #
41
+ # @param [Symbol] event the event key
42
+ # @return [Object] the return-value of the callback
43
+ # @raise [ArgumentError] if the event key is undefined
44
+ def trigger(event, *args)
45
+ raise ArgumentError, "Undefined event #{event}" unless @events.include? event
46
+
47
+ @handlers[event].call(*args)
48
+ end
49
+
50
+ # checks if all callbacks are defined
51
+ # @return [Boolean]
52
+ def definition_complete?
53
+ missing_handlers.empty?
54
+ end
55
+
56
+ # Returns all undefined callbacks
57
+ # @return [Hash] callback keys
58
+ def missing_handlers
59
+ @events - @handlers.keys
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,146 @@
1
+ module DiasporaFederation
2
+ # +Entity+ is the base class for all other objects used to encapsulate data
3
+ # for federation messages in the Diaspora* network.
4
+ # Entity fields are specified using a simple {PropertiesDSL DSL} as part of
5
+ # the class definition.
6
+ #
7
+ # Any entity also provides the means to serialize itself and all nested
8
+ # entities to XML (for deserialization from XML to +Entity+ instances, see
9
+ # {XmlPayload}).
10
+ #
11
+ # @abstract Subclass and specify properties to implement various entities.
12
+ #
13
+ # @example Entity subclass definition
14
+ # class MyEntity < Entity
15
+ # property :prop
16
+ # property :optional, default: false
17
+ # property :dynamic_default, default: -> { Time.now }
18
+ # entity :nested, NestedEntity
19
+ # entity :multiple, [OtherEntity]
20
+ # end
21
+ #
22
+ # @example Entity instantiation
23
+ # nentity = NestedEntity.new
24
+ # oe1 = OtherEntity.new
25
+ # oe2 = OtherEntity.new
26
+ #
27
+ # entity = MyEntity.new(prop: 'some property',
28
+ # nested: nentity,
29
+ # multiple: [oe1, oe2])
30
+ #
31
+ # @note Entity properties can only be set during initialization, after that the
32
+ # entity instance becomes frozen and must not be modified anymore. Instances
33
+ # are intended to be immutable data containers, only.
34
+ class Entity
35
+ extend PropertiesDSL
36
+
37
+ # Initializes the Entity with the given attribute hash and freezes the created
38
+ # instance it returns.
39
+ #
40
+ # @note Attributes not defined as part of the class definition ({PropertiesDSL#property},
41
+ # {PropertiesDSL#entity}) get discarded silently.
42
+ #
43
+ # @param [Hash] data
44
+ # @return [Entity] new instance
45
+ def initialize(data)
46
+ raise ArgumentError, "expected a Hash" unless data.is_a?(Hash)
47
+ missing_props = self.class.missing_props(data)
48
+ unless missing_props.empty?
49
+ raise ArgumentError, "missing required properties: #{missing_props.join(', ')}"
50
+ end
51
+
52
+ self.class.default_values.merge(data).each do |k, v|
53
+ instance_variable_set("@#{k}", v) if setable?(k, v)
54
+ end
55
+ freeze
56
+ end
57
+
58
+ # Returns a Hash representing this Entity (attributes => values)
59
+ # @return [Hash] entity data (mostly equal to the hash used for initialization).
60
+ def to_h
61
+ self.class.class_prop_names.each_with_object({}) do |prop, hash|
62
+ hash[prop] = send(prop)
63
+ end
64
+ end
65
+
66
+ # Returns the XML representation for this entity constructed out of
67
+ # {http://www.rubydoc.info/gems/nokogiri/Nokogiri/XML/Element Nokogiri::XML::Element}s
68
+ #
69
+ # @see Nokogiri::XML::Node.to_xml
70
+ # @see XmlPayload.pack
71
+ #
72
+ # @return [Nokogiri::XML::Element] root element containing properties as child elements
73
+ def to_xml
74
+ entity_xml
75
+ end
76
+
77
+ # some of this is from Rails "Inflector.demodulize" and "Inflector.undersore"
78
+ def self.entity_name
79
+ word = name.dup
80
+ i = word.rindex("::")
81
+ word = word[(i + 2)..-1] if i
82
+
83
+ word.gsub!("::", "/")
84
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
85
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
86
+ word.tr!("-", "_")
87
+ word.downcase!
88
+ word
89
+ end
90
+
91
+ private
92
+
93
+ def setable?(name, val)
94
+ prop_def = self.class.class_props.find {|p| p[:name] == name }
95
+ return false if prop_def.nil? # property undefined
96
+
97
+ setable_string?(prop_def, val) || setable_nested?(prop_def, val) || setable_multi?(prop_def, val)
98
+ end
99
+
100
+ def setable_string?(definition, val)
101
+ (definition[:type] == String && val.respond_to?(:to_s))
102
+ end
103
+
104
+ def setable_nested?(definition, val)
105
+ t = definition[:type]
106
+ (t.is_a?(Class) && t.ancestors.include?(Entity) && val.is_a?(Entity))
107
+ end
108
+
109
+ def setable_multi?(definition, val)
110
+ t = definition[:type]
111
+ (t.instance_of?(Array) &&
112
+ val.instance_of?(Array) &&
113
+ val.all? {|v| v.instance_of?(t.first) })
114
+ end
115
+
116
+ # Serialize the Entity into XML elements
117
+ # @return [Nokogiri::XML::Element] root node
118
+ def entity_xml
119
+ doc = Nokogiri::XML::DocumentFragment.new(Nokogiri::XML::Document.new)
120
+ root_element = Nokogiri::XML::Element.new(self.class.entity_name, doc)
121
+
122
+ self.class.class_props.each do |prop_def|
123
+ name = prop_def[:name]
124
+ type = prop_def[:type]
125
+ if type == String
126
+ root_element << simple_node(doc, name)
127
+ else
128
+ # call #to_xml for each item and append to root
129
+ [*send(name)].compact.each do |item|
130
+ root_element << item.to_xml
131
+ end
132
+ end
133
+ end
134
+
135
+ root_element
136
+ end
137
+
138
+ # create simple node, fill it with text and append to root
139
+ def simple_node(doc, name)
140
+ node = Nokogiri::XML::Element.new(name.to_s, doc)
141
+ data = send(name).to_s
142
+ node.content = data unless data.empty?
143
+ node
144
+ end
145
+ end
146
+ end