nelumba-mongodb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - ruby-head
7
+ - rbx
8
+ services:
9
+ - mongodb
10
+ script: "bundle exec rake test"
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