federails 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a3463b1a877ca8224ade011016236c1fdbcbe41e31712b609347f41592d30531
4
- data.tar.gz: 16ec7975b26bc5fbec1e243a4653c27d8b0239c7dbda42544150c9e5680d65f5
3
+ metadata.gz: 9e6d475999161590317e4a038b44829fdae2a44ccb007b03f24c240fccfa0771
4
+ data.tar.gz: b2266e7c4e9ac8c68177e2b17fc2e74afab52fc0e93607943716e06b39c10f6d
5
5
  SHA512:
6
- metadata.gz: b54850967223bb1be7aadd54547f757a9e5fbe6ccc8d84249a4a2b340a494b275905c25e862c493b2d2b177a7cc5e12abf2d6e44c4f84b4bec76796a7b9cba5f
7
- data.tar.gz: cc00b05153d4434eacc6409a4422575d5e851e07c332001a72fc818e5f64d1bf6f2cd35f6eaa5f4025b3150fe9009a370665a044d44f84a60399690186a8f5e8
6
+ metadata.gz: 14c482129bac20d5dfdaa2fa498070ab215819a0fe6af6499e268d631f40a7c15ee3341e999e6b0bfee5fd997b7a41f879c35537aeebbfbbdfc6182289d5edf4
7
+ data.tar.gz: 0c6fe55888c7b31f93958e258ad4b137aa305269056b2b512af2a137e2a15ac6de36a0a83f7fbec436c6657fca3c4ad9c18c08bb46a93e48601ef226c9a0011c
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Experiments Labs / Experimentations
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -23,206 +23,11 @@ The general direction is to be able to:
23
23
  - implement some or all the parts of the RFC labelled with **SHOULD** and **SHOULD NOT**
24
24
  - maybe implement the parts of the RFC labelled with **MAY**
25
25
 
26
- ## Installation
26
+ ## Documentation
27
27
 
28
- Add this line to your application's Gemfile:
29
-
30
- ```ruby
31
- gem "federails"
32
- ```
33
-
34
- And then execute:
35
-
36
- ```bash
37
- $ bundle
38
- ```
39
-
40
- ### Configuration
41
-
42
- Generate configuration files:
43
-
44
- ```sh
45
- bundle exec rails generate federails:install
46
- ```
47
-
48
- It creates an initializer and a configuration file:
49
- - `config/initializers/federails.rb`
50
- - `config/federails.yml`
51
-
52
- By default, Federails is configured using `config_from` method, that loads the appropriate YAML file, but you may want
53
- to configure it differently:
54
-
55
- ```rb
56
- # config/initializers/federails.rb
57
- Federails.configure do |config|
58
- config.host = 'localhost'
59
- # ...
60
- end
61
- ```
62
-
63
- For now, refer to [the source code](lib/federails/configuration.rb) for the full list of options.
64
-
65
- ### Routes
66
-
67
- Mount the engine on `/`: routes to `/.well-known/*` and `/nodeinfo/*` must be at the root of the site.
68
- Federails routes are then available under the configured path (`routes_path`):
69
-
70
- ```rb
71
- # config/routes.rb
72
- mount Federails::Engine => '/'
73
- ```
74
-
75
- With `routes_path = 'federation'`, routes will be:
76
-
77
- ```txt
78
- /.well-known/webfinger(.:format)
79
- /.well-known/host-meta(.:format)
80
- /.well-known/nodeinfo(.:format)
81
- /nodeinfo/2.0(.:format)
82
- /federation/actors/:id/followers(.:format)
83
- /federation/actors/:id/following(.:format)
84
- /federation/actors/:actor_id/outbox(.:format)
85
- /federation/actors/:actor_id/inbox(.:format)
86
- /federation/actors/:actor_id/activities/:id(.:format)
87
- /federation/actors/:actor_id/followings/:id(.:format)
88
- /federation/actors/:actor_id/notes/:id(.:format)
89
- /federation/actors/:id(.:format)
90
- ...
91
- ```
92
-
93
- Some routes can be disabled in configuration if you don't want to expose particular features:
94
-
95
- ```rb
96
- Federails.configure do |config|
97
- # Disable routing for .well-known and nodeinfo
98
- config.enable_discovery = false
99
-
100
- # Disable web client UI routes
101
- config.client_routes_path = nil
102
- end
103
- ```
104
-
105
- #### Remote following
106
-
107
- By default, remote follow requests (where you press a follow button on another server and get redirected home to complete the follow)
108
- will use the built-in client paths. If you're not using the client, or want to provide your own user interface, you can set the path like this, assuming that `new_follow_url` is a valid route in your app. A `uri` query parameter template will be automatically appended, you don't need to specify that.
109
-
110
- ```rb
111
- Federails.configure do |config|
112
- config.remote_follow_url_method = :new_follow_url
113
- end
114
- ```
115
-
116
- ### Migrations
117
-
118
- Copy the migrations:
119
-
120
- ```sh
121
- bundle exec rails federails:install:migrations
122
- ```
123
-
124
- ### User model
125
-
126
- In the ActivityPub world, we refer to _actors_ to represent the thing that publishes or subscribe to _other actors_.
127
-
128
- Federails provides a concern to include in your "user" model or whatever will publish data:
129
-
130
- ```rb
131
- # app/models/user.rb
132
-
133
- class User < ApplicationRecord
134
- # Include the concern here:
135
- include Federails::ActorEntity
136
-
137
- # Configure field names
138
- acts_as_federails_actor username_field: :username, name_field: :name, profile_url_method: :user_url
139
- end
140
- ```
141
-
142
- This concern automatically create a `Federails::Actor` after a user creation, as well as the `actor` reference. When adding it to
143
- an existing model with existing data, you will need to generate the corresponding actors yourself in a migration.
144
-
145
- Usage example:
146
-
147
- ```rb
148
- actor = User.find(1).federails_actor
149
-
150
- actor.inbox
151
- actor.outbox
152
- actor.followers
153
- actor.following
154
- #...
155
- ```
156
-
157
- ### Using the Federails client
158
-
159
- Federails comes with a client, enabled by default, that provides basic views to display and interact with Federails data,
160
- accessible on `/app` by default (changeable with the configuration option `client_routes_path`)
161
-
162
- If it's a good starting point, it might be disabled once you made your own integration by setting `client_routes_path`
163
- to a `nil` value.
164
-
165
- If you want to override the client's views, copy them in your application:
166
-
167
- ```sh
168
- rails generate federails:copy_client_views
169
- ```
170
-
171
- ## Common questions
172
-
173
- - **I override the base controller and the links breaks in my layout**
174
-
175
- Use `main_app.<url_helper>` for links to your application; `federails.<federails_url_helper>` for links to the Federails client.
176
- - **I specified a custom layout and the links breaks in it**
177
-
178
- Use `main_app.<url_helper>` for links to your application; `federails.<federails_url_helper>` for links to the Federails client.
179
- - **I specified a custom layout and my helpers are not available**
180
-
181
- You will have better results if you specify a `base_controller` from your application as Federails base controller is isolated from the main app and does not have access to its helpers.
182
-
183
- ## Contributing
184
-
185
- Contributions are welcome, may it be issues, ideas, code or whatever you want to share. Please note:
186
-
187
- - This project is _fast forward_ only: we don't do merge commits
188
- - We adhere to [semantic versioning](). Please update the changelog in your commits
189
- - We try to adhere to [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) principles
190
- - We _may_ rename your commits before merging them
191
- - We _may_ split your commits before merging them
192
-
193
- To contribute:
194
-
195
- 1. Fork this repository
196
- 2. Create small commits
197
- 3. Ideally create small pull requests. Don't hesitate to open them early so we all can follow how it's going
198
- 4. Get congratulated
199
-
200
- ### Tooling
201
-
202
- #### RSpec
203
-
204
- RSpec is the test suite. Start it with
205
-
206
- ```sh
207
- bundle exec rspec
208
- ```
209
-
210
- #### Rubocop
211
-
212
- Rubocop is a linter. Start it with
213
-
214
- ```sh
215
- bundle exec rubocop
216
- ```
217
-
218
- #### FactoryBot
219
-
220
- FactoryBot is a factory generator used in tests and development.
221
- A rake task checks the replayability of the factories and traits:
222
-
223
- ```sh
224
- bundle exec app:factory_bot:lint
225
- ```
28
+ - [Usage](docs/usage.md)
29
+ - [Common questions](docs/faq.md)
30
+ - [Contributing](docs/contributing.md)
226
31
 
227
32
  ## License
228
33
 
@@ -0,0 +1,30 @@
1
+ module Federails
2
+ module Server
3
+ # Controller to render ActivityPub representation of entities configured with Federails::DataEntity
4
+ class PublishedController < Federails::ServerController
5
+ def show
6
+ @publishable = type_scope.find_by!(url_param => params[:id])
7
+ authorize @publishable, policy_class: Federails::Server::PublishablePolicy
8
+ end
9
+
10
+ private
11
+
12
+ def publishable_config
13
+ return @publishable_config if instance_variable_defined? :@publishable_config
14
+
15
+ _, @publishable_config = Federails.configuration.data_types.find { |_, v| v[:route_path_segment].to_s == params[:publishable_type] }
16
+ raise ActiveRecord::RecordNotFound, "Invalid #{params[:publishable_type]} type" unless @publishable_config
17
+
18
+ @publishable_config
19
+ end
20
+
21
+ def url_param
22
+ publishable_config[:url_param]
23
+ end
24
+
25
+ def type_scope
26
+ publishable_config[:class].all
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,51 +1,45 @@
1
1
  module Federails
2
+ # Concern to include in models that acts as actors.
3
+ #
4
+ # Actors can be anything; they authors content _via_ their _outbox_ and receive content in their _inbox_.
5
+ # Actors can follow and be followed by each other
6
+ #
7
+ # By default, when an entry is created on models using this concern, a _local_ `Federails::Actor` will be created.
8
+ #
9
+ # See also:
10
+ # - https://www.w3.org/TR/activitypub/#actor-objects
11
+ #
12
+ # ## Usage
13
+ #
14
+ # Include the concern in an existing model:
15
+ #
16
+ # ```rb
17
+ # class User < ApplicationRecord
18
+ # include Federails::ActorEntity
19
+ # acts_as_federails_actor options
20
+ # end
21
+ # ```
2
22
  module ActorEntity
3
23
  extend ActiveSupport::Concern
4
24
 
5
- included do # rubocop:todo Metrics/BlockLength
6
- include ActiveSupport::Callbacks
7
- define_callbacks :followed
8
-
9
- # Define a method that will be called after the entity receives a follow request
10
- # @param method [Symbol] The name of the method to call, or a block that will be called directly
11
- # @example
12
- # after_followed :accept_follow
13
- def self.after_followed(method)
14
- set_callback :followed, :after, method
15
- end
16
-
17
- # Define a method that will be called after an activity has been received
18
- # @param activity_type [String] The activity action to handle, e.g. 'Create'. If you specify '*', the handler will be called for any activity type.
19
- # @param object_type [String] The object type to handle, e.g. 'Note'. If you specify '*', the handler will be called for any object type.
20
- # @param method [Symbol] The name of the class method to call. The method will receive the complete activity payload as a parameter.
21
- # @example
22
- # after_activity_received 'Create', 'Note', :create_note
23
- def self.after_activity_received(activity_type, object_type, method)
24
- Fediverse::Inbox.register_handler(activity_type, object_type, self, method)
25
- end
26
-
27
- has_one :federails_actor, class_name: 'Federails::Actor', as: :entity, dependent: :destroy
28
-
29
- after_create :create_federails_actor, if: lambda {
30
- raise("Entity not configured for #{self.class.name}. Did you use \"acts_as_federails_actor\"?") unless Federails.actor_entity? self
31
-
32
- Federails.actor_entity(self)[:auto_create_actors]
33
- }
34
-
25
+ # Class methods automatically included in the concern.
26
+ module ClassMethods
35
27
  # Configures the mapping between entity and actor
28
+ #
36
29
  # @param username_field [Symbol] The method or attribute name that returns the preferred username for ActivityPub
37
30
  # @param name_field [Symbol] The method or attribute name that returns the preferred name for ActivityPub
38
31
  # @param profile_url_method [Symbol] The route method name that will generate the profile URL for ActivityPub
39
32
  # @param actor_type [String] The ActivityStreams Actor type for this entity; defaults to 'Person'
40
33
  # @param user_count_method [Symbol] A class method to call to count active users. Leave unspecified to leave this
41
- # entity out of user counts. Method signature should accept a single parameter which will specify a date range
42
- # If parameter is nil, the total user count should be returned. If the parameter is specified, the number of users
43
- # active during the time period should be returned.
34
+ # entity out of user counts. Method signature should accept a single parameter which will specify a date range
35
+ # If parameter is nil, the total user count should be returned. If the parameter is specified, the number of users
36
+ # active during the time period should be returned.
44
37
  # @param auto_create_actors [Boolean] Whether to automatically create an actor when the entity is created
38
+ #
45
39
  # @example
46
40
  # acts_as_federails_actor username_field: :username, name_field: :display_name, profile_url_method: :url_for, actor_type: 'Person'
47
41
  # rubocop:disable Metrics/ParameterLists
48
- def self.acts_as_federails_actor(
42
+ def acts_as_federails_actor(
49
43
  name_field:,
50
44
  username_field:,
51
45
  profile_url_method: nil,
@@ -65,34 +59,72 @@ module Federails
65
59
  end
66
60
  # rubocop:enable Metrics/ParameterLists
67
61
 
68
- # Add custom data to actor responses.
69
- # Override in your own model to add extra data, which will be merged into the actor response
70
- # generated by Federails. You can include extra `@context` for activitypub extensions and it will
71
- # be merged with the main response context.
62
+ # Define a method that will be called after the entity receives a follow request
63
+ #
64
+ # @param method_name [Symbol] The name of the method to call, or a block that will be called directly
65
+ #
72
66
  # @example
73
- # def to_activitypub_object
74
- # {
75
- # "@context": {
76
- # toot: "http://joinmastodon.org/ns#",
77
- # attributionDomains: {
78
- # "@id": "toot:attributionDomains",
79
- # "@type": "@id"
80
- # }
81
- # },
82
- # attributionDomains: [
83
- # "example.com"
84
- # ]
85
- # }
86
- # end
87
- def to_activitypub_object
88
- {}
67
+ # after_followed :accept_follow
68
+ def after_followed(method_name)
69
+ set_callback :followed, :after, method_name
89
70
  end
90
71
 
91
- private
92
-
93
- def create_federails_actor
94
- Federails::Actor.create! entity: self
72
+ # Define a method that will be called after an activity has been received
73
+ #
74
+ # @param activity_type [String] The activity action to handle, e.g. 'Create'. If you specify '*', the handler will be called for any activity type.
75
+ # @param object_type [String] The object type to handle, e.g. 'Note'. If you specify '*', the handler will be called for any object type.
76
+ # @param method_name [Symbol] The name of the class method to call. The method will receive the complete activity payload as a parameter.
77
+ #
78
+ # @example
79
+ # after_activity_received 'Create', 'Note', :create_note
80
+ def after_activity_received(activity_type, object_type, method_name)
81
+ Fediverse::Inbox.register_handler(activity_type, object_type, self, method_name)
95
82
  end
96
83
  end
84
+
85
+ included do
86
+ include ActiveSupport::Callbacks
87
+
88
+ define_callbacks :followed
89
+
90
+ has_one :federails_actor, class_name: 'Federails::Actor', as: :entity, dependent: :destroy
91
+
92
+ after_create :create_federails_actor, if: lambda {
93
+ raise("Entity not configured for #{self.class.name}. Did you use \"acts_as_federails_actor\"?") unless Federails.actor_entity? self
94
+
95
+ Federails.actor_entity(self)[:auto_create_actors]
96
+ }
97
+ end
98
+
99
+ # Add custom data to actor responses.
100
+ #
101
+ # Override in your own model to add extra data, which will be merged into the actor response
102
+ # generated by Federails. You can include extra `@context` for activitypub extensions and it will
103
+ # be merged with the main response context.
104
+ #
105
+ # @example
106
+ # def to_activitypub_object
107
+ # {
108
+ # "@context": {
109
+ # toot: "http://joinmastodon.org/ns#",
110
+ # attributionDomains: {
111
+ # "@id": "toot:attributionDomains",
112
+ # "@type": "@id"
113
+ # }
114
+ # },
115
+ # attributionDomains: [
116
+ # "example.com"
117
+ # ]
118
+ # }
119
+ # end
120
+ def to_activitypub_object
121
+ {}
122
+ end
123
+
124
+ private
125
+
126
+ def create_federails_actor
127
+ Federails::Actor.create! entity: self
128
+ end
97
129
  end
98
130
  end
@@ -0,0 +1,178 @@
1
+ require 'fediverse/inbox'
2
+
3
+ module Federails
4
+ # Model concern to include in models for which data is pushed to the Fediverse and comes from the Fediverse.
5
+ #
6
+ # Once included, an activity will automatically be created upon
7
+ # - entity creation
8
+ # - entity updates
9
+ #
10
+ # Also, when properly configured, a handler is registered to transform incoming objects and create/update entities
11
+ # accordingly.
12
+ #
13
+ # ## Pre-requisites
14
+ #
15
+ # Model must have a `federated_url` attribute:
16
+ # ```rb
17
+ # add_column :posts, :federated_url, :string, null: true, default: nil
18
+ # ```
19
+ #
20
+ # ## Usage
21
+ #
22
+ # Include the concern in an existing model:
23
+ #
24
+ # ```rb
25
+ # class Post < ApplicationRecord
26
+ # include Federails::DataEntity
27
+ # acts_as_federails_data options
28
+ # end
29
+ # ```
30
+ module DataEntity
31
+ extend ActiveSupport::Concern
32
+
33
+ # Class methods automatically included in the concern.
34
+ module ClassMethods
35
+ # Configures the mapping between entity and Fediverse
36
+ #
37
+ # Model should have the following methods:
38
+ # - `to_activitypub_object`, returning a valid ActivityPub object
39
+ #
40
+ # @param actor_entity_method [Symbol] Method returning an object responding to 'federails_actor', for local content
41
+ # @param url_param [Symbol] Column name of the object ID that should be used in URLs. Defaults to +:id+
42
+ # @param route_path_segment [Symbol] Segment used in Federails routes to display the ActivityPub representation.
43
+ # Defaults to the pluralized, underscored class name
44
+ # @param handles [String] Type of ActivityPub object handled by this entity type
45
+ # @param with [Symbol] Self class method that will handle incoming objects. Defaults to +:handle_incoming_fediverse_data+
46
+ # @param filter_method [Symbol] Self class method that determines if an incoming object should be handled. Note
47
+ # that the first model for which this method returns true will be used. If left empty, the model CAN be selected,
48
+ # so define them if many models handle the same data type.
49
+ # @param should_federate_method [Symbol] method to determine if an object should be federated. If the method returns false,
50
+ # no create/update activities will happen, and object will not be accessible at federated_url. Defaults to a method
51
+ # that always returns true.
52
+ #
53
+ # @example
54
+ # acts_as_federails_data handles: 'Note', with: :note_handler, route_path_segment: :articles, actor_entity_method: :user
55
+ # rubocop:disable Metrics/ParameterLists
56
+ def acts_as_federails_data(
57
+ handles:,
58
+ with: :handle_incoming_fediverse_data,
59
+ route_path_segment: nil,
60
+ actor_entity_method: nil,
61
+ url_param: :id,
62
+ filter_method: nil,
63
+ should_federate_method: :default_should_federate?
64
+ )
65
+ route_path_segment ||= name.pluralize.underscore
66
+
67
+ Federails::Configuration.register_data_type self,
68
+ route_path_segment: route_path_segment,
69
+ actor_entity_method: actor_entity_method,
70
+ url_param: url_param,
71
+ handles: handles,
72
+ with: with,
73
+ filter_method: filter_method,
74
+ should_federate_method: should_federate_method
75
+
76
+ Fediverse::Inbox.register_handler 'Create', handles, self, with
77
+ Fediverse::Inbox.register_handler 'Update', handles, self, with
78
+ end
79
+ # rubocop:enable Metrics/ParameterLists
80
+
81
+ # Instantiates a new instance from an ActivityPub object
82
+ #
83
+ # @param activitypub_object [Hash]
84
+ #
85
+ # @return [self]
86
+ def new_from_activitypub_object(activitypub_object)
87
+ new from_activitypub_object(activitypub_object)
88
+ end
89
+
90
+ # Creates or updates entity based on the ActivityPub activity
91
+ #
92
+ # @param activity_hash_or_id [Hash, String] Dereferenced activity hash or ID
93
+ #
94
+ # @return [self]
95
+ def handle_incoming_fediverse_data(activity_hash_or_id)
96
+ activity = Fediverse::Request.dereference(activity_hash_or_id)
97
+ object = Fediverse::Request.dereference(activity['object'])
98
+
99
+ entity = Federails::Utils::Object.find_or_create!(object)
100
+
101
+ if activity['type'] == 'Update'
102
+ entity.assign_attributes from_activitypub_object(object)
103
+
104
+ # Use timestamps from attributes
105
+ entity.save! touch: false
106
+ end
107
+
108
+ entity
109
+ end
110
+ end
111
+
112
+ included do
113
+ belongs_to :federails_actor, class_name: 'Federails::Actor'
114
+
115
+ scope :local_federails_entities, -> { where federated_url: nil }
116
+ scope :distant_federails_entities, -> { where.not(federated_url: nil) }
117
+
118
+ before_validation :set_federails_actor
119
+ after_create :create_federails_activity
120
+ after_update :update_federails_activity
121
+ end
122
+
123
+ # Computed value for the federated URL
124
+ #
125
+ # @return [String]
126
+ def federated_url
127
+ return nil unless send(federails_data_configuration[:should_federate_method])
128
+ return attributes['federated_url'] if attributes['federated_url'].present?
129
+
130
+ path_segment = Federails.data_entity_configuration(self)[:route_path_segment]
131
+ url_param = Federails.data_entity_configuration(self)[:url_param]
132
+ Federails::Engine.routes.url_helpers.server_published_url(publishable_type: path_segment, id: send(url_param))
133
+ end
134
+
135
+ # Check whether the entity was created locally or comes from the Fediverse
136
+ #
137
+ # @return [Boolean]
138
+ def local_federails_entity?
139
+ attributes['federated_url'].blank?
140
+ end
141
+
142
+ def federails_data_configuration
143
+ Federails.data_entity_configuration(self)
144
+ end
145
+
146
+ private
147
+
148
+ def set_federails_actor
149
+ return federails_actor if federails_actor.present?
150
+
151
+ self.federails_actor = send(federails_data_configuration[:actor_entity_method])&.federails_actor if federails_data_configuration[:actor_entity_method]
152
+
153
+ raise 'Cannot determine actor from configuration' unless federails_actor
154
+ end
155
+
156
+ def create_federails_activity
157
+ ensure_federails_configuration!
158
+ return unless local_federails_entity? && send(federails_data_configuration[:should_federate_method])
159
+
160
+ Activity.create! actor: federails_actor, action: 'Create', entity: self
161
+ end
162
+
163
+ def update_federails_activity
164
+ ensure_federails_configuration!
165
+ return unless local_federails_entity? && send(federails_data_configuration[:should_federate_method])
166
+
167
+ Activity.create! actor: federails_actor, action: 'Update', entity: self
168
+ end
169
+
170
+ def ensure_federails_configuration!
171
+ raise("Entity not configured for #{self.class.name}. Did you use \"acts_as_federails_data\"?") unless Federails.data_entity? self
172
+ end
173
+
174
+ def default_should_federate?
175
+ true
176
+ end
177
+ end
178
+ end
@@ -1,4 +1,28 @@
1
1
  module Federails
2
+ # Model concern providing UUIDs as model parameter (instead of IDs).
3
+ #
4
+ #
5
+ # A _required_, `uuid` field is required on the model's table for this concern to work:
6
+ #
7
+ # ```rb
8
+ # # Example migration
9
+ # add_column :my_table, :uuid, :text, default: nil, index: { unique: true }
10
+ # ```
11
+ #
12
+ # Usage:
13
+ #
14
+ # ```rb
15
+ # class MyModel < ApplicationRecord
16
+ # include Federails::HasUuid
17
+ # end
18
+ #
19
+ # # And now:
20
+ # instance = MyModel.find_param 'aaaa_bbbb_cccc_dddd_....'
21
+ # instance.to_param
22
+ # # => 'aaaa_bbbb_cccc_dddd_....'
23
+ # ```
24
+ #
25
+ # It can be added on existing tables without data migration as the `uuid` accessor will generate the value when missing.
2
26
  module HasUuid
3
27
  extend ActiveSupport::Concern
4
28
 
@@ -11,12 +35,14 @@ module Federails
11
35
  end
12
36
  end
13
37
 
38
+ # @return [String] The UUID
14
39
  def to_param
15
40
  uuid
16
41
  end
17
42
 
18
- # Override UUID accessor to provide lazy initialization of UUIDs for old data
43
+ # @return [String]
19
44
  def uuid
45
+ # Override UUID accessor to provide lazy initialization of UUIDs for old data
20
46
  if self[:uuid].blank?
21
47
  generate_uuid
22
48
  save!
@@ -1,4 +1,13 @@
1
1
  module Federails
2
+ # Activities can be compared to a log of what happened in the Fediverse.
3
+ #
4
+ # Activities from local actors ends in the actors _outboxes_.
5
+ # Activities form distant actors comes from the actor's _inbox_.
6
+ # We try to only keep activities _from_ local actors, and external activities _targetting_ local actors.
7
+ #
8
+ # See also:
9
+ # - https://www.w3.org/TR/activitypub/#outbox
10
+ # - https://www.w3.org/TR/activitypub/#inbox
2
11
  class Activity < ApplicationRecord
3
12
  include Federails::HasUuid
4
13
 
@@ -15,6 +24,9 @@ module Federails
15
24
 
16
25
  after_create_commit :post_to_inboxes
17
26
 
27
+ # Determines the list of actors targeted by the activity
28
+ #
29
+ # @return [Array<Federails::Actor>]
18
30
  def recipients
19
31
  return [] unless actor.local?
20
32
 
@@ -2,6 +2,12 @@ require 'federails/utils/host'
2
2
  require 'fediverse/webfinger'
3
3
 
4
4
  module Federails
5
+ # Model storing _distant_ actors and links to local ones.
6
+ #
7
+ # To make a model act as an actor, use the `Federails::ActorEntity` concern
8
+ #
9
+ # See also:
10
+ # - https://www.w3.org/TR/activitypub/#actor-objects
5
11
  class Actor < ApplicationRecord # rubocop:disable Metrics/ClassLength
6
12
  include Federails::HasUuid
7
13
 
@@ -25,6 +31,7 @@ module Federails
25
31
  has_many :follows, source: :target_actor, through: :following_follows
26
32
 
27
33
  scope :local, -> { where.not(entity: nil) }
34
+ scope :distant, -> { where.not(federated_url: nil) }
28
35
 
29
36
  def local?
30
37
  entity.present?
@@ -1,4 +1,5 @@
1
1
  module Federails
2
+ # Stores following data between actors
2
3
  class Following < ApplicationRecord
3
4
  include Federails::HasUuid
4
5
 
@@ -0,0 +1,15 @@
1
+ module Federails
2
+ module Server
3
+ class PublishablePolicy < Federails::FederailsPolicy
4
+ def show?
5
+ @record.send(@record.federails_data_configuration[:should_federate_method])
6
+ end
7
+
8
+ class Scope < Scope
9
+ def resolve
10
+ raise NotImplementedError
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ context = true unless context == false
2
+ json.set! '@context', 'https://www.w3.org/ns/activitystreams' if context
3
+
4
+ publishable.to_activitypub_object.each_pair do |key, value|
5
+ json.set! key, value
6
+ end
7
+
8
+ json.id publishable.federated_url
9
+ json.actor publishable.federails_actor.federated_url
10
+ json.to ['https://www.w3.org/ns/activitystreams#Public']
11
+ json.cc [publishable.federails_actor.followers_url]
@@ -0,0 +1 @@
1
+ json.partial! 'federails/server/published/publishable', publishable: @publishable
data/config/routes.rb CHANGED
@@ -45,5 +45,9 @@ Federails::Engine.routes.draw do
45
45
  resources :activities, only: [:show]
46
46
  resources :followings, only: [:show]
47
47
  end
48
+
49
+ scope :published do
50
+ get ':publishable_type/:id', to: 'published#show', as: :published
51
+ end
48
52
  end
49
53
  end
@@ -1,5 +1,7 @@
1
1
  module Federails
2
2
  # rubocop:disable Style/ClassVars
3
+
4
+ # Stores the Federails configuration in a _singleton_.
3
5
  module Configuration
4
6
  # Application name, used in well-known and nodeinfo endpoints
5
7
  mattr_accessor :app_name
@@ -45,7 +47,16 @@ module Federails
45
47
  mattr_accessor :base_client_controller
46
48
  @@base_client_controller = 'ActionController::Base'
47
49
 
50
+ # @!method self.remote_follow_url_method
51
+ #
48
52
  # Route method for remote-following requests
53
+
54
+ # @!method self.remote_follow_url_method=(value)
55
+ #
56
+ # Sets the route method for remote-following requests
57
+ # @param value [String] Route method name as used in links
58
+ # @example
59
+ # remote_follow_url_method 'main_app.my_custom_route_helper'
49
60
  mattr_accessor :remote_follow_url_method
50
61
  @@remote_follow_url_method = 'federails.new_client_following_url'
51
62
 
@@ -66,6 +77,14 @@ module Federails
66
77
  def self.register_actor_class(klass, config = {})
67
78
  @@actor_types[klass.name] = config.merge(class: klass)
68
79
  end
80
+
81
+ # List of data types (classes using Federails::DataEntity)
82
+ mattr_reader :data_types
83
+ @@data_types = {}
84
+
85
+ def self.register_data_type(klass, config = {})
86
+ @@data_types[klass.name] = config.merge(class: klass)
87
+ end
69
88
  end
70
89
  # rubocop:enable Style/ClassVars
71
90
  end
@@ -0,0 +1,31 @@
1
+ module Federails
2
+ module DataTransformer
3
+ module Note
4
+ # Renders a Note. The entity is used to determine actor and generic fields data
5
+ #
6
+ # @param entity [#federail_actor] A model instance
7
+ # @param content [String] Note content
8
+ # @param name [String, nil] Optional name/title
9
+ # @param custom [Hash] Optional additional keys (e.g.: attachment, icon, ...). Defaults will override these.
10
+ #
11
+ # @return [Hash]
12
+ #
13
+ # @example
14
+ # Federails::DataTransformer::Note.to_federation(comment, content: comment.content, custom: { 'inReplyTo' => comment.parent.federated_url })
15
+ #
16
+ # See:
17
+ # - https://www.w3.org/TR/activitystreams-vocabulary/#dfn-object
18
+ # - https://www.w3.org/TR/activitystreams-vocabulary/#dfn-note
19
+ def self.to_federation(entity, content:, name: nil, custom: {})
20
+ custom.merge '@context' => 'https://www.w3.org/ns/activitystreams',
21
+ 'id' => entity.federated_url,
22
+ 'type' => 'Note',
23
+ 'name' => name,
24
+ 'content' => content,
25
+ 'attributedTo' => entity.federails_actor.federated_url,
26
+ 'published' => entity.created_at,
27
+ 'updated' => entity.updated_at
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,106 @@
1
+ module Federails
2
+ module Utils
3
+ # Methods to manipulate incoming objects
4
+ class Object
5
+ class << self
6
+ # Finds data from an object or its ID.
7
+ #
8
+ # When data exists locally, the entity is returned.
9
+ # For distant data, a new instance is returned unless the target does not exist.
10
+ #
11
+ # @param object_or_id [String, Hash] String identifier or incoming object
12
+ #
13
+ # @return [ApplicationRecord, nil] Entity or nil when invalid/not found
14
+ def find_or_initialize(object_or_id)
15
+ federated_url = object_or_id.is_a?(Hash) ? object_or_id['id'] : object_or_id
16
+
17
+ route = local_route(federated_url)
18
+ return from_local_route(route) if route
19
+
20
+ from_distant_server(object_or_id)
21
+ end
22
+
23
+ # Finds or initializes an entity from an ActivityPub object or id
24
+ #
25
+ # @see .find_or_initialize
26
+ #
27
+ # @param object_or_id [String, Hash] String identifier or incoming object
28
+ #
29
+ # @return [ApplicationRecord, nil] Entity or nil when invalid/not found
30
+ def find_or_initialize!(object_or_id)
31
+ entity = find_or_initialize object_or_id
32
+ raise ActiveRecord::RecordNotFound unless entity
33
+
34
+ entity
35
+ end
36
+
37
+ # Finds or create an entity from an ActivityPub object or id
38
+ #
39
+ # Note that the data transformer MUST return timestamps from the ActivityPub object if used on the model,
40
+ # as they won't be set automatically.
41
+ #
42
+ # @see .find_or_initialize!
43
+ #
44
+ # @param object_or_id [String, Hash] String identifier or incoming object
45
+ #
46
+ # @return [ApplicationRecord, nil] Entity or nil when invalid/not found
47
+ def find_or_create!(object_or_id)
48
+ entity = find_or_initialize! object_or_id
49
+ return entity if entity.persisted?
50
+
51
+ entity.save!(touch: false)
52
+ entity
53
+ end
54
+
55
+ # Returns the timestamps to use from an ActivityPub object
56
+ #
57
+ # @param hash [Hash] ActivityPub object
58
+ #
59
+ # @return [Hash] Hash with timestamps
60
+ def timestamp_attributes(hash)
61
+ {
62
+ created_at: hash['published'] ||= Time.current,
63
+ updated_at: hash['updated'].presence || hash['published'],
64
+ }
65
+ end
66
+
67
+ private
68
+
69
+ def local_route(url)
70
+ route = Utils::Host.local_route(url)
71
+
72
+ return nil unless route && route[:controller] == 'federails/server/published' && route[:action] == 'show'
73
+
74
+ route
75
+ end
76
+
77
+ def from_local_route(route)
78
+ config = Federails.data_entity_handled_on route[:publishable_type]
79
+ return unless config
80
+
81
+ config[:class]&.find_by(config[:url_param] => route[:id])
82
+ rescue ActiveRecord::RecordNotFound
83
+ nil
84
+ end
85
+
86
+ def from_distant_server(federated_url)
87
+ hash = Fediverse::Request.dereference(federated_url)
88
+ return unless hash
89
+
90
+ handler = Federails.data_entity_handler_for hash
91
+ return unless handler
92
+
93
+ entity = handler[:class].find_by federated_url: hash['id']
94
+ return entity if entity
95
+
96
+ entity = handler[:class].new_from_activitypub_object(hash)
97
+ return unless entity
98
+
99
+ entity.federails_actor = Federails::Actor.find_or_create_by_object hash['attributedTo'] if entity && !entity.federails_actor
100
+
101
+ entity
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -1,3 +1,3 @@
1
1
  module Federails
2
- VERSION = '0.4.0'.freeze
2
+ VERSION = '0.5.0'.freeze
3
3
  end
data/lib/federails.rb CHANGED
@@ -1,9 +1,14 @@
1
1
  require 'federails/version'
2
2
  require 'federails/engine'
3
3
  require 'federails/configuration'
4
+ require 'federails/utils/object'
4
5
 
5
6
  # rubocop:disable Style/ClassVars
7
+
8
+ # This module includes classes and methods related to Ruby on Rails: engine configuration, models, controllers, etc.
6
9
  module Federails
10
+ DEFAULT_DATA_FILTER_METHOD = :handle_federated_object?
11
+
7
12
  mattr_reader :configuration
8
13
  @@configuration = Configuration
9
14
 
@@ -49,6 +54,55 @@ module Federails
49
54
  Configuration.actor_types[klass]
50
55
  end
51
56
 
57
+ # @return [Boolean] True if the given model is a possible data entity
58
+ def data_entity?(class_or_instance)
59
+ Configuration.data_types.key? class_or_instance_name(class_or_instance)
60
+ end
61
+
62
+ # Finds configured data types from ActivityPub type
63
+ #
64
+ # @param type [String] ActivityPub object type, as configured with `:handles`
65
+ # @return [Array] List of data entity configurations
66
+ #
67
+ # @example
68
+ # data_entity_handlers_for 'Note'
69
+ def data_entity_handlers_for(type)
70
+ Federails::Configuration.data_types.select { |_, v| v[:handles] == type }.map(&:last)
71
+ end
72
+
73
+ # Finds the configured handler for a given ActivityPub object
74
+ #
75
+ # @param hash [Hash] ActivityPub object hash
76
+ #
77
+ # @return [Hash, nil] Data entity configuration
78
+ def data_entity_handler_for(hash)
79
+ data_entity_handlers_for(hash['type']).find do |handler|
80
+ return true if !handler[:filter_method] && !handler[:class].respond_to?(DEFAULT_DATA_FILTER_METHOD)
81
+
82
+ handler[:class].send(handler[:filter_method] || DEFAULT_DATA_FILTER_METHOD, hash)
83
+ end
84
+ end
85
+
86
+ # Finds configured data type from route path segment
87
+ #
88
+ # @param route_path_segment [Symbol, String] Route path segment, as configured with `:route_path_segment`
89
+ # @return [Hash, nil] Entity configuration
90
+ #
91
+ # @example
92
+ # data_entity_handled_on :articles
93
+ def data_entity_handled_on(route_path_segment)
94
+ route_path_segment = route_path_segment.to_sym
95
+ Federails::Configuration.data_types.find { |_, v| v[:route_path_segment] == route_path_segment }&.last
96
+ end
97
+
98
+ # @return [Hash] The configuration for the given data entity
99
+ def data_entity_configuration(class_or_instance)
100
+ klass = class_or_instance_name(class_or_instance)
101
+ raise "#{klass} is not a configured data entity" unless Configuration.data_types.key?(klass)
102
+
103
+ Configuration.data_types[klass]
104
+ end
105
+
52
106
  private
53
107
 
54
108
  # @return [String] Class name of the provided class or instance
@@ -4,21 +4,32 @@ module Fediverse
4
4
  class Inbox
5
5
  @@handlers = {} # rubocop:todo Style/ClassVars
6
6
  class << self
7
+ # Registers a handler for incoming data
8
+ #
9
+ # @param activity_type [String] Target activity type ('Create', 'Follow', 'Like', ...)
10
+ # See https://www.w3.org/TR/activitystreams-vocabulary/#activity-types for a list of common ones
11
+ # @param object_type [String] Type of the related object ('Article', 'Note', ...)
12
+ # See https://www.w3.org/TR/activitystreams-vocabulary/#object-types for a list of common object types
13
+ # @param klass [String] Class handling the incoming object
14
+ # @param method [Symbol] Method in the class that will handle the object
7
15
  def register_handler(activity_type, object_type, klass, method)
8
16
  @@handlers[activity_type] ||= {}
9
17
  @@handlers[activity_type][object_type] ||= {}
10
18
  @@handlers[activity_type][object_type][klass] = method
11
19
  end
12
20
 
21
+ # Executes the registered handler for an incoming object
22
+ #
23
+ # @param payload [Hash] Dereferenced activity
13
24
  def dispatch_request(payload)
14
- handlers = get_handlers(payload['type'], payload['object'].is_a?(Hash) ? payload.dig('object', 'type') : nil)
25
+ payload['object'] = Fediverse::Request.dereference(payload['object']) if payload.key? 'object'
26
+
27
+ handlers = get_handlers(payload['type'], payload.dig('object', 'type'))
15
28
  handlers.each_pair do |klass, method|
16
29
  klass.send method, payload
17
30
  end
18
31
  return true unless handlers.empty?
19
32
 
20
- # FIXME: Fails silently
21
- # raise NotImplementedError
22
33
  Rails.logger.debug { "Unhandled activity type: #{payload['type']}" }
23
34
  false
24
35
  end
@@ -40,7 +51,7 @@ module Fediverse
40
51
  end
41
52
 
42
53
  def handle_accept_request(activity)
43
- original_activity = Request.get(activity['object'])
54
+ original_activity = Request.dereference(activity['object'])
44
55
 
45
56
  actor = Federails::Actor.find_or_create_by_object original_activity['actor']
46
57
  target_actor = Federails::Actor.find_or_create_by_object original_activity['object']
@@ -3,6 +3,9 @@ require 'fediverse/signature'
3
3
  module Fediverse
4
4
  class Notifier
5
5
  class << self
6
+ # Posts an activity to its recipients
7
+ #
8
+ # @param activity [Federails::Activity]
6
9
  def post_to_inboxes(activity)
7
10
  actors = activity.recipients
8
11
  Rails.logger.debug('Nobody to notice') && return if actors.count.zero?
@@ -11,6 +11,7 @@ module Fediverse
11
11
  @id = id
12
12
  end
13
13
 
14
+ # FIXME: Replace by `Webfinger.get_json` (move other method here as class method)
14
15
  def get
15
16
  Rails.logger.debug { "GET #{@id}" }
16
17
  @response = Faraday.get(@id, nil, BASE_HEADERS)
@@ -21,6 +22,18 @@ module Fediverse
21
22
  def get(id)
22
23
  new(id).get
23
24
  end
25
+
26
+ # Dereferences a value
27
+ #
28
+ # @param value [String, Hash]
29
+ #
30
+ # @return [Hash, nil]
31
+ def dereference(value)
32
+ return value if value.is_a? Hash
33
+ return get(value) if value.is_a? String
34
+
35
+ raise "Unhandled object type #{value.class}"
36
+ end
24
37
  end
25
38
 
26
39
  private
@@ -4,31 +4,63 @@ require 'faraday/follow_redirects'
4
4
  require 'federails/utils/host'
5
5
 
6
6
  module Fediverse
7
+ # Methods related to Webfinger: find accounts, fetch actors,...
7
8
  class Webfinger
8
9
  class << self
9
10
  ACCOUNT_REGEX = /(?<username>[a-z0-9\-_.]+)(?:@(?<domain>.*))?/
10
11
 
12
+ # Extracts username and domain from a "acct:username@domain" string
13
+ #
14
+ # @param account [String] Account string
15
+ #
16
+ # @return [MatchData, nil] Matches with +:username+ and +:domain+ or +nil+
11
17
  def split_resource_account(account)
12
18
  /\Aacct:#{ACCOUNT_REGEX}\z/io.match account
13
19
  end
14
20
 
21
+ # Extracts username and domain from a "username@domain" string
22
+ #
23
+ # @param account [String] Account string
24
+ #
25
+ # @return [MatchData, nil] Matches with +:username+ and +:domain+ or +nil+
15
26
  def split_account(account)
16
27
  /\A#{ACCOUNT_REGEX}\z/io.match account
17
28
  end
18
29
 
19
- def local_user?(account)
20
- account[:username] && (account[:domain].nil? || (account[:domain] == Federails::Utils::Host.localhost))
30
+ # Determines if a given account string should be a local account (same host as configured one)
31
+ #
32
+ # @param hash [Hash, MatchData] Object with +:username+ and +:domain+ keys
33
+ #
34
+ # @return [Boolean]
35
+ def local_user?(hash)
36
+ hash[:username] && (hash[:domain].nil? || (hash[:domain] == Federails::Utils::Host.localhost))
21
37
  end
22
38
 
39
+ # Fetches a distant actor
40
+ #
41
+ # @param username [String]
42
+ # @param domain [String]
43
+ #
44
+ # @return [Federails::Actor, nil] Federails actor or nothing when not found
23
45
  def fetch_actor(username, domain)
24
46
  fetch_actor_url webfinger(username, domain)
25
47
  end
26
48
 
49
+ # Fetches an actor given its URL
50
+ #
51
+ # @param url [String] Actor's federation URL
52
+ #
53
+ # @return [Federails::Actor, nil] Federails actor or nothing when not found
27
54
  def fetch_actor_url(url)
28
55
  webfinger_to_actor get_json url
29
56
  end
30
57
 
31
- # Returns actor id
58
+ # Gets the real actor's federation URL from its username and domain
59
+ #
60
+ # @param username [String]
61
+ # @param domain [String]
62
+ #
63
+ # @return [String, nil] Federation URL if found
32
64
  def webfinger(username, domain)
33
65
  json = webfinger_response(username, domain)
34
66
  link = json['links'].find { |l| l['type'] == 'application/activity+json' }
@@ -37,6 +69,12 @@ module Fediverse
37
69
  end
38
70
 
39
71
  # Returns remote follow link template, or complete link if actor_url is provided
72
+ #
73
+ # @param username [String]
74
+ # @param domain [String]
75
+ # @param actor_url [String] Optional Federation URL to provide when known
76
+ #
77
+ # @return [String] The URL to use as follow URL
40
78
  def remote_follow_url(username, domain, actor_url: nil)
41
79
  json = webfinger_response(username, domain)
42
80
  link = json['links'].find { |l| l['rel'] == 'http://ostatus.org/schema/1.0/subscribe' }
@@ -51,13 +89,17 @@ module Fediverse
51
89
 
52
90
  private
53
91
 
92
+ # Makes a webfinger request for a given username/domain
93
+ # @return [Hash] Webfinger response's content
54
94
  def webfinger_response(username, domain)
55
95
  scheme = Federails.configuration.force_ssl ? 'https' : 'http'
56
96
  get_json "#{scheme}://#{domain}/.well-known/webfinger", resource: "acct:#{username}@#{domain}"
57
97
  end
58
98
 
59
- def server_and_port(id)
60
- uri = URI.parse id
99
+ # Extracts the server and port from a string, omitting common ports
100
+ # @return [String] Server and port
101
+ def server_and_port(string)
102
+ uri = URI.parse string
61
103
  if uri.port && [80, 443].exclude?(uri.port)
62
104
  "#{uri.host}:#{uri.port}"
63
105
  else
@@ -65,6 +107,9 @@ module Fediverse
65
107
  end
66
108
  end
67
109
 
110
+ # Builds a +Federails::Actor+ from a Webfinger response
111
+ # @param data [Hash] Webfinger response
112
+ # @return [Federails::Actor]
68
113
  def webfinger_to_actor(data)
69
114
  Federails::Actor.new federated_url: data['id'],
70
115
  username: data['preferredUsername'],
@@ -78,6 +123,9 @@ module Fediverse
78
123
  public_key: data.dig('publicKey', 'publicKeyPem')
79
124
  end
80
125
 
126
+ # Makes a simple GET request and returns a +Hash+ from the parsed body
127
+ # @return [Hash]
128
+ # @raise [ActiveRecord::RecordNotFound] when the response is invalid
81
129
  def get_json(url, payload = {})
82
130
  response = get(url, payload: payload, headers: { accept: 'application/json' })
83
131
 
@@ -93,10 +141,12 @@ module Fediverse
93
141
  raise ActiveRecord::RecordNotFound
94
142
  end
95
143
 
96
- # Only perform a GET request and throws an ActiveRecord::RecordNotFound
97
- # on error.
98
- # That's "ok-ish"; when an actor is unavailable, whatever the reason is, it's
99
- # not found...
144
+ # Only perform a GET request and throws an ActiveRecord::RecordNotFound on error.
145
+ #
146
+ # That's "ok-ish"; when an actor is unavailable, whatever the reason is, it's not found...
147
+ #
148
+ # @return [Faraday::Response]
149
+ # @raise [ActiveRecord::RecordNotFound] when the response is invalid
100
150
  def get(url, payload: {}, headers: {})
101
151
  connection = Faraday.new url: url, params: payload, headers: headers do |faraday|
102
152
  faraday.response :follow_redirects # use Faraday::FollowRedirects::Middleware
data/lib/fediverse.rb ADDED
@@ -0,0 +1,3 @@
1
+ # This module includes classes and helpers to interact with the Fediverse.
2
+ module Fediverse
3
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: federails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Manuel Tancoigne
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-12-02 00:00:00.000000000 Z
11
+ date: 2025-01-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -115,6 +115,7 @@ executables: []
115
115
  extensions: []
116
116
  extra_rdoc_files: []
117
117
  files:
118
+ - LICENSE
118
119
  - README.md
119
120
  - Rakefile
120
121
  - app/assets/config/federails_manifest.js
@@ -127,6 +128,7 @@ files:
127
128
  - app/controllers/federails/server/actors_controller.rb
128
129
  - app/controllers/federails/server/followings_controller.rb
129
130
  - app/controllers/federails/server/nodeinfo_controller.rb
131
+ - app/controllers/federails/server/published_controller.rb
130
132
  - app/controllers/federails/server/web_finger_controller.rb
131
133
  - app/controllers/federails/server_controller.rb
132
134
  - app/helpers/federails/server_helper.rb
@@ -134,6 +136,7 @@ files:
134
136
  - app/jobs/federails/notify_inbox_job.rb
135
137
  - app/mailers/federails/application_mailer.rb
136
138
  - app/models/concerns/federails/actor_entity.rb
139
+ - app/models/concerns/federails/data_entity.rb
137
140
  - app/models/concerns/federails/has_uuid.rb
138
141
  - app/models/federails/activity.rb
139
142
  - app/models/federails/actor.rb
@@ -146,6 +149,7 @@ files:
146
149
  - app/policies/federails/server/activity_policy.rb
147
150
  - app/policies/federails/server/actor_policy.rb
148
151
  - app/policies/federails/server/following_policy.rb
152
+ - app/policies/federails/server/publishable_policy.rb
149
153
  - app/views/federails/client/activities/_activity.html.erb
150
154
  - app/views/federails/client/activities/_activity.json.jbuilder
151
155
  - app/views/federails/client/activities/_index.json.jbuilder
@@ -180,6 +184,8 @@ files:
180
184
  - app/views/federails/server/followings/show.activitypub.jbuilder
181
185
  - app/views/federails/server/nodeinfo/index.nodeinfo.jbuilder
182
186
  - app/views/federails/server/nodeinfo/show.nodeinfo.jbuilder
187
+ - app/views/federails/server/published/_publishable.activitypub.jbuilder
188
+ - app/views/federails/server/published/show.activitypub.jbuilder
183
189
  - app/views/federails/server/web_finger/find.jrd.jbuilder
184
190
  - app/views/federails/server/web_finger/host_meta.xrd.erb
185
191
  - config/initializers/mime_types.rb
@@ -191,9 +197,12 @@ files:
191
197
  - db/migrate/20241002094501_add_keypair_to_actors.rb
192
198
  - lib/federails.rb
193
199
  - lib/federails/configuration.rb
200
+ - lib/federails/data_transformer/note.rb
194
201
  - lib/federails/engine.rb
195
202
  - lib/federails/utils/host.rb
203
+ - lib/federails/utils/object.rb
196
204
  - lib/federails/version.rb
205
+ - lib/fediverse.rb
197
206
  - lib/fediverse/inbox.rb
198
207
  - lib/fediverse/notifier.rb
199
208
  - lib/fediverse/request.rb
@@ -230,7 +239,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
230
239
  - !ruby/object:Gem::Version
231
240
  version: '0'
232
241
  requirements: []
233
- rubygems_version: 3.3.7
242
+ rubygems_version: 3.5.23
234
243
  signing_key:
235
244
  specification_version: 4
236
245
  summary: An ActivityPub engine for Ruby on Rails