nelumba-mongodb 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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