rails_newsfeed 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +20 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.travis.yml +22 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +116 -0
- data/Rakefile +3 -0
- data/bin/console +6 -0
- data/bin/setup +5 -0
- data/config/cassandra.yml +16 -0
- data/lib/generators/rails_newsfeed/config_generator.rb +12 -0
- data/lib/generators/rails_newsfeed/init_generator.rb +31 -0
- data/lib/generators/rails_newsfeed/model_generator.rb +41 -0
- data/lib/generators/rails_newsfeed/templates/config/cassandra.yml +20 -0
- data/lib/rails_newsfeed.rb +10 -0
- data/lib/rails_newsfeed/activity.rb +195 -0
- data/lib/rails_newsfeed/connection.rb +105 -0
- data/lib/rails_newsfeed/feed_table.rb +36 -0
- data/lib/rails_newsfeed/newsfeed_model.rb +161 -0
- data/lib/rails_newsfeed/railtie.rb +7 -0
- data/lib/rails_newsfeed/relation.rb +64 -0
- data/lib/rails_newsfeed/version.rb +3 -0
- data/rails_newsfeed.gemspec +31 -0
- data/tasks/rspec.rake +3 -0
- data/travis/create-schema.cql +9 -0
- data/travis/drop-schema.cql +1 -0
- metadata +227 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d6b107405806ed747a2935a326a486593789a6ec
|
4
|
+
data.tar.gz: f2cac89d046dc7ea51988adf593664ca10842bb5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f2d9dd1c7e8061a93c8f4a90ad9264b03d60acdad038905e3fe2401a52aeb16fba319b8102603599963d5a1149dc9c56b861c78c24346e87082ce57004c7a4eb
|
7
|
+
data.tar.gz: d6ec820776305a46a9fefa99c568252507cd5612df42e1eaa230a75cfd9b0b197a01dc10f3ac7e984b0120ea953d0393f5db121bf530f95cfed96e78a93c2384
|
data/.codeclimate.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
---
|
2
|
+
engines:
|
3
|
+
duplication:
|
4
|
+
enabled: true
|
5
|
+
config:
|
6
|
+
languages:
|
7
|
+
- ruby
|
8
|
+
fixme:
|
9
|
+
enabled: true
|
10
|
+
rubocop:
|
11
|
+
enabled: true
|
12
|
+
config: ".rubocop.yml"
|
13
|
+
ratings:
|
14
|
+
paths:
|
15
|
+
- "**.rb"
|
16
|
+
exclude_paths:
|
17
|
+
- config/
|
18
|
+
- spec/
|
19
|
+
- travis/
|
20
|
+
- tasks/
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.0.0
|
4
|
+
- 2.1.8
|
5
|
+
- 2.2.0
|
6
|
+
- 2.2.4
|
7
|
+
- 2.3.0
|
8
|
+
|
9
|
+
install:
|
10
|
+
- bundle install
|
11
|
+
|
12
|
+
services:
|
13
|
+
- cassandra
|
14
|
+
|
15
|
+
before_script:
|
16
|
+
- cqlsh -f travis/create-schema.cql
|
17
|
+
|
18
|
+
after_script:
|
19
|
+
- cqlsh -f travis/drop-schema.cql
|
20
|
+
|
21
|
+
script:
|
22
|
+
- bundle exec rspec spec
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Thanh (Adam) T. NGUYEN
|
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/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Adam Nguyen
|
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
|
13
|
+
all 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
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
# Newsfeed for Rails
|
2
|
+
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/adamnguyenit/rails_newsfeed/master/LICENSE)
|
3
|
+
[![Build Status](https://travis-ci.org/adamnguyenit/rails_newsfeed.svg?branch=master)](https://travis-ci.org/adamnguyenit/rails_newsfeed)
|
4
|
+
[![Dependency Status](https://gemnasium.com/adamnguyenit/rails_newsfeed.svg)](https://gemnasium.com/adamnguyenit/rails_newsfeed)
|
5
|
+
[![Code Climate](https://codeclimate.com/github/adamnguyenit/rails_newsfeed/badges/gpa.svg)](https://codeclimate.com/github/adamnguyenit/rails_newsfeed)
|
6
|
+
[![Coverage Status](https://coveralls.io/repos/github/adamnguyenit/rails_newsfeed/badge.svg?branch=master)](https://coveralls.io/github/adamnguyenit/rails_newsfeed?branch=master)
|
7
|
+
[![Gem Version](https://badge.fury.io/rb/rails_newsfeed.svg)](https://badge.fury.io/rb/rails_newsfeed)
|
8
|
+
![Download](http://ruby-gem-downloads-badge.herokuapp.com/rails_newsfeed)
|
9
|
+
[![GitHub stars](https://img.shields.io/github/stars/adamnguyenit/rails_newsfeed.svg)](https://github.com/adamnguyenit/rails_newsfeed/stargazers)
|
10
|
+
[![GitHub issues](https://img.shields.io/github/issues/adamnguyenit/rails_newsfeed.svg)](https://github.com/adamnguyenit/rails_newsfeed/issues)
|
11
|
+
![Repo Size](https://reposs.herokuapp.com/?path=adamnguyenit/rails_newsfeed)
|
12
|
+
|
13
|
+
This is a gem for newsfeed module on rails. It uses Cassandra >= 2.x to store data and control your feeds system.
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
*Requirement*
|
17
|
+
|
18
|
+
Cassandra >= 2.x
|
19
|
+
Rails 4.x
|
20
|
+
After cassandra installed, it is recommend to increase `batch_size_fail_threshold_in_kb` in your `cassandra.yaml` and restart cassandra. Depends on your size of relation between models.
|
21
|
+
*Gem*
|
22
|
+
|
23
|
+
Add this line to your application's Gemfile:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
gem 'rails_newsfeed'
|
27
|
+
```
|
28
|
+
|
29
|
+
And then execute:
|
30
|
+
|
31
|
+
$ bundle
|
32
|
+
|
33
|
+
Or install it yourself as:
|
34
|
+
|
35
|
+
$ gem install rails_newsfeed
|
36
|
+
|
37
|
+
## Usage
|
38
|
+
|
39
|
+
First, let this gem generate the cassandra config file. Run rails generation
|
40
|
+
|
41
|
+
$ rails g rails_newsfeed:config
|
42
|
+
|
43
|
+
and change the configuration follows your system.
|
44
|
+
Then let create the schema by generator
|
45
|
+
|
46
|
+
$ rails g rails_newsfeed:init
|
47
|
+
|
48
|
+
And create a model by
|
49
|
+
|
50
|
+
$ rails g rails_newsfeed:model user_feed --type_of_id=bigint
|
51
|
+
|
52
|
+
Change the type_of_id option to match the type of your model
|
53
|
+
|
54
|
+
# Quick start
|
55
|
+
|
56
|
+
Save the activity first
|
57
|
+
```ruby
|
58
|
+
activity = RailsNewsfeed::Activity.new(content: 'user 1 upload photo 1')
|
59
|
+
activity.save
|
60
|
+
```
|
61
|
+
|
62
|
+
Add a feed
|
63
|
+
```ruby
|
64
|
+
user_feed = UserFeed.new(id: 1)
|
65
|
+
user_feed.insert(activity)
|
66
|
+
```
|
67
|
+
|
68
|
+
Get feeds
|
69
|
+
```ruby
|
70
|
+
user_feed = UserFeed.new(id: 1)
|
71
|
+
feeds = user_feed.feeds
|
72
|
+
next_page_token = user_feed.next_page_token
|
73
|
+
```
|
74
|
+
|
75
|
+
Delete a feed
|
76
|
+
```ruby
|
77
|
+
activity = RailsNewsfeed::Activity.find(feed_id)
|
78
|
+
activity.delete
|
79
|
+
```
|
80
|
+
|
81
|
+
Register to another feed model
|
82
|
+
```ruby
|
83
|
+
user_a_feed = UserFeed.new(id: 1)
|
84
|
+
user_b_feed = UserFeed.new(id: 2)
|
85
|
+
user_a_feed.register(user_a_feed)
|
86
|
+
```
|
87
|
+
|
88
|
+
|
89
|
+
## Development
|
90
|
+
|
91
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
92
|
+
|
93
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
94
|
+
|
95
|
+
Before running test you must apply these cqls into cassandra.
|
96
|
+
```ruby
|
97
|
+
CREATE KEYSPACE rails_newsfeed_test WITH REPLICATION = { 'class': 'SimpleStrategy', 'replication_factor': 3 };
|
98
|
+
USE rails_newsfeed_test;
|
99
|
+
CREATE TABLE activity (id uuid, content text, time timestamp, object text, PRIMARY KEY (id));
|
100
|
+
CREATE TABLE activity_index (id uuid, content text, time timestamp, object text, PRIMARY KEY ((object), id));
|
101
|
+
CREATE TABLE feed_table (table_class text, PRIMARY KEY (table_class));
|
102
|
+
CREATE TABLE relation (id uuid, from_class text, from_id text, to_class text, to_id text, PRIMARY KEY ((from_class, from_id), id));
|
103
|
+
CREATE TABLE relation_index(id uuid, from_class text, from_id text, to_class text, to_id text, PRIMARY KEY ((from_class, from_id, to_class, to_id)));
|
104
|
+
CREATE TABLE user_feed (id bigint, activity_id uuid, activity_content text, activity_object text, activity_time timestamp, PRIMARY KEY ((id), activity_id));
|
105
|
+
INSERT INTO feed_table (table_class) VALUES ('UserFeed');
|
106
|
+
```
|
107
|
+
We use rspec to test this gem.
|
108
|
+
|
109
|
+
## Contributing
|
110
|
+
|
111
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/adamnguyenit/rails_newsfeed. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
112
|
+
|
113
|
+
|
114
|
+
## License
|
115
|
+
|
116
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
data/bin/setup
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails_newsfeed/railtie'
|
3
|
+
|
4
|
+
module RailsNewsfeed
|
5
|
+
class ConfigGenerator < Rails::Generators::Base
|
6
|
+
desc 'This generator creates the config/cassandra.xml file to config cassandra server for newsfeed'
|
7
|
+
source_root File.expand_path('../templates', __FILE__)
|
8
|
+
def process
|
9
|
+
template 'config/cassandra.yml'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails_newsfeed/connection'
|
3
|
+
require 'rails_newsfeed/activity'
|
4
|
+
require 'rails_newsfeed/feed_table'
|
5
|
+
require 'rails_newsfeed/relation'
|
6
|
+
|
7
|
+
module RailsNewsfeed
|
8
|
+
class InitGenerator < Rails::Generators::Base
|
9
|
+
desc 'This generator initials your cassandra schema db for feed'
|
10
|
+
def process
|
11
|
+
cfg = RailsNewsfeed::Connection.config
|
12
|
+
connection = Cassandra.cluster(cfg || {}).connect('system')
|
13
|
+
connection.execute("DROP KEYSPACE IF EXISTS #{cfg['keyspace']}")
|
14
|
+
connection.execute("CREATE KEYSPACE #{cfg['keyspace']}
|
15
|
+
WITH REPLICATION={ 'class': 'SimpleStrategy', 'replication_factor': 3 }")
|
16
|
+
connection.execute("USE #{cfg['keyspace']}")
|
17
|
+
connection.execute("CREATE TABLE #{RailsNewsfeed::Activity.table_name}
|
18
|
+
(id uuid, content text, time timestamp, object text, PRIMARY KEY (id))")
|
19
|
+
connection.execute("CREATE TABLE #{RailsNewsfeed::Activity.index_table_name}
|
20
|
+
(id uuid, content text, time timestamp, object text, PRIMARY KEY ((object), id))
|
21
|
+
WITH CLUSTERING ORDER BY (id DESC)")
|
22
|
+
connection.execute("CREATE TABLE #{RailsNewsfeed::FeedTable.table_name}
|
23
|
+
(table_class text, PRIMARY KEY (table_class))")
|
24
|
+
connection.execute("CREATE TABLE #{RailsNewsfeed::Relation.table_name}
|
25
|
+
(id uuid, from_class text, from_id text, to_class text, to_id text, PRIMARY KEY ((from_class, from_id), id))")
|
26
|
+
connection.execute("CREATE TABLE #{RailsNewsfeed::Relation.index_table_name}
|
27
|
+
(id uuid, from_class text, from_id text, to_class text, to_id text,
|
28
|
+
PRIMARY KEY ((from_class, from_id, to_class, to_id)))")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails_newsfeed/feed_table'
|
3
|
+
|
4
|
+
module RailsNewsfeed
|
5
|
+
class ModelGenerator < Rails::Generators::NamedBase
|
6
|
+
class_option :type_of_id, type: :string, require: false
|
7
|
+
def process
|
8
|
+
case behavior
|
9
|
+
when :invoke
|
10
|
+
invoke
|
11
|
+
when :revoke
|
12
|
+
revoke
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def invoke
|
19
|
+
t = options.key?('type_of_id') ? options['type_of_id'] : 'bigint'
|
20
|
+
RailsNewsfeed::Connection.exec_cql("DROP TABLE IF EXISTS #{file_name}")
|
21
|
+
RailsNewsfeed::Connection.exec_cql("CREATE TABLE #{file_name}
|
22
|
+
(id #{t}, activity_id uuid, activity_content text, activity_object text, activity_time timestamp,
|
23
|
+
PRIMARY KEY ((id), activity_id)) WITH CLUSTERING ORDER BY (activity_id DESC)")
|
24
|
+
RailsNewsfeed::Connection.exec_cql("INSERT INTO #{RailsNewsfeed::FeedTable.table_name} (table_class)
|
25
|
+
VALUES ('#{class_name}')")
|
26
|
+
create_file "app/models/#{file_name}.rb", <<-FILE
|
27
|
+
class #{class_name} < RailsNewsfeed::NewsfeedModel
|
28
|
+
type_of_id :#{t}
|
29
|
+
end
|
30
|
+
FILE
|
31
|
+
end
|
32
|
+
|
33
|
+
def revoke
|
34
|
+
RailsNewsfeed::Connection.exec_cql("DROP TABLE IF EXISTS #{file_name}")
|
35
|
+
RailsNewsfeed::Connection.exec_cql("DELETE FROM #{RailsNewsfeed::FeedTable.table_name}
|
36
|
+
WHERE table_class='#{class_name}'")
|
37
|
+
@behavior = :invoke
|
38
|
+
remove_file "app/models/#{file_name}.rb"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# reads more options from http://datastax.github.io/ruby-driver/api/#cluster-class_method
|
2
|
+
<% app_name = RailsNewsfeed::Railtie.app_name %>
|
3
|
+
default: &default
|
4
|
+
hosts:
|
5
|
+
- '127.0.0.1'
|
6
|
+
port: 9042
|
7
|
+
|
8
|
+
development:
|
9
|
+
<<: *default
|
10
|
+
keyspace: <%= app_name %>_dev
|
11
|
+
|
12
|
+
test:
|
13
|
+
<<: *default
|
14
|
+
keyspace: <%= app_name %>_test
|
15
|
+
|
16
|
+
production:
|
17
|
+
<<: *default
|
18
|
+
keyspace: <%= app_name %>
|
19
|
+
username: 'cassandra'
|
20
|
+
password: 'cassandra'
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'cassandra'
|
2
|
+
require 'rails_newsfeed/version'
|
3
|
+
require 'rails_newsfeed/connection'
|
4
|
+
require 'rails_newsfeed/newsfeed_model'
|
5
|
+
require 'rails_newsfeed/activity'
|
6
|
+
require 'rails_newsfeed/relation'
|
7
|
+
require 'rails_newsfeed/feed_table'
|
8
|
+
|
9
|
+
module RailsNewsfeed
|
10
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
module RailsNewsfeed
|
2
|
+
class Activity
|
3
|
+
attr_reader :id
|
4
|
+
attr_accessor :content
|
5
|
+
attr_accessor :object
|
6
|
+
attr_accessor :time
|
7
|
+
attr_reader :new_record
|
8
|
+
|
9
|
+
# gets table name
|
10
|
+
def self.table_name
|
11
|
+
'activity'
|
12
|
+
end
|
13
|
+
|
14
|
+
# gets index table name
|
15
|
+
def self.index_table_name
|
16
|
+
"#{table_name}_index"
|
17
|
+
end
|
18
|
+
|
19
|
+
# gets schema
|
20
|
+
# DO NOT override this method unless you know what you are doing
|
21
|
+
def self.schema
|
22
|
+
{ id: :uuid, content: :text, object: :text, time: :timestamp }
|
23
|
+
end
|
24
|
+
|
25
|
+
# gets all activities
|
26
|
+
def self.all(opt = {})
|
27
|
+
rs = Connection.select(table_name, schema, '*') if opt.empty?
|
28
|
+
rs = Connection.select(index_table_name, schema, '*', object: opt[:object]) if opt.key?(:object)
|
29
|
+
acts = []
|
30
|
+
rs.each { |r| acts.push(create_from_cass(:act, r)) } if rs
|
31
|
+
acts
|
32
|
+
end
|
33
|
+
|
34
|
+
# creates activity
|
35
|
+
def self.create(opt = {})
|
36
|
+
act = new(opt)
|
37
|
+
return nil unless act.save
|
38
|
+
act
|
39
|
+
end
|
40
|
+
|
41
|
+
# creates without failling
|
42
|
+
def self.create!(opt = {})
|
43
|
+
create(opt)
|
44
|
+
rescue
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
|
48
|
+
# finds activity by id
|
49
|
+
def self.find(id)
|
50
|
+
r = Connection.select(table_name, schema, '*', { id: id }, page_size: 1).first
|
51
|
+
return nil unless r
|
52
|
+
create_from_cass(:act, r)
|
53
|
+
end
|
54
|
+
|
55
|
+
# finds without failling
|
56
|
+
def self.find!(id)
|
57
|
+
find(id)
|
58
|
+
rescue
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
|
62
|
+
# deletes activities
|
63
|
+
def self.delete(opt = {}, show_last = true)
|
64
|
+
if opt.key?(:id)
|
65
|
+
act = find(opt[:id])
|
66
|
+
return true unless act
|
67
|
+
return act.delete(show_last)
|
68
|
+
elsif opt.key?(:object)
|
69
|
+
all(object: opt[:object]).each { |a| a.delete(false) }
|
70
|
+
return true
|
71
|
+
else
|
72
|
+
# truncates activity table and feed tables
|
73
|
+
Connection.exec_cql("TRUNCATE #{table_name}")
|
74
|
+
Connection.exec_cql("TRUNCATE #{index_table_name}")
|
75
|
+
FeedTable.all.each { |r| Connection.exec_cql("TRUNCATE #{r.class.table_name}") }
|
76
|
+
return true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# deletes without failling
|
81
|
+
def self.delete!(opt = {}, show_last = true)
|
82
|
+
delete(opt, show_last)
|
83
|
+
rescue
|
84
|
+
false
|
85
|
+
end
|
86
|
+
|
87
|
+
# creates from feed cassandra
|
88
|
+
def self.create_from_cass(type, res)
|
89
|
+
h = {}
|
90
|
+
temp = type == :act ? '' : 'activity_'
|
91
|
+
schema.keys.each do |k|
|
92
|
+
key = "#{temp}#{k}"
|
93
|
+
val = k == :id || k == :time ? res[key].to_s : res[key]
|
94
|
+
h[k] = val
|
95
|
+
end
|
96
|
+
h[:new_record] = false
|
97
|
+
new(h)
|
98
|
+
end
|
99
|
+
|
100
|
+
# initializes
|
101
|
+
def initialize(options = {})
|
102
|
+
@id = options.key?(:id) ? options[:id] : nil
|
103
|
+
@content = options.key?(:content) ? options[:content] : nil
|
104
|
+
@object = options.key?(:object) ? options[:object] : nil
|
105
|
+
@time = options.key?(:time) ? options[:time] : nil
|
106
|
+
@new_record = options.key?(:new_record) ? options[:new_record] : true
|
107
|
+
end
|
108
|
+
|
109
|
+
# saves
|
110
|
+
def save
|
111
|
+
return insert if @new_record
|
112
|
+
update
|
113
|
+
end
|
114
|
+
|
115
|
+
# saves without failling
|
116
|
+
def save!
|
117
|
+
save
|
118
|
+
rescue
|
119
|
+
false
|
120
|
+
end
|
121
|
+
|
122
|
+
# deletes including activities from feed tables
|
123
|
+
def delete(show_last = true)
|
124
|
+
return false if @new_record
|
125
|
+
Connection.delete(self.class.table_name, self.class.schema, id: @id)
|
126
|
+
Connection.delete(self.class.index_table_name, self.class.schema, object: @object, id: @id) if @object
|
127
|
+
return delete_from_feed(@id, nil) unless show_last
|
128
|
+
delete_from_feed(@id, last)
|
129
|
+
end
|
130
|
+
|
131
|
+
# deletes without failling
|
132
|
+
def delete!(show_last = true)
|
133
|
+
delete(show_last)
|
134
|
+
rescue
|
135
|
+
false
|
136
|
+
end
|
137
|
+
|
138
|
+
# converts to hash
|
139
|
+
def to_h(prefix = nil)
|
140
|
+
{ "#{prefix}id".to_sym => @id, "#{prefix}content".to_sym => @content,
|
141
|
+
"#{prefix}object".to_sym => @object, "#{prefix}time".to_sym => @time }
|
142
|
+
end
|
143
|
+
|
144
|
+
protected
|
145
|
+
|
146
|
+
# inserts
|
147
|
+
def insert
|
148
|
+
return false unless @content
|
149
|
+
@id ||= Cassandra::Uuid::Generator.new.now.to_s
|
150
|
+
@time ||= Cassandra::Types::Timestamp.new(DateTime.current).to_s
|
151
|
+
Connection.insert(self.class.table_name, self.class.schema, to_h)
|
152
|
+
Connection.insert(self.class.index_table_name, self.class.schema, to_h) unless @object.nil?
|
153
|
+
@new_record = false
|
154
|
+
true
|
155
|
+
end
|
156
|
+
|
157
|
+
# updates
|
158
|
+
def update
|
159
|
+
Connection.update(self.class.table_name, self.class.schema, { id: @id }, to_h)
|
160
|
+
# updates from all feed models
|
161
|
+
cqls = []
|
162
|
+
FeedTable.all.each do |i|
|
163
|
+
i_tbl = i.class.table_name
|
164
|
+
i_schema = i.class.schema
|
165
|
+
Connection.select(i_tbl, i_schema, '*', { activity_id: @id }, filtering: true).each do |r|
|
166
|
+
cqls.push(Connection.update(i_tbl, i_schema, { id: r['id'], activity_id: @id }, to_h('activity_'), true))
|
167
|
+
end
|
168
|
+
end
|
169
|
+
Connection.batch_cqls(cqls)
|
170
|
+
true
|
171
|
+
end
|
172
|
+
|
173
|
+
# deletes from all feed tables
|
174
|
+
def delete_from_feed(id, last = nil)
|
175
|
+
cqls = []
|
176
|
+
FeedTable.all.each do |i|
|
177
|
+
i_tbl = i.class.table_name
|
178
|
+
i_schema = i.class.schema
|
179
|
+
Connection.select(i_tbl, i_schema, '*', { activity_id: id }, filtering: true).each do |r|
|
180
|
+
cqls.push(Connection.delete(i_tbl, i_schema, { id: r['id'], activity_id: r['activity_id'].to_s }, true))
|
181
|
+
next unless last
|
182
|
+
cqls.push(Connection.insert(i_tbl, i_schema, NewsfeedModel.from_cass_act(r['id'], last), true))
|
183
|
+
end
|
184
|
+
end
|
185
|
+
Connection.batch_cqls(cqls.uniq)
|
186
|
+
true
|
187
|
+
end
|
188
|
+
|
189
|
+
# gets last activity of object
|
190
|
+
def last
|
191
|
+
return nil unless @object
|
192
|
+
Connection.select(self.class.index_table_name, self.class.schema, '*', object: @object).first
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module RailsNewsfeed
|
2
|
+
class Connection
|
3
|
+
class << self
|
4
|
+
private
|
5
|
+
|
6
|
+
# exports a value to cassandra value
|
7
|
+
def cass_val(val, type)
|
8
|
+
return 'null' if val.nil?
|
9
|
+
case type
|
10
|
+
when :ascii, :text, :varchar, :timestamp
|
11
|
+
return "'#{val.to_s.gsub("'", "''")}'"
|
12
|
+
else
|
13
|
+
val
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# exports values to cassandra values
|
18
|
+
def cass_vals(schema, vals)
|
19
|
+
cass_vals = []
|
20
|
+
vals.each do |col, val|
|
21
|
+
cass_vals.push(cass_val(val, schema[col]))
|
22
|
+
end
|
23
|
+
cass_vals.join(',')
|
24
|
+
end
|
25
|
+
|
26
|
+
# exports column value pair
|
27
|
+
def exported_col_val(schema, col_val_pair)
|
28
|
+
a = []
|
29
|
+
col_val_pair.each do |col, val|
|
30
|
+
a.push("#{col}=#{cass_val(val, schema[col])}")
|
31
|
+
end
|
32
|
+
a
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# gets config
|
37
|
+
def self.config
|
38
|
+
YAML.load_file('config/cassandra.yml')[Rails.env]
|
39
|
+
end
|
40
|
+
|
41
|
+
# gets cassandra connection
|
42
|
+
def self.connection
|
43
|
+
return @connection if @connection
|
44
|
+
cfg = config
|
45
|
+
@connection = Cassandra.cluster(cfg || {}).connect(cfg['keyspace'])
|
46
|
+
end
|
47
|
+
|
48
|
+
# executes cql
|
49
|
+
def self.exec_cql(cql, options = {})
|
50
|
+
connection.execute(cql, options)
|
51
|
+
end
|
52
|
+
|
53
|
+
# executes batch
|
54
|
+
def self.batch_cqls(cqls, options = {})
|
55
|
+
return true if cqls.empty?
|
56
|
+
batch = connection.batch do |b|
|
57
|
+
cqls.each do |cql|
|
58
|
+
b.add(cql)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
exec_cql(batch, options)
|
62
|
+
end
|
63
|
+
|
64
|
+
# inserts
|
65
|
+
def self.insert(tbl, schema, vals, to_cql = false)
|
66
|
+
cql = "INSERT INTO #{tbl} (#{vals.keys.join(',')}) VALUES (#{cass_vals(schema, vals)})"
|
67
|
+
return cql if to_cql
|
68
|
+
exec_cql(cql)
|
69
|
+
true
|
70
|
+
end
|
71
|
+
|
72
|
+
# updates
|
73
|
+
def self.update(tbl, schema, conditions, vals, to_cql = false)
|
74
|
+
# unsets primary keys
|
75
|
+
conditions.keys.each { |k| vals.delete(k) }
|
76
|
+
val_s = exported_col_val(schema, vals).join(',')
|
77
|
+
cql = "UPDATE #{tbl} SET #{val_s} WHERE #{exported_col_val(schema, conditions).join(' AND ')}"
|
78
|
+
return cql if to_cql
|
79
|
+
exec_cql(cql)
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
83
|
+
# deletes
|
84
|
+
def self.delete(tbl, schema, conditions, to_cql = false)
|
85
|
+
cql = "DELETE FROM #{tbl} WHERE #{exported_col_val(schema, conditions).join(' AND ')}"
|
86
|
+
return cql if to_cql
|
87
|
+
exec_cql(cql)
|
88
|
+
true
|
89
|
+
end
|
90
|
+
|
91
|
+
# selects
|
92
|
+
def self.select(tbl, schema = {}, columns = '*', conditions = {}, options = {})
|
93
|
+
cql = 'SELECT '
|
94
|
+
cql += columns unless columns.is_a?(Array)
|
95
|
+
cql += columns.join(',') if columns.is_a?(Array)
|
96
|
+
cql += " FROM #{tbl}"
|
97
|
+
cql += " WHERE #{exported_col_val(schema, conditions).join(' AND ')}" unless conditions.empty?
|
98
|
+
if options[:filtering]
|
99
|
+
cql += ' ALLOW FILTERING'
|
100
|
+
options.delete(:filtering)
|
101
|
+
end
|
102
|
+
exec_cql(cql, options)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module RailsNewsfeed
|
2
|
+
class FeedTable
|
3
|
+
# gets table name
|
4
|
+
def self.table_name
|
5
|
+
'feed_table'
|
6
|
+
end
|
7
|
+
|
8
|
+
# gets schema
|
9
|
+
# DO NOT override this method unless you know what you are doing
|
10
|
+
def self.schema
|
11
|
+
{ table_class: :text }
|
12
|
+
end
|
13
|
+
|
14
|
+
# adds table
|
15
|
+
def self.create(tbl_class)
|
16
|
+
Connection.insert(table_name, schema, table_class: tbl_class)
|
17
|
+
end
|
18
|
+
|
19
|
+
# removes table
|
20
|
+
def self.delete(tbl_class)
|
21
|
+
Connection.delete(table_name, schema, table_class: tbl_class)
|
22
|
+
end
|
23
|
+
|
24
|
+
# gets all feed tables
|
25
|
+
def self.all
|
26
|
+
items = []
|
27
|
+
Connection.select(table_name).each do |r|
|
28
|
+
cons = r['table_class'].safe_constantize
|
29
|
+
next unless cons
|
30
|
+
ins = cons.new
|
31
|
+
items.push(ins) if ins
|
32
|
+
end
|
33
|
+
items
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
module RailsNewsfeed
|
2
|
+
class NewsfeedModel
|
3
|
+
SALT_KEY = 'fQEEumGosuS92VcVVMPm'.freeze
|
4
|
+
SECRET_KEY = 'sL6PySf7Ijo8L0ZhU7R2'.freeze
|
5
|
+
|
6
|
+
class_attribute :id_type, instance_writer: false
|
7
|
+
self.id_type = :bigint
|
8
|
+
|
9
|
+
attr_reader :id
|
10
|
+
attr_reader :next_page_token
|
11
|
+
|
12
|
+
# sets type of id
|
13
|
+
def self.type_of_id(type)
|
14
|
+
self.id_type = type
|
15
|
+
end
|
16
|
+
|
17
|
+
# gets table name
|
18
|
+
def self.table_name
|
19
|
+
name.demodulize.underscore
|
20
|
+
end
|
21
|
+
|
22
|
+
# gets schema
|
23
|
+
# DO NOT override this method unless you know what you are doing
|
24
|
+
def self.schema
|
25
|
+
{ id: id_type, activity_id: :uuid, activity_content: :text, activity_object: :text, activity_time: :timestamp }
|
26
|
+
end
|
27
|
+
|
28
|
+
# inserts
|
29
|
+
def self.insert(id, activity, related = true, hide_old = true)
|
30
|
+
new(id: id).insert(activity, related, hide_old)
|
31
|
+
end
|
32
|
+
|
33
|
+
# deletes by id
|
34
|
+
def self.delete(id, act_id = nil, related = false)
|
35
|
+
new(id: id).delete(act_id, related)
|
36
|
+
end
|
37
|
+
|
38
|
+
# gets feeds by id
|
39
|
+
def self.feeds(id, page_size = 10, next_page_token = nil)
|
40
|
+
new(id: id, next_page_token: next_page_token).feeds(page_size)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.from_cass_act(id, res)
|
44
|
+
{ id: id, activity_id: res['id'].to_s, activity_content: res['content'],
|
45
|
+
activity_object: res['object'], activity_time: res['time'].to_s }
|
46
|
+
end
|
47
|
+
|
48
|
+
# initializes
|
49
|
+
def initialize(options = {})
|
50
|
+
@id = options.key?(:id) ? options[:id] : nil
|
51
|
+
@next_page_token = options.key?(:next_page_token) ? options[:next_page_token] : nil
|
52
|
+
end
|
53
|
+
|
54
|
+
# inserts an activity into table
|
55
|
+
def insert(activity, related = true, hide_old = true)
|
56
|
+
record = { id: @id, activity_id: activity.id, activity_content: activity.content,
|
57
|
+
activity_object: activity.object, activity_time: activity.time }
|
58
|
+
Connection.insert(self.class.table_name, self.class.schema, record)
|
59
|
+
ins_arr = []
|
60
|
+
cqls = []
|
61
|
+
if related
|
62
|
+
Relation.related_of(self).each do |ins|
|
63
|
+
record[:id] = ins.id
|
64
|
+
cqls.push(Connection.insert(ins.class.table_name, ins.class.schema, record, true))
|
65
|
+
ins_arr.push(ins) if hide_old
|
66
|
+
end
|
67
|
+
end
|
68
|
+
if hide_old
|
69
|
+
ins_arr.push(self)
|
70
|
+
cqls |= cql_hide_old_feeds_of(activity, ins_arr)
|
71
|
+
end
|
72
|
+
Connection.batch_cqls(cqls)
|
73
|
+
true
|
74
|
+
end
|
75
|
+
|
76
|
+
# deletes an activity or all activities from this feed
|
77
|
+
def delete(act_id = nil, related = true)
|
78
|
+
tbl = self.class.table_name
|
79
|
+
schema = self.class.schema
|
80
|
+
if act_id
|
81
|
+
return Activity.delete(act_id, true) if related
|
82
|
+
return Connection.delete(tbl, schema, id: @id, activity_id: act_id)
|
83
|
+
end
|
84
|
+
cqls = []
|
85
|
+
Connection.select(tbl, schema, '*', id: @id).each do |r|
|
86
|
+
if related
|
87
|
+
Activity.delete(r['id'].to_s, true)
|
88
|
+
else
|
89
|
+
cqls.push(Connection.delete(tbl, schema, { id: @id, activity_id: r['id'].to_s }, true))
|
90
|
+
end
|
91
|
+
end
|
92
|
+
Connection.batch_cqls(cqls.uniq)
|
93
|
+
true
|
94
|
+
end
|
95
|
+
|
96
|
+
# gets feeds
|
97
|
+
def feeds(page_size = 10)
|
98
|
+
@feeds = []
|
99
|
+
options = { page_size: page_size.to_i }
|
100
|
+
options[:paging_state] = decoded_next_page_token if @next_page_token
|
101
|
+
result = Connection.select(self.class.table_name, self.class.schema, '*', { id: @id }, options)
|
102
|
+
result.each { |r| @feeds.push(Activity.create_from_cass(:feed, r)) }
|
103
|
+
encoded_next_page_token(result)
|
104
|
+
after_feeds
|
105
|
+
@feeds
|
106
|
+
end
|
107
|
+
|
108
|
+
# overrides this method to implement your own
|
109
|
+
def after_feeds
|
110
|
+
end
|
111
|
+
|
112
|
+
# registers to another
|
113
|
+
def register(to, options = {})
|
114
|
+
Relation.create(to, self, options)
|
115
|
+
end
|
116
|
+
|
117
|
+
# deregisters to another
|
118
|
+
def deregister(to, options = {})
|
119
|
+
Relation.delete(to, self, options)
|
120
|
+
end
|
121
|
+
|
122
|
+
# checks related
|
123
|
+
def register?(to)
|
124
|
+
Relation.related?(to, self)
|
125
|
+
end
|
126
|
+
|
127
|
+
protected
|
128
|
+
|
129
|
+
# encodes next_page_token
|
130
|
+
def encoded_next_page_token(result)
|
131
|
+
if result.nil? || result.last_page?
|
132
|
+
@next_page_token = nil
|
133
|
+
else
|
134
|
+
key = ActiveSupport::KeyGenerator.new(SECRET_KEY).generate_key(SALT_KEY)
|
135
|
+
@next_page_token = ActiveSupport::MessageEncryptor.new(key).encrypt_and_sign(result.paging_state)
|
136
|
+
end
|
137
|
+
@next_page_token
|
138
|
+
end
|
139
|
+
|
140
|
+
# decodes next_page_token
|
141
|
+
def decoded_next_page_token
|
142
|
+
key = ActiveSupport::KeyGenerator.new(SECRET_KEY).generate_key(SALT_KEY)
|
143
|
+
ActiveSupport::MessageEncryptor.new(key).decrypt_and_verify(@next_page_token)
|
144
|
+
end
|
145
|
+
|
146
|
+
# generates cqls to hide old feeds
|
147
|
+
def cql_hide_old_feeds_of(activity, ins_arr)
|
148
|
+
return [] unless activity.object
|
149
|
+
cqls = []
|
150
|
+
cond = { object: activity.object }
|
151
|
+
Connection.select(activity.class.index_table_name, activity.class.schema, '*', cond).each do |r|
|
152
|
+
id = r['id'].to_s
|
153
|
+
next if id == activity.id
|
154
|
+
ins_arr.each do |t|
|
155
|
+
cqls.push(Connection.delete(t.class.table_name, t.class.schema, { id: t.id, activity_id: id }, true))
|
156
|
+
end
|
157
|
+
end
|
158
|
+
cqls.uniq
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module RailsNewsfeed
|
2
|
+
class Relation
|
3
|
+
# gets table name
|
4
|
+
def self.table_name
|
5
|
+
'relation'
|
6
|
+
end
|
7
|
+
|
8
|
+
# gets index table name
|
9
|
+
def self.index_table_name
|
10
|
+
"#{table_name}_index"
|
11
|
+
end
|
12
|
+
|
13
|
+
# gets schema
|
14
|
+
# DO NOT override this method unless you know what you are doing
|
15
|
+
def self.schema
|
16
|
+
{ id: :uuid, from_class: :text, from_id: :text, to_class: :text, to_id: :text }
|
17
|
+
end
|
18
|
+
|
19
|
+
# creates relations between two objects
|
20
|
+
def self.create(from, to, options = {})
|
21
|
+
id = Cassandra::Uuid::Generator.new.now.to_s
|
22
|
+
record = { id: id, from_class: from.class.name, from_id: from.id, to_class: to.class.name, to_id: to.id }
|
23
|
+
return false unless Connection.insert(table_name, schema, record)
|
24
|
+
Connection.insert(index_table_name, schema, record)
|
25
|
+
return true unless options.key?(:side) && options[:side] == :both
|
26
|
+
create(to, from)
|
27
|
+
end
|
28
|
+
|
29
|
+
# deletes relations between two objects
|
30
|
+
def self.delete(from, to, options = {})
|
31
|
+
cond = { from_class: from.class.name, from_id: from.id, to_class: to.class.name, to_id: to.id }
|
32
|
+
i = Connection.select(index_table_name, schema, '*', cond).first
|
33
|
+
if i
|
34
|
+
Connection.delete(table_name, schema, from_class: from.class.name, from_id: from.id, id: i['id'].to_s)
|
35
|
+
Connection.delete(index_table_name, schema, cond)
|
36
|
+
end
|
37
|
+
return true unless options.key?(:side) && options[:side] == :both
|
38
|
+
delete(to, from)
|
39
|
+
end
|
40
|
+
|
41
|
+
# gets relateds of object
|
42
|
+
def self.related_of(from)
|
43
|
+
relateds = []
|
44
|
+
result = Connection.select(table_name, schema, '*', from_class: from.class.name, from_id: from.id)
|
45
|
+
result.each do |r|
|
46
|
+
cons = r['to_class'].safe_constantize
|
47
|
+
next unless cons
|
48
|
+
ins = cons.new(id: r['to_id'])
|
49
|
+
relateds.push(ins) if ins
|
50
|
+
end
|
51
|
+
relateds
|
52
|
+
end
|
53
|
+
|
54
|
+
# checks is related
|
55
|
+
def self.related?(from, to)
|
56
|
+
cond = { from_class: from.class.name, from_id: from.id, to_class: to.class.name, to_id: to.id }
|
57
|
+
i = Connection.select(index_table_name, schema, '*', cond).first
|
58
|
+
return false unless i
|
59
|
+
cond = { from_class: from.class.name, from_id: from.id, id: i['id'].to_s }
|
60
|
+
return false unless Connection.select(table_name, schema, '*', cond).first
|
61
|
+
true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rails_newsfeed/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'rails_newsfeed'
|
8
|
+
spec.version = RailsNewsfeed::VERSION
|
9
|
+
spec.authors = ['Adam Nguyen']
|
10
|
+
spec.email = ['adamnguyen.itdn@gmail.com']
|
11
|
+
spec.summary = 'News Feed module for ruby'
|
12
|
+
spec.description = 'News Feed module for ruby'
|
13
|
+
spec.homepage = 'https://github.com/adamnguyenit/rails_newsfeed'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
16
|
+
spec.bindir = 'exe'
|
17
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
spec.required_ruby_version = '>= 2.0'
|
20
|
+
spec.add_dependency 'rails', '>= 4.0.0'
|
21
|
+
spec.add_dependency 'cassandra-driver', '~> 3.0.0.rc'
|
22
|
+
spec.add_development_dependency 'bundler'
|
23
|
+
spec.add_development_dependency 'rake'
|
24
|
+
spec.add_development_dependency 'rspec', '~> 3.4'
|
25
|
+
spec.add_development_dependency 'rspec-collection_matchers'
|
26
|
+
spec.add_development_dependency 'factory_girl', '~> 4.5'
|
27
|
+
spec.add_development_dependency 'faker', '~> 1.6'
|
28
|
+
spec.add_development_dependency 'rubocop'
|
29
|
+
spec.add_development_dependency 'coveralls'
|
30
|
+
spec.add_development_dependency 'codeclimate-test-reporter'
|
31
|
+
end
|
data/tasks/rspec.rake
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
CREATE KEYSPACE rails_newsfeed_test WITH REPLICATION = { 'class': 'SimpleStrategy', 'replication_factor': 3 };
|
2
|
+
USE rails_newsfeed_test;
|
3
|
+
CREATE TABLE activity (id uuid, content text, time timestamp, object text, PRIMARY KEY (id));
|
4
|
+
CREATE TABLE activity_index (id uuid, content text, time timestamp, object text, PRIMARY KEY ((object), id));
|
5
|
+
CREATE TABLE feed_table (table_class text, PRIMARY KEY (table_class));
|
6
|
+
CREATE TABLE relation (id uuid, from_class text, from_id text, to_class text, to_id text, PRIMARY KEY ((from_class, from_id), id));
|
7
|
+
CREATE TABLE relation_index(id uuid, from_class text, from_id text, to_class text, to_id text, PRIMARY KEY ((from_class, from_id, to_class, to_id)));
|
8
|
+
CREATE TABLE user_feed (id bigint, activity_id uuid, activity_content text, activity_object text, activity_time timestamp, PRIMARY KEY ((id), activity_id));
|
9
|
+
INSERT INTO feed_table (table_class) VALUES ('UserFeed');
|
@@ -0,0 +1 @@
|
|
1
|
+
DROP KEYSPACE rails_newsfeed_test;
|
metadata
ADDED
@@ -0,0 +1,227 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails_newsfeed
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Adam Nguyen
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-03-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 4.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: cassandra-driver
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.0.0.rc
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 3.0.0.rc
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.4'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.4'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec-collection_matchers
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: factory_girl
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '4.5'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '4.5'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: faker
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.6'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '1.6'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rubocop
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: coveralls
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: codeclimate-test-reporter
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
description: News Feed module for ruby
|
168
|
+
email:
|
169
|
+
- adamnguyen.itdn@gmail.com
|
170
|
+
executables: []
|
171
|
+
extensions: []
|
172
|
+
extra_rdoc_files: []
|
173
|
+
files:
|
174
|
+
- ".codeclimate.yml"
|
175
|
+
- ".gitignore"
|
176
|
+
- ".rspec"
|
177
|
+
- ".travis.yml"
|
178
|
+
- CODE_OF_CONDUCT.md
|
179
|
+
- Gemfile
|
180
|
+
- LICENSE
|
181
|
+
- LICENSE.txt
|
182
|
+
- README.md
|
183
|
+
- Rakefile
|
184
|
+
- bin/console
|
185
|
+
- bin/setup
|
186
|
+
- config/cassandra.yml
|
187
|
+
- lib/generators/rails_newsfeed/config_generator.rb
|
188
|
+
- lib/generators/rails_newsfeed/init_generator.rb
|
189
|
+
- lib/generators/rails_newsfeed/model_generator.rb
|
190
|
+
- lib/generators/rails_newsfeed/templates/config/cassandra.yml
|
191
|
+
- lib/rails_newsfeed.rb
|
192
|
+
- lib/rails_newsfeed/activity.rb
|
193
|
+
- lib/rails_newsfeed/connection.rb
|
194
|
+
- lib/rails_newsfeed/feed_table.rb
|
195
|
+
- lib/rails_newsfeed/newsfeed_model.rb
|
196
|
+
- lib/rails_newsfeed/railtie.rb
|
197
|
+
- lib/rails_newsfeed/relation.rb
|
198
|
+
- lib/rails_newsfeed/version.rb
|
199
|
+
- rails_newsfeed.gemspec
|
200
|
+
- tasks/rspec.rake
|
201
|
+
- travis/create-schema.cql
|
202
|
+
- travis/drop-schema.cql
|
203
|
+
homepage: https://github.com/adamnguyenit/rails_newsfeed
|
204
|
+
licenses:
|
205
|
+
- MIT
|
206
|
+
metadata: {}
|
207
|
+
post_install_message:
|
208
|
+
rdoc_options: []
|
209
|
+
require_paths:
|
210
|
+
- lib
|
211
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - ">="
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: '2.0'
|
216
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
217
|
+
requirements:
|
218
|
+
- - ">="
|
219
|
+
- !ruby/object:Gem::Version
|
220
|
+
version: '0'
|
221
|
+
requirements: []
|
222
|
+
rubyforge_project:
|
223
|
+
rubygems_version: 2.4.5.1
|
224
|
+
signing_key:
|
225
|
+
specification_version: 4
|
226
|
+
summary: News Feed module for ruby
|
227
|
+
test_files: []
|