ten_cubed 0.1.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 46b0d9afa96923136e1b0a005ce47f86518d3cf6bb9d527881df2df1157fd800
4
+ data.tar.gz: 25c8497f22e9f5576d3a458f1aa7151d9f8b4355be9005b111422fc4520212e5
5
+ SHA512:
6
+ metadata.gz: ed86c62a478218f8470926cc3683a79ddda901df554e2b72dda52bcf5b325518dc7ca6f9cec27b22bfbeb90f742fd278a1d6340528902deedf4e459af8f3558e
7
+ data.tar.gz: 6304479ac2398459ec173b3e7cb439c9ff74657413b1ac8cee60385c2e5f545e25e161edcdbb656701ab221ae21b2c8da2a867a847b50f39189463d816f4bc47
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.standard.yml ADDED
@@ -0,0 +1,7 @@
1
+ # For available configuration options, see:
2
+ # https://github.com/standardrb/standard
3
+ ruby_version: 3.3
4
+
5
+ # Exclude the template files from standardrb checks
6
+ ignore:
7
+ - 'lib/generators/**/templates/**'
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2025-03-08
4
+
5
+ - Initial release to public
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Sebastian Wildwood
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,202 @@
1
+ # ten_cubed
2
+ [![Ruby Gem CI](https://github.com/darkpicnic/ten_cubed/actions/workflows/main.yml/badge.svg)](https://github.com/darkpicnic/ten_cubed/actions/workflows/main.yml)
3
+
4
+ A Ruby on Rails gem implementing the ten_cubed networking system - an artificially restricted social graph that limits users to 10 direct connections with a maximum network size of 1,110 total connections (10 + 100 + 1000).
5
+
6
+ The ten_cubed system promotes healthier social interactions by imposing natural limits on connection growth, limiting viral content spread, and eliminating influencer dynamics.
7
+
8
+ ## Disclosure of AI generated content
9
+ The core functionality of ten_cubed was written by myself, however, this gem was almost entirely AI generated through Cursor + Claude 3.7. I've added specific strings throughout the project to indicate when files were entirely written by AI. There is almost certainly some head scratchers in this repository; feel free to submit PRs if something seems really off.
10
+
11
+ ## Project Status
12
+
13
+ ⚠️ **ACTIVE DEVELOPMENT** ⚠️
14
+
15
+ This project is currently in active development. APIs and functionality may change without notice. Breaking changes are possible between versions. While we encourage testing and contributions, this gem is not yet recommended for production environments without thorough evaluation.
16
+
17
+ ## Requirements
18
+
19
+ * Ruby 3.2+
20
+ * Rails 7.0+
21
+ * PostgreSQL database (required for the recursive CTE queries used by the gem)
22
+
23
+ ## About
24
+
25
+ The ten_cubed networking system is based on three key concepts:
26
+
27
+ 1. Users can have no more than 10 connections in their immediate network
28
+ 2. Users have three levels of their network (1st, 2nd, and 3rd degree) with a theoretical maximum of 1,110 total connections
29
+ 3. Users can set the maximum degree allowed for different activities within your app
30
+
31
+ For more details on the ten_cubed concept, see: [https://highlyprobable.io/articles/ten-cubed](https://highlyprobable.io/articles/ten-cubed)
32
+
33
+ ## Installation
34
+
35
+ Add this line to your application's Gemfile:
36
+
37
+ ```ruby
38
+ gem 'ten_cubed', github: "darkpicnic/ten_cubed"
39
+ ```
40
+
41
+ And then execute:
42
+
43
+ ```bash
44
+ $ bundle install
45
+ ```
46
+
47
+ Then run the installer:
48
+
49
+ ```bash
50
+ $ rails generate ten_cubed:install
51
+ ```
52
+
53
+ The installer will:
54
+ - Create an initializer at `config/initializers/ten_cubed.rb`
55
+ - Check if a User model exists
56
+ - If it exists: Add a migration for the `max_degree` column and inject the TenCubed concern
57
+ - If it doesn't exist: Generate a new User model with TenCubed functionality
58
+ - Check if a Connection model exists
59
+ - If it exists: Raise an error (you need to rename or remove the existing model)
60
+ - If it doesn't exist: Generate the Connection model for TenCubed
61
+ - Install all required migrations
62
+
63
+ After running the installer, don't forget to run:
64
+
65
+ ```bash
66
+ $ rails db:migrate
67
+ ```
68
+
69
+ ### Manual Setup (Alternative)
70
+
71
+ If you prefer to set up the components individually:
72
+
73
+ ```bash
74
+ # Generate the User model (if you don't already have one)
75
+ $ rails generate ten_cubed:user
76
+
77
+ # Generate the Connection model
78
+ $ rails generate ten_cubed:connection
79
+
80
+ # Run migrations
81
+ $ rails db:migrate
82
+ ```
83
+
84
+ ## Usage
85
+
86
+ ### Configuration
87
+
88
+ You can configure TenCubed in the initializer file at `config/initializers/ten_cubed.rb`:
89
+
90
+ ```ruby
91
+ TenCubed.configure do |config|
92
+ # Maximum number of direct connections a user can have
93
+ # Default: 10
94
+ config.max_direct_connections = 10
95
+
96
+ # Maximum network depth for querying connections
97
+ # Default: 3
98
+ config.max_network_depth = 3
99
+
100
+ # Table name for connections
101
+ # Default: :connections
102
+ config.connection_table_name = :connections
103
+ end
104
+ ```
105
+
106
+ ### User Model
107
+
108
+ If you already have a User model, the `ten_cubed:install` generator will add the `max_degree` column to your users table automatically. If you create a User model with the `ten_cubed:user` generator, the model will include the TenCubed functionality by default.
109
+
110
+ User models will have the following methods:
111
+
112
+ #### `my_network`
113
+
114
+ Returns the user's network up to their `max_degree` setting.
115
+
116
+ ```ruby
117
+ current_user.my_network
118
+ # => [<User>, <User>, ...] (array of users in the network)
119
+ ```
120
+
121
+ #### `degree_of_connection(user)`
122
+
123
+ Returns the degree of connection between the current user and another user.
124
+
125
+ ```ruby
126
+ current_user.degree_of_connection(other_user)
127
+ # => 1 (direct connection)
128
+ # => 2 (friend of friend)
129
+ # => 3 (third-degree connection)
130
+ # => nil (not connected)
131
+ ```
132
+
133
+ #### `in_network?(user)`
134
+
135
+ Checks if a user is in the current user's network.
136
+
137
+ ```ruby
138
+ current_user.in_network?(other_user)
139
+ # => true or false
140
+ ```
141
+
142
+ #### `network(max_depth = 3)`
143
+
144
+ Returns the user's network up to the specified depth (1-3).
145
+
146
+ ```ruby
147
+ current_user.network(2)
148
+ # => [<User>, <User>, ...] (users within 2 degrees)
149
+ ```
150
+
151
+ ### Connection Model
152
+
153
+ The `Connection` model enforces the ten_cubed constraints:
154
+
155
+ 1. A user cannot have more than 10 connections
156
+ 2. A user cannot connect to themselves
157
+
158
+ To create a connection between two users:
159
+
160
+ ```ruby
161
+ TenCubed::Connection.create(user: current_user, target: other_user)
162
+ ```
163
+
164
+ ### Example Usage
165
+
166
+ Here's an example of how you might use ten_cubed in a controller:
167
+
168
+ ```ruby
169
+ class PostsController < ApplicationController
170
+ def index
171
+ # Show posts only from users in the current user's network
172
+ @posts = Post.where(user_id: current_user.my_network.pluck(:id) + [current_user.id])
173
+ end
174
+
175
+ def show
176
+ @post = Post.find(params[:id])
177
+
178
+ # Check if the current user can access this post based on connection degree
179
+ unless current_user.id == @post.user_id ||
180
+ (current_user.in_network?(@post.user) &&
181
+ current_user.degree_of_connection(@post.user) <= @post.visibility_degree)
182
+ flash[:alert] = "You don't have permission to view this post"
183
+ redirect_to posts_path
184
+ end
185
+ end
186
+ end
187
+ ```
188
+
189
+ ## Development
190
+
191
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
192
+
193
+ To install this gem onto your local machine, run `bundle exec rake install`.
194
+
195
+ ## Contributing
196
+
197
+ Bug reports and pull requests are welcome on GitHub at https://github.com/darkpicnic/ten_cubed.
198
+
199
+ ## License
200
+
201
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
202
+
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ # Task specifically for testing generators
9
+ RSpec::Core::RakeTask.new(:generator_spec) do |t|
10
+ t.pattern = "spec/generators/**/*_spec.rb"
11
+ end
12
+
13
+ require "standard/rake"
14
+
15
+ task default: %i[spec standard]
16
+
17
+ # Run generator tests specifically
18
+ desc "Run generator tests"
19
+ task test_generators: :generator_spec
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Created by AI
4
+
5
+ module TenCubed
6
+ module Generators
7
+ class ConnectionGenerator < Rails::Generators::Base
8
+ include Rails::Generators::Migration
9
+ source_root File.expand_path("templates", __dir__)
10
+ desc "Create Connection model and migration for TenCubed"
11
+
12
+ # Required for Rails::Generators::Migration
13
+ def self.next_migration_number(dirname)
14
+ next_migration_number = current_migration_number(dirname) + 1
15
+ ActiveRecord::Migration.next_migration_number(next_migration_number)
16
+ end
17
+
18
+ def create_connection_model
19
+ template "connection.rb", "app/models/connection.rb"
20
+ end
21
+
22
+ def create_connection_migration
23
+ migration_template "create_connections.rb",
24
+ "db/migrate/create_connections.rb",
25
+ migration_version: migration_version
26
+ end
27
+
28
+ private
29
+
30
+ def migration_version
31
+ if Rails.version >= "5.0.0"
32
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Created by AI
4
+
5
+ class Connection < ApplicationRecord
6
+ belongs_to :user
7
+ belongs_to :target, class_name: "User"
8
+
9
+ validate :ensure_user_has_less_than_max_connections, on: :create
10
+ validate :ensure_target_is_not_self, on: :create
11
+
12
+ private
13
+
14
+ def ensure_user_has_less_than_max_connections
15
+ if user.connections.count >= 10
16
+ errors.add(:user, "can't have more than 10 connections")
17
+ end
18
+ end
19
+
20
+ def ensure_target_is_not_self
21
+ if user == target
22
+ errors.add(:target, "can't be the same as user")
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ # Created by AI
3
+
4
+ class CreateConnections < ActiveRecord::Migration[<%= migration_version %>]
5
+ def change
6
+ create_table :connections do |t|
7
+ t.references :user, null: false, foreign_key: true
8
+ t.references :target, null: false, foreign_key: { to_table: :users }
9
+
10
+ t.timestamps
11
+ end
12
+
13
+ add_index :connections, [:user_id, :target_id], unique: true
14
+ end
15
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Created by AI
4
+
5
+ module TenCubed
6
+ module Generators
7
+ class InstallGenerator < Rails::Generators::Base
8
+ include Rails::Generators::Migration
9
+ source_root File.expand_path("templates", __dir__)
10
+ desc "Install TenCubed into your Rails application"
11
+
12
+ # Required for Rails::Generators::Migration
13
+ def self.next_migration_number(dirname)
14
+ next_migration_number = current_migration_number(dirname) + 1
15
+ ActiveRecord::Migration.next_migration_number(next_migration_number)
16
+ end
17
+
18
+ def add_migrations
19
+ rails_command "railties:install:migrations FROM=ten_cubed", inline: true
20
+ end
21
+
22
+ def create_initializer
23
+ template "initializer.rb", "config/initializers/ten_cubed.rb"
24
+ end
25
+
26
+ def setup_user_model
27
+ if model_exists?("User")
28
+ add_max_degree_to_existing_user
29
+ inject_ten_cubed_user_concern
30
+ else
31
+ generate "ten_cubed:user"
32
+ end
33
+ end
34
+
35
+ def setup_connection_model
36
+ if model_exists?("Connection")
37
+ # Throw an error if Connection model already exists
38
+ raise "Connection model already exists. Please remove it or rename it before installing TenCubed."
39
+ else
40
+ generate "ten_cubed:connection"
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def model_exists?(model_name)
47
+ File.exist?(Rails.root.join("app", "models", "#{model_name.underscore}.rb"))
48
+ end
49
+
50
+ def add_max_degree_to_existing_user
51
+ migration_template "add_max_degree_to_users.rb",
52
+ "db/migrate/add_max_degree_to_users.rb",
53
+ migration_version: migration_version
54
+ end
55
+
56
+ def inject_ten_cubed_user_concern
57
+ inject_into_file "app/models/user.rb", after: "class User < ApplicationRecord\n" do
58
+ " include TenCubed::Models::Concerns::TenCubedUser\n"
59
+ end
60
+ end
61
+
62
+ def migration_version
63
+ if Rails.version >= "5.0.0"
64
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ # Created by AI
3
+
4
+ class AddMaxDegreeToUsers < ActiveRecord::Migration[<%= migration_version %>]
5
+ def change
6
+ add_column :users, :max_degree, :integer, default: 3, null: false
7
+ end
8
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Created by AI
4
+
5
+ TenCubed.configure do |config|
6
+ # Maximum number of direct connections a user can have
7
+ # Default: 10
8
+ # config.max_direct_connections = 10
9
+
10
+ # Maximum network depth for querying connections
11
+ # Default: 3
12
+ # config.max_network_depth = 3
13
+
14
+ # Table name for connections
15
+ # Default: :connections
16
+ # config.connection_table_name = :connections
17
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ # Created by AI
3
+
4
+ class CreateUsers < ActiveRecord::Migration[<%= migration_version %>]
5
+ def change
6
+ create_table :users do |t|
7
+ t.string :name
8
+ t.string :email, null: false, default: ""
9
+ t.integer :max_degree, default: 3, null: false
10
+
11
+ # Add any other fields needed for your User model
12
+
13
+ t.timestamps
14
+ end
15
+
16
+ add_index :users, :email, unique: true
17
+ end
18
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Created by AI
4
+
5
+ class User < ApplicationRecord
6
+ include TenCubed::Models::Concerns::TenCubedUser
7
+
8
+ # Add your custom User model code here
9
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Created by AI
4
+
5
+ module TenCubed
6
+ module Generators
7
+ class UserGenerator < Rails::Generators::Base
8
+ include Rails::Generators::Migration
9
+ source_root File.expand_path("templates", __dir__)
10
+ desc "Create a User model with ten_cubed functionality"
11
+
12
+ # Required for Rails::Generators::Migration
13
+ def self.next_migration_number(dirname)
14
+ next_migration_number = current_migration_number(dirname) + 1
15
+ ActiveRecord::Migration.next_migration_number(next_migration_number)
16
+ end
17
+
18
+ def create_user_model
19
+ user_file = "app/models/user.rb"
20
+
21
+ if File.exist?(user_file)
22
+ # Check if the file already has the include statement
23
+ contents = File.read(user_file)
24
+ if contents.include?("include TenCubed::Models::Concerns::TenCubedUser")
25
+ say_status :identical, "User model already includes TenCubedUser", :blue
26
+ else
27
+ # Insert the include statement after the class declaration
28
+ inject_into_file user_file, after: /class User < .*\n/ do
29
+ " include TenCubed::Models::Concerns::TenCubedUser\n"
30
+ end
31
+ say_status :insert, "include TenCubed::Models::Concerns::TenCubedUser added to User model", :green
32
+ end
33
+ else
34
+ # Create a new user.rb file from template
35
+ template "user.rb", user_file
36
+ end
37
+ end
38
+
39
+ def create_user_migration
40
+ migration_template "create_users.rb",
41
+ "db/migrate/create_users.rb",
42
+ migration_version: migration_version
43
+ end
44
+
45
+ private
46
+
47
+ def migration_version
48
+ if Rails.version >= "5.0.0"
49
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Created by AI
4
+
5
+ module TenCubed
6
+ class Configuration
7
+ # Default maximum direct connections allowed
8
+ attr_accessor :max_direct_connections
9
+
10
+ # Default maximum network depth allowed
11
+ attr_accessor :max_network_depth
12
+
13
+ # Table name for the connections table
14
+ attr_accessor :connection_table_name
15
+
16
+ def initialize
17
+ @max_direct_connections = 10
18
+ @max_network_depth = 3
19
+ @connection_table_name = :connections
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Created by AI
4
+
5
+ require "active_record"
6
+
7
+ module TenCubed
8
+ class Connection < ::ActiveRecord::Base
9
+ def self.table_name
10
+ TenCubed.configuration.connection_table_name.to_s
11
+ end
12
+
13
+ if defined?(::User)
14
+ belongs_to :user, class_name: "::User"
15
+ belongs_to :target, class_name: "::User"
16
+ else
17
+ # For tests
18
+ attr_accessor :user, :target
19
+ end
20
+
21
+ validate :ensure_user_has_less_than_max_connections, on: :create
22
+ validate :ensure_target_is_not_self, on: :create
23
+
24
+ private
25
+
26
+ def ensure_user_has_less_than_max_connections
27
+ if user && user.connections.count >= TenCubed.configuration.max_direct_connections
28
+ errors.add(:user, "can't have more than #{TenCubed.configuration.max_direct_connections} connections")
29
+ end
30
+ end
31
+
32
+ def ensure_target_is_not_self
33
+ if user && target && user == target
34
+ errors.add(:target, "can't be the same as user")
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Created by AI
4
+
5
+ require "rails"
6
+ require "active_record"
7
+ require "active_support/all"
8
+
9
+ module TenCubed
10
+ class Engine < ::Rails::Engine
11
+ isolate_namespace TenCubed
12
+
13
+ config.generators do |g|
14
+ g.test_framework :rspec
15
+ g.fixture_replacement :factory_bot
16
+ g.factory_bot dir: "spec/factories"
17
+ end
18
+
19
+ initializer "ten_cubed.check_postgres" do
20
+ ActiveSupport.on_load(:active_record) do
21
+ unless /postgresql/i.match?(ActiveRecord::Base.connection.adapter_name)
22
+ raise "TenCubed requires PostgreSQL as the database adapter. Found: #{ActiveRecord::Base.connection.adapter_name}"
23
+ end
24
+ end
25
+ end
26
+
27
+ initializer "ten_cubed.load_concerns" do
28
+ ActiveSupport.on_load(:active_record) do
29
+ require "ten_cubed/models/concerns/ten_cubed_user"
30
+ end
31
+ end
32
+
33
+ config.to_prepare do
34
+ if defined?(User)
35
+ User.include TenCubed::Models::Concerns::TenCubedUser
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Created by AI
4
+
5
+ require "active_record"
6
+ require "active_support/concern"
7
+
8
+ module TenCubed
9
+ module Models
10
+ module Concerns
11
+ module TenCubedUser
12
+ extend ActiveSupport::Concern
13
+
14
+ included do
15
+ has_many :connections, dependent: :destroy, class_name: "TenCubed::Connection", foreign_key: "user_id"
16
+ has_many :friends, through: :connections, source: :target
17
+ end
18
+
19
+ # Returns the user's network up to the user's max_degree
20
+ def my_network
21
+ @my_network ||= {}
22
+ @my_network[max_degree] ||= network(max_degree)
23
+ end
24
+
25
+ # Returns the degree of connection between self and another user
26
+ def degree_of_connection(user)
27
+ my_network.each do |connection|
28
+ return connection.degree if connection.id == user.id
29
+ end
30
+ nil # Return nil if no connection exists
31
+ end
32
+
33
+ # Checks if a user is in the current user's network
34
+ def in_network?(user)
35
+ my_network.include?(user)
36
+ end
37
+
38
+ # Returns a network of connections up to the specified max_depth
39
+ def network(max_depth = 3)
40
+ return [] if max_depth <= 0 || max_depth > 3
41
+
42
+ sql = <<-SQL
43
+ WITH RECURSIVE friend_of_friend AS (
44
+ SELECT connections.target_id, users.max_degree, 1 AS depth
45
+ FROM #{TenCubed.configuration.connection_table_name}
46
+ JOIN users ON connections.target_id = users.id
47
+ WHERE connections.user_id = :user_id
48
+ UNION ALL
49
+ SELECT connections.target_id, users.max_degree, depth + 1
50
+ FROM #{TenCubed.configuration.connection_table_name}
51
+ JOIN users ON connections.target_id = users.id
52
+ JOIN friend_of_friend ON connections.user_id = friend_of_friend.target_id
53
+ WHERE depth < :max_depth
54
+ AND depth < users.max_degree -- Only add users that can appear at current depth.
55
+ )
56
+ SELECT DISTINCT ON (users.id) users.*, friend_of_friend.depth AS degree
57
+ FROM users
58
+ JOIN friend_of_friend ON users.id = friend_of_friend.target_id
59
+ WHERE users.id != :user_id
60
+ ORDER BY users.id, friend_of_friend.depth;
61
+ SQL
62
+
63
+ connections = self.class.find_by_sql([sql, {user_id: id, max_depth: max_depth}])
64
+ connections.uniq { |c| c.id }
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TenCubed
4
+ VERSION = "0.1.0"
5
+ end
data/lib/ten_cubed.rb ADDED
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+ require_relative "ten_cubed/version"
5
+ require_relative "ten_cubed/configuration"
6
+ require_relative "ten_cubed/connection"
7
+ require_relative "ten_cubed/models/concerns/ten_cubed_user"
8
+ require_relative "ten_cubed/engine" if defined?(Rails)
9
+
10
+ module TenCubed
11
+ class Error < StandardError; end
12
+
13
+ # Configuration
14
+ class << self
15
+ def configuration
16
+ @configuration ||= Configuration.new
17
+ end
18
+
19
+ def configure
20
+ yield(configuration)
21
+ end
22
+ end
23
+ end
data/sig/ten_cubed.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module TenCubed
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,211 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ten_cubed
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sebastian Wildwood
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-03-08 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: '7.0'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 7.0.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '7.0'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 7.0.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: activerecord
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '7.0'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 7.0.0
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '7.0'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 7.0.0
53
+ - !ruby/object:Gem::Dependency
54
+ name: pg
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '1.5'
60
+ type: :runtime
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '1.5'
67
+ - !ruby/object:Gem::Dependency
68
+ name: rspec-rails
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: 7.1.1
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: 7.1.1
81
+ - !ruby/object:Gem::Dependency
82
+ name: database_cleaner-active_record
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '2.2'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '2.2'
95
+ - !ruby/object:Gem::Dependency
96
+ name: standard
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '1.3'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '1.3'
109
+ - !ruby/object:Gem::Dependency
110
+ name: factory_bot_rails
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '6.2'
116
+ type: :development
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - "~>"
121
+ - !ruby/object:Gem::Version
122
+ version: '6.2'
123
+ - !ruby/object:Gem::Dependency
124
+ name: simplecov
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: 0.22.0
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - "~>"
135
+ - !ruby/object:Gem::Version
136
+ version: 0.22.0
137
+ - !ruby/object:Gem::Dependency
138
+ name: generator_spec
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - "~>"
142
+ - !ruby/object:Gem::Version
143
+ version: 0.9.5
144
+ type: :development
145
+ prerelease: false
146
+ version_requirements: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - "~>"
149
+ - !ruby/object:Gem::Version
150
+ version: 0.9.5
151
+ description: The ten_cubed gem allows you to easily integrate an artificially restricted
152
+ social graph into your Rails application. It limits users to 10 direct connections
153
+ and provides a network of up to 1,110 total connections (10 + 100 + 1000) with configurable
154
+ degree access. Requires PostgreSQL.
155
+ email:
156
+ - sebastian@lemery.io
157
+ executables: []
158
+ extensions: []
159
+ extra_rdoc_files: []
160
+ files:
161
+ - ".rspec"
162
+ - ".standard.yml"
163
+ - CHANGELOG.md
164
+ - LICENSE
165
+ - README.md
166
+ - Rakefile
167
+ - lib/generators/ten_cubed/connection/connection_generator.rb
168
+ - lib/generators/ten_cubed/connection/templates/connection.rb
169
+ - lib/generators/ten_cubed/connection/templates/create_connections.rb
170
+ - lib/generators/ten_cubed/install/install_generator.rb
171
+ - lib/generators/ten_cubed/install/templates/add_max_degree_to_users.rb
172
+ - lib/generators/ten_cubed/install/templates/initializer.rb
173
+ - lib/generators/ten_cubed/user/templates/create_users.rb
174
+ - lib/generators/ten_cubed/user/templates/user.rb
175
+ - lib/generators/ten_cubed/user/user_generator.rb
176
+ - lib/ten_cubed.rb
177
+ - lib/ten_cubed/configuration.rb
178
+ - lib/ten_cubed/connection.rb
179
+ - lib/ten_cubed/engine.rb
180
+ - lib/ten_cubed/models/concerns/ten_cubed_user.rb
181
+ - lib/ten_cubed/version.rb
182
+ - sig/ten_cubed.rbs
183
+ homepage: https://github.com/darkpicnic/ten_cubed
184
+ licenses:
185
+ - MIT
186
+ metadata:
187
+ allowed_push_host: https://rubygems.org
188
+ homepage_uri: https://github.com/darkpicnic/ten_cubed
189
+ source_code_uri: https://github.com/darkpicnic/ten_cubed
190
+ changelog_uri: https://github.com/darkpicnic/ten_cubed/blob/main/CHANGELOG.md
191
+ documentation_uri: https://github.com/darkpicnic/ten_cubed/blob/main/README.md
192
+ post_install_message:
193
+ rdoc_options: []
194
+ require_paths:
195
+ - lib
196
+ required_ruby_version: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - ">="
199
+ - !ruby/object:Gem::Version
200
+ version: 3.2.0
201
+ required_rubygems_version: !ruby/object:Gem::Requirement
202
+ requirements:
203
+ - - ">="
204
+ - !ruby/object:Gem::Version
205
+ version: '0'
206
+ requirements: []
207
+ rubygems_version: 3.5.11
208
+ signing_key:
209
+ specification_version: 4
210
+ summary: Implementation of the ten_cubed networking system for Rails applications
211
+ test_files: []