nelumba-mongodb 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.travis.yml +10 -0
- data/Gemfile +25 -0
- data/LICENSE +127 -0
- data/README.md +29 -0
- data/Rakefile +60 -0
- data/lib/nelumba-mongodb.rb +35 -0
- data/lib/nelumba-mongodb/activity.rb +316 -0
- data/lib/nelumba-mongodb/article.rb +30 -0
- data/lib/nelumba-mongodb/authorization.rb +213 -0
- data/lib/nelumba-mongodb/avatar.rb +130 -0
- data/lib/nelumba-mongodb/comment.rb +7 -0
- data/lib/nelumba-mongodb/embedded_object.rb +43 -0
- data/lib/nelumba-mongodb/feed.rb +268 -0
- data/lib/nelumba-mongodb/identity.rb +191 -0
- data/lib/nelumba-mongodb/image.rb +121 -0
- data/lib/nelumba-mongodb/note.rb +5 -0
- data/lib/nelumba-mongodb/object.rb +43 -0
- data/lib/nelumba-mongodb/person.rb +533 -0
- data/lib/nelumba-mongodb/version.rb +3 -0
- data/nelumba-mongodb.gemspec +26 -0
- data/spec/activity_spec.rb +337 -0
- data/spec/article_spec.rb +71 -0
- data/spec/authorization_spec.rb +514 -0
- data/spec/avatar_spec.rb +418 -0
- data/spec/feed_spec.rb +485 -0
- data/spec/helper.rb +72 -0
- data/spec/identity_spec.rb +245 -0
- data/spec/note_spec.rb +71 -0
- data/spec/person_spec.rb +922 -0
- metadata +164 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in nelumba-mongodb.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem "redfinger", :git => "git://github.com/hotsh/redfinger.git"
|
7
|
+
gem 'nelumba', :git => 'git://github.com/hotsh/nelumba.git'
|
8
|
+
|
9
|
+
group :test do
|
10
|
+
gem "rake" # rakefile
|
11
|
+
gem "minitest", "4.7.0" # test framework (specified here for prior rubies)
|
12
|
+
gem "ansi" # minitest colors
|
13
|
+
gem "turn" # minitest output
|
14
|
+
gem "mocha" # stubs
|
15
|
+
gem "debugger" # debugging
|
16
|
+
|
17
|
+
gem "awesome_print"
|
18
|
+
gem "rack-test"
|
19
|
+
end
|
20
|
+
|
21
|
+
platforms :rbx do
|
22
|
+
gem "json"
|
23
|
+
gem "racc"
|
24
|
+
gem "rubysl"
|
25
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
nelumba-mongodb
|
2
|
+
|
3
|
+
To the extent possible under law, the author(s) have dedicated all
|
4
|
+
copyright and related and neighboring rights to this software to the
|
5
|
+
public domain worldwide. This software is distributed without any warranty.
|
6
|
+
|
7
|
+
Creative Commons Legal Code
|
8
|
+
|
9
|
+
CC0 1.0 Universal
|
10
|
+
|
11
|
+
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
12
|
+
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
13
|
+
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
14
|
+
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
15
|
+
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
16
|
+
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
17
|
+
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
18
|
+
HEREUNDER.
|
19
|
+
|
20
|
+
Statement of Purpose
|
21
|
+
|
22
|
+
The laws of most jurisdictions throughout the world automatically confer
|
23
|
+
exclusive Copyright and Related Rights (defined below) upon the creator
|
24
|
+
and subsequent owner(s) (each and all, an "owner") of an original work of
|
25
|
+
authorship and/or a database (each, a "Work").
|
26
|
+
|
27
|
+
Certain owners wish to permanently relinquish those rights to a Work for
|
28
|
+
the purpose of contributing to a commons of creative, cultural and
|
29
|
+
scientific works ("Commons") that the public can reliably and without fear
|
30
|
+
of later claims of infringement build upon, modify, incorporate in other
|
31
|
+
works, reuse and redistribute as freely as possible in any form whatsoever
|
32
|
+
and for any purposes, including without limitation commercial purposes.
|
33
|
+
These owners may contribute to the Commons to promote the ideal of a free
|
34
|
+
culture and the further production of creative, cultural and scientific
|
35
|
+
works, or to gain reputation or greater distribution for their Work in
|
36
|
+
part through the use and efforts of others.
|
37
|
+
|
38
|
+
For these and/or other purposes and motivations, and without any
|
39
|
+
expectation of additional consideration or compensation, the person
|
40
|
+
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
41
|
+
is an owner of Copyright and Related Rights in the Work, voluntarily
|
42
|
+
elects to apply CC0 to the Work and publicly distribute the Work under its
|
43
|
+
terms, with knowledge of his or her Copyright and Related Rights in the
|
44
|
+
Work and the meaning and intended legal effect of CC0 on those rights.
|
45
|
+
|
46
|
+
1. Copyright and Related Rights. A Work made available under CC0 may be
|
47
|
+
protected by copyright and related or neighboring rights ("Copyright and
|
48
|
+
Related Rights"). Copyright and Related Rights include, but are not
|
49
|
+
limited to, the following:
|
50
|
+
|
51
|
+
i. the right to reproduce, adapt, distribute, perform, display,
|
52
|
+
communicate, and translate a Work;
|
53
|
+
ii. moral rights retained by the original author(s) and/or performer(s);
|
54
|
+
iii. publicity and privacy rights pertaining to a person's image or
|
55
|
+
likeness depicted in a Work;
|
56
|
+
iv. rights protecting against unfair competition in regards to a Work,
|
57
|
+
subject to the limitations in paragraph 4(a), below;
|
58
|
+
v. rights protecting the extraction, dissemination, use and reuse of data
|
59
|
+
in a Work;
|
60
|
+
vi. database rights (such as those arising under Directive 96/9/EC of the
|
61
|
+
European Parliament and of the Council of 11 March 1996 on the legal
|
62
|
+
protection of databases, and under any national implementation
|
63
|
+
thereof, including any amended or successor version of such
|
64
|
+
directive); and
|
65
|
+
vii. other similar, equivalent or corresponding rights throughout the
|
66
|
+
world based on applicable law or treaty, and any national
|
67
|
+
implementations thereof.
|
68
|
+
|
69
|
+
2. Waiver. To the greatest extent permitted by, but not in contravention
|
70
|
+
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
71
|
+
irrevocably and unconditionally waives, abandons, and surrenders all of
|
72
|
+
Affirmer's Copyright and Related Rights and associated claims and causes
|
73
|
+
of action, whether now known or unknown (including existing as well as
|
74
|
+
future claims and causes of action), in the Work (i) in all territories
|
75
|
+
worldwide, (ii) for the maximum duration provided by applicable law or
|
76
|
+
treaty (including future time extensions), (iii) in any current or future
|
77
|
+
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
78
|
+
including without limitation commercial, advertising or promotional
|
79
|
+
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
80
|
+
member of the public at large and to the detriment of Affirmer's heirs and
|
81
|
+
successors, fully intending that such Waiver shall not be subject to
|
82
|
+
revocation, rescission, cancellation, termination, or any other legal or
|
83
|
+
equitable action to disrupt the quiet enjoyment of the Work by the public
|
84
|
+
as contemplated by Affirmer's express Statement of Purpose.
|
85
|
+
|
86
|
+
3. Public License Fallback. Should any part of the Waiver for any reason
|
87
|
+
be judged legally invalid or ineffective under applicable law, then the
|
88
|
+
Waiver shall be preserved to the maximum extent permitted taking into
|
89
|
+
account Affirmer's express Statement of Purpose. In addition, to the
|
90
|
+
extent the Waiver is so judged Affirmer hereby grants to each affected
|
91
|
+
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
92
|
+
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
93
|
+
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
94
|
+
maximum duration provided by applicable law or treaty (including future
|
95
|
+
time extensions), (iii) in any current or future medium and for any number
|
96
|
+
of copies, and (iv) for any purpose whatsoever, including without
|
97
|
+
limitation commercial, advertising or promotional purposes (the
|
98
|
+
"License"). The License shall be deemed effective as of the date CC0 was
|
99
|
+
applied by Affirmer to the Work. Should any part of the License for any
|
100
|
+
reason be judged legally invalid or ineffective under applicable law, such
|
101
|
+
partial invalidity or ineffectiveness shall not invalidate the remainder
|
102
|
+
of the License, and in such case Affirmer hereby affirms that he or she
|
103
|
+
will not (i) exercise any of his or her remaining Copyright and Related
|
104
|
+
Rights in the Work or (ii) assert any associated claims and causes of
|
105
|
+
action with respect to the Work, in either case contrary to Affirmer's
|
106
|
+
express Statement of Purpose.
|
107
|
+
|
108
|
+
4. Limitations and Disclaimers.
|
109
|
+
|
110
|
+
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
111
|
+
surrendered, licensed or otherwise affected by this document.
|
112
|
+
b. Affirmer offers the Work as-is and makes no representations or
|
113
|
+
warranties of any kind concerning the Work, express, implied,
|
114
|
+
statutory or otherwise, including without limitation warranties of
|
115
|
+
title, merchantability, fitness for a particular purpose, non
|
116
|
+
infringement, or the absence of latent or other defects, accuracy, or
|
117
|
+
the present or absence of errors, whether or not discoverable, all to
|
118
|
+
the greatest extent permissible under applicable law.
|
119
|
+
c. Affirmer disclaims responsibility for clearing rights of other persons
|
120
|
+
that may apply to the Work or any use thereof, including without
|
121
|
+
limitation any person's Copyright and Related Rights in the Work.
|
122
|
+
Further, Affirmer disclaims responsibility for obtaining any necessary
|
123
|
+
consents, permissions or other rights required for any use of the
|
124
|
+
Work.
|
125
|
+
d. Affirmer understands and acknowledges that Creative Commons is not a
|
126
|
+
party to this document and has no duty or obligation with respect to
|
127
|
+
this CC0 or use of the Work.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Nelumba::Mongodb
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'nelumba-mongodb'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install nelumba-mongodb
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
Rake::TestTask.new do |t|
|
6
|
+
t.pattern = "spec/**/*_spec.rb"
|
7
|
+
end
|
8
|
+
|
9
|
+
namespace :test do
|
10
|
+
desc "Run all tests (rake test will do this be default)"
|
11
|
+
task :all do
|
12
|
+
Rake::TestTask.new("all") do |t|
|
13
|
+
t.pattern = "spec/**/*_spec.rb"
|
14
|
+
end
|
15
|
+
task("all").execute
|
16
|
+
end
|
17
|
+
|
18
|
+
Dir.foreach("spec") do |dirname|
|
19
|
+
if File.directory?(File.join("spec", dirname))
|
20
|
+
desc "Run #{dirname} tests"
|
21
|
+
task dirname do
|
22
|
+
test_task = Rake::TestTask.new("#{dirname}tests") do |t|
|
23
|
+
t.test_files = Dir.glob(File.join("spec", dirname, "**", "*_spec.rb"))
|
24
|
+
end
|
25
|
+
task("#{dirname}tests").execute
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "Run single model"
|
31
|
+
task :model, :file do |task, args|
|
32
|
+
test_task = Rake::TestTask.new("unittests") do |t|
|
33
|
+
if args.file
|
34
|
+
file = "spec/#{args.file}_spec.rb"
|
35
|
+
t.pattern = file
|
36
|
+
puts "Testing #{file}"
|
37
|
+
else
|
38
|
+
t.pattern = "spec/*_spec.rb"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
task("unittests").execute
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "Run single file"
|
45
|
+
task :file, :file do |task, args|
|
46
|
+
test_task = Rake::TestTask.new("unittests") do |t|
|
47
|
+
if args.file
|
48
|
+
file = args.file
|
49
|
+
unless file.start_with? "spec/"
|
50
|
+
file = "spec/#{args.file}"
|
51
|
+
end
|
52
|
+
t.pattern = file
|
53
|
+
puts "Testing #{file}"
|
54
|
+
else
|
55
|
+
t.pattern = "spec/**/*_spec.rb"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
task("unittests").execute
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'nelumba'
|
2
|
+
require 'mongo_mapper'
|
3
|
+
|
4
|
+
MongoMapper.setup({
|
5
|
+
'default' => {
|
6
|
+
'database' => ENV['MONGOHQ_DATABASE'] ||
|
7
|
+
ENV['MONGODB_DATABASE'] ||
|
8
|
+
'nelumba-mongodb',
|
9
|
+
'uri' => ENV['MONGOHQ_URL'] ||
|
10
|
+
ENV['MONGODB_URI'] ||
|
11
|
+
ENV['MONGOLAB_URI']
|
12
|
+
}
|
13
|
+
}, 'default')
|
14
|
+
|
15
|
+
require "nelumba-mongodb/embedded_object"
|
16
|
+
require "nelumba-mongodb/object"
|
17
|
+
|
18
|
+
require "nelumba-mongodb/identity"
|
19
|
+
require "nelumba-mongodb/activity"
|
20
|
+
require "nelumba-mongodb/feed"
|
21
|
+
require "nelumba-mongodb/person"
|
22
|
+
require "nelumba-mongodb/authorization"
|
23
|
+
require "nelumba-mongodb/avatar"
|
24
|
+
|
25
|
+
require "nelumba-mongodb/note"
|
26
|
+
require "nelumba-mongodb/article"
|
27
|
+
require "nelumba-mongodb/comment"
|
28
|
+
require "nelumba-mongodb/image"
|
29
|
+
|
30
|
+
module Nelumba
|
31
|
+
BCRYPT_ROUNDS = 13
|
32
|
+
|
33
|
+
def create_person
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,316 @@
|
|
1
|
+
module Nelumba
|
2
|
+
class Activity
|
3
|
+
include Nelumba::Object
|
4
|
+
|
5
|
+
# All Activities originate from one particular Feed.
|
6
|
+
key :feed_id, ObjectId
|
7
|
+
belongs_to :feed, :class_name => 'Nelumba::Feed'
|
8
|
+
|
9
|
+
# Determines what type of object this Activity represents. Standard types
|
10
|
+
# include:
|
11
|
+
# :article, :audio, :bookmark, :comment, :file, :folder, :group,
|
12
|
+
# :list, :note, :person, :photo, :"photo-album", :place, :playlist,
|
13
|
+
# :product, :review, :service, :status, :video
|
14
|
+
key :type
|
15
|
+
|
16
|
+
# Determines the action this Activity represents. Standard types include:
|
17
|
+
# :favorite, :follow, :like, :"make-friend", :join, :play,
|
18
|
+
# :post, :save, :share, :tag, :update
|
19
|
+
key :verb
|
20
|
+
|
21
|
+
# Determines what is acting.
|
22
|
+
key :actor_id, ObjectId
|
23
|
+
key :actor_type, String
|
24
|
+
|
25
|
+
# Determines what the action is acting upon.
|
26
|
+
key :target_id, ObjectId
|
27
|
+
key :target_type, String
|
28
|
+
|
29
|
+
# Can attach an external object to this Activity.
|
30
|
+
key :external_object_id, ObjectId
|
31
|
+
key :external_object_type, String
|
32
|
+
|
33
|
+
# Optionally, an object can be embedded inside an Activity.
|
34
|
+
one :embedded_object, :class_name => "Nelumba::Comment", :polymorphic => true
|
35
|
+
|
36
|
+
# The title of the Activity.
|
37
|
+
key :title
|
38
|
+
|
39
|
+
# Contains the source of this Activity if it is a repost or otherwise copied
|
40
|
+
# from another Feed.
|
41
|
+
key :source, :class_name => 'Nelumba::Feed'
|
42
|
+
|
43
|
+
# Contains the Activity this Activity is a response of.
|
44
|
+
key :in_reply_to_ids, Array
|
45
|
+
remove_method :in_reply_to
|
46
|
+
many :in_reply_to, :in => :in_reply_to_ids, :class_name => 'Nelumba::Activity'
|
47
|
+
|
48
|
+
# Contains the Activities that are replies to this one
|
49
|
+
key :replies_ids, Array
|
50
|
+
remove_method :replies
|
51
|
+
many :replies, :in => :replies_ids, :class_name => 'Nelumba::Activity'
|
52
|
+
|
53
|
+
# Contains the Persons this Activity mentions.
|
54
|
+
key :mentions_ids, Array
|
55
|
+
remove_method :mentions
|
56
|
+
many :mentions, :in => :mentions_ids, :class_name => 'Nelumba::Person'
|
57
|
+
|
58
|
+
# Contains the Persons that have shared this activity
|
59
|
+
key :shares_ids, Array
|
60
|
+
remove_method :shares
|
61
|
+
many :shares, :in => :shares_ids, :class_name => 'Nelumba::Person'
|
62
|
+
|
63
|
+
# Contains the Persons that have liked this activity
|
64
|
+
key :likes_ids, Array
|
65
|
+
remove_method :likes
|
66
|
+
many :likes, :in => :likes_ids, :class_name => 'Nelumba::Person'
|
67
|
+
|
68
|
+
# Hash containing various interaction metadata
|
69
|
+
key :interactions
|
70
|
+
|
71
|
+
# Ensure that url and uid for the activity are set
|
72
|
+
before_create :ensure_uid_and_url
|
73
|
+
|
74
|
+
# Scrape content for mentions
|
75
|
+
before_create :scrape_mentions
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
# Ensure uid and url are established. If they don't exist, just use urls
|
80
|
+
# that point to us for the sake of uniqueness.
|
81
|
+
def ensure_uid_and_url
|
82
|
+
unless self.uid && self.url
|
83
|
+
self.uid = "/activities/#{self.id}"
|
84
|
+
self.url = "/activities/#{self.id}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Scrape content for username mentions.
|
89
|
+
def scrape_mentions
|
90
|
+
return if self.object.nil?
|
91
|
+
return unless !self.object.is_a?(Nelumba::Person) &&
|
92
|
+
self.object.respond_to?(:mentions)
|
93
|
+
authors = self.object.mentions do |username, domain|
|
94
|
+
i = Identity.first(:username => /^#{Regexp.escape(username)}$/i)
|
95
|
+
i.person if i
|
96
|
+
end
|
97
|
+
authors ||= []
|
98
|
+
self.mentions_ids = authors.compact.map(&:id)
|
99
|
+
end
|
100
|
+
|
101
|
+
public
|
102
|
+
|
103
|
+
def mentions?(author)
|
104
|
+
if author.is_a? Nelumba::Identity
|
105
|
+
author = author.person
|
106
|
+
end
|
107
|
+
|
108
|
+
self.mentions_ids.include? author.id
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.article_by_id(id)
|
112
|
+
self.object_by_id_and_type(id, Nelumba::Article)
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.note_by_id(id)
|
116
|
+
self.object_by_id_and_type(id, Nelumba::Note)
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.object_by_id(id)
|
120
|
+
oid = id
|
121
|
+
if id.is_a? String
|
122
|
+
oid = BSON::ObjectId.from_string(id)
|
123
|
+
end
|
124
|
+
|
125
|
+
activity = Nelumba::Activity.first("embedded_object._id" => oid)
|
126
|
+
activity.object if activity
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.object_by_id_and_type(id, type)
|
130
|
+
obj = self.object_by_id(id)
|
131
|
+
return obj if obj.is_a? type
|
132
|
+
nil
|
133
|
+
end
|
134
|
+
|
135
|
+
# Intern, for consistency, standard object types.
|
136
|
+
def type=(type)
|
137
|
+
if STANDARD_TYPES.map(&:to_s).include? type
|
138
|
+
type = type.intern
|
139
|
+
end
|
140
|
+
|
141
|
+
super type
|
142
|
+
end
|
143
|
+
|
144
|
+
# Set the actor.
|
145
|
+
def actor=(obj)
|
146
|
+
if obj.nil?
|
147
|
+
self.actor_id = nil
|
148
|
+
self.actor_type = nil
|
149
|
+
return
|
150
|
+
end
|
151
|
+
|
152
|
+
@actor = obj
|
153
|
+
|
154
|
+
self.actor_id = obj.id
|
155
|
+
self.actor_type = obj.class.to_s
|
156
|
+
self.actor_type = self.actor_type[9..-1] if self.actor_type.start_with? "Nelumba::"
|
157
|
+
end
|
158
|
+
|
159
|
+
# Get the actor.
|
160
|
+
def actor
|
161
|
+
return @actor if @actor
|
162
|
+
|
163
|
+
return nil if self.actor_type && !Nelumba.const_defined?(self.actor_type)
|
164
|
+
klass = Nelumba.const_get(self.actor_type) if self.actor_type
|
165
|
+
@actor = klass.first(:id => self.actor_id) if klass && self.actor_id
|
166
|
+
end
|
167
|
+
|
168
|
+
# Set the object.
|
169
|
+
def object=(obj)
|
170
|
+
if obj.nil?
|
171
|
+
self.external_object_id = nil
|
172
|
+
self.external_object_type = nil
|
173
|
+
elsif obj.class.respond_to?(:embeddable?) && obj.class.embeddable?
|
174
|
+
self.embedded_object = obj
|
175
|
+
self.external_object_id = nil
|
176
|
+
self.external_object_type = nil
|
177
|
+
else
|
178
|
+
self.embedded_object = nil
|
179
|
+
self.external_object_id = obj.id
|
180
|
+
self.external_object_type = obj.class.to_s
|
181
|
+
if self.external_object_type.start_with? "Nelumba::"
|
182
|
+
self.external_object_type = self.external_object_type[7..-1]
|
183
|
+
end
|
184
|
+
end
|
185
|
+
@object = obj
|
186
|
+
end
|
187
|
+
|
188
|
+
# Get the object.
|
189
|
+
def object
|
190
|
+
return @object if @object
|
191
|
+
|
192
|
+
return @object = self.embedded_object if self.embedded_object
|
193
|
+
return @object = self unless self.external_object_type
|
194
|
+
|
195
|
+
return nil if self.external_object_type && !Nelumba.const_defined?(self.external_object_type)
|
196
|
+
klass = Nelumba.const_get(self.external_object_type) if self.external_object_type
|
197
|
+
@object = klass.find_by_id(self.external_object_id) if klass && self.external_object_id
|
198
|
+
end
|
199
|
+
|
200
|
+
# Create a new Activity if the given Activity is not found by its id.
|
201
|
+
def self.find_or_create_by_uid!(arg, *args)
|
202
|
+
if arg.is_a? Nelumba::Activity
|
203
|
+
uid = arg.uid
|
204
|
+
else
|
205
|
+
uid = arg[:uid]
|
206
|
+
|
207
|
+
if arg[:author]
|
208
|
+
arg[:author] = Person.find_or_create_by_uid!(arg[:author])
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
activity = self.first(:uid => uid)
|
213
|
+
return activity if activity
|
214
|
+
|
215
|
+
begin
|
216
|
+
if arg.is_a? Nelumba::Activity
|
217
|
+
arg.save
|
218
|
+
else
|
219
|
+
activity = create!(arg, *args)
|
220
|
+
end
|
221
|
+
rescue
|
222
|
+
activity = self.first(:uid => uid) or raise
|
223
|
+
end
|
224
|
+
|
225
|
+
activity
|
226
|
+
end
|
227
|
+
|
228
|
+
# Create a new Activity from a Hash of values or a Nelumba::Activity.
|
229
|
+
def self.create!(*args)
|
230
|
+
hash = {}
|
231
|
+
if args.length > 0
|
232
|
+
hash = args.shift
|
233
|
+
end
|
234
|
+
|
235
|
+
if hash.is_a? Nelumba::Activity
|
236
|
+
hash = hash.to_hash
|
237
|
+
|
238
|
+
hash.delete :author
|
239
|
+
hash.delete :in_reply_to
|
240
|
+
end
|
241
|
+
|
242
|
+
super hash, *args
|
243
|
+
end
|
244
|
+
|
245
|
+
# Create a new Activity from a Hash of values or a Nelumba::Activity.
|
246
|
+
def self.create(*args)
|
247
|
+
self.create! *args
|
248
|
+
end
|
249
|
+
|
250
|
+
# Discover a feed by the given activity location.
|
251
|
+
def self.discover!(activity_identifier)
|
252
|
+
activity = Nelumba::Activity.first(:url => activity_identifier)
|
253
|
+
return activity if activity
|
254
|
+
|
255
|
+
activity = Nelumba::Discover.activity(activity_identifier)
|
256
|
+
return false unless activity
|
257
|
+
|
258
|
+
existing = Activity.first(:uid => activity.uid)
|
259
|
+
return existing if existing
|
260
|
+
|
261
|
+
self.create!(activity)
|
262
|
+
end
|
263
|
+
|
264
|
+
def self.find_from_notification(notification)
|
265
|
+
Nelumba::Activity.first(:uid => notification.activity.uid)
|
266
|
+
end
|
267
|
+
|
268
|
+
def self.create_from_notification!(notification)
|
269
|
+
# We need to verify the payload
|
270
|
+
identity = Nelumba::Identity.discover!(notification.account)
|
271
|
+
if notification.verified? identity.return_or_discover_public_key
|
272
|
+
# Then add it to our feed in the appropriate place
|
273
|
+
identity.discover_person!
|
274
|
+
internal_activity = Nelumba::Activity.find_from_notification(notification)
|
275
|
+
|
276
|
+
# If it already exists, update it
|
277
|
+
if internal_activity
|
278
|
+
internal_activity.update_from_notification(notification, true)
|
279
|
+
else
|
280
|
+
internal_activity = Nelumba::Activity.create!(notification.activity)
|
281
|
+
internal_author = Nelumba::Person.find_or_create_by_uid!(
|
282
|
+
notification.activity.actor.uid)
|
283
|
+
|
284
|
+
internal_activity.actor = internal_author
|
285
|
+
internal_activity.save
|
286
|
+
internal_activity
|
287
|
+
end
|
288
|
+
else
|
289
|
+
nil
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def update_from_notification(notification, force = false)
|
294
|
+
# Do not allow another actor to change an existing activity
|
295
|
+
if self.actor && self.actor.url != notification.activity.actor.url
|
296
|
+
return nil
|
297
|
+
end
|
298
|
+
|
299
|
+
# We need to verify the payload
|
300
|
+
identity = Nelumba::Identity.discover!(notification.account)
|
301
|
+
if force or notification.verified?(identity.return_or_discover_public_key)
|
302
|
+
# Then add it to our feed in the appropriate place
|
303
|
+
identity.discover_person!
|
304
|
+
|
305
|
+
attributes = notification.activity.to_hash
|
306
|
+
attributes.delete :uid
|
307
|
+
|
308
|
+
self.update_attributes!(attributes)
|
309
|
+
|
310
|
+
self
|
311
|
+
else
|
312
|
+
nil
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|