├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── config.yml
│ └── issue_template.md
├── dependabot.yml
├── pull_request_template.md
└── workflows
│ └── ci.yml
├── .gitignore
├── .rubocop.yml
├── .ruby-version
├── Appraisals
├── CHANGELOG.md
├── Gemfile
├── Gemfile.lock
├── MIT-LICENSE
├── README.md
├── Rakefile
├── app
├── controllers
│ ├── .keep
│ └── concerns
│ │ └── .keep
└── models
│ ├── .keep
│ └── refer
│ ├── application_record.rb
│ ├── referral.rb
│ ├── referral_code.rb
│ └── visit.rb
├── bin
├── rails
└── rubocop
├── config
└── routes.rb
├── db
└── migrate
│ ├── 20240611180738_create_refer_referrals.rb
│ ├── 20240611183349_create_refer_referral_codes.rb
│ └── 20240701172643_create_refer_visits.rb
├── gemfiles
├── rails_7.1.gemfile
├── rails_7.1.gemfile.lock
├── rails_7.2.gemfile
├── rails_7.2.gemfile.lock
├── rails_8.0.gemfile
├── rails_8.0.gemfile.lock
├── rails_main.gemfile
└── rails_main.gemfile.lock
├── lib
├── generators
│ └── refer
│ │ ├── install
│ │ ├── USAGE
│ │ └── install_generator.rb
│ │ └── model
│ │ ├── USAGE
│ │ └── model_generator.rb
├── refer.rb
├── refer
│ ├── controller.rb
│ ├── engine.rb
│ ├── has_referrals.rb
│ ├── model.rb
│ └── version.rb
└── tasks
│ └── refer_tasks.rake
├── refer.gemspec
└── test
├── dummy
├── Rakefile
├── app
│ ├── assets
│ │ ├── config
│ │ │ └── manifest.js
│ │ ├── images
│ │ │ └── .keep
│ │ └── stylesheets
│ │ │ └── application.css
│ ├── channels
│ │ └── application_cable
│ │ │ ├── channel.rb
│ │ │ └── connection.rb
│ ├── controllers
│ │ ├── application_controller.rb
│ │ ├── concerns
│ │ │ └── .keep
│ │ └── referrals_controller.rb
│ ├── helpers
│ │ └── application_helper.rb
│ ├── jobs
│ │ └── application_job.rb
│ ├── mailers
│ │ └── application_mailer.rb
│ ├── models
│ │ ├── application_record.rb
│ │ ├── concerns
│ │ │ └── .keep
│ │ └── user.rb
│ └── views
│ │ ├── layouts
│ │ ├── application.html.erb
│ │ ├── mailer.html.erb
│ │ └── mailer.text.erb
│ │ └── pwa
│ │ ├── manifest.json.erb
│ │ └── service-worker.js
├── bin
│ ├── rails
│ ├── rake
│ └── setup
├── config.ru
├── config
│ ├── application.rb
│ ├── boot.rb
│ ├── cable.yml
│ ├── database.yml
│ ├── environment.rb
│ ├── environments
│ │ ├── development.rb
│ │ ├── production.rb
│ │ └── test.rb
│ ├── initializers
│ │ ├── assets.rb
│ │ ├── content_security_policy.rb
│ │ ├── filter_parameter_logging.rb
│ │ ├── inflections.rb
│ │ └── permissions_policy.rb
│ ├── locales
│ │ └── en.yml
│ ├── puma.rb
│ ├── routes.rb
│ └── storage.yml
├── db
│ ├── migrate
│ │ └── 20240611180748_create_users.rb
│ └── schema.rb
├── lib
│ └── assets
│ │ └── .keep
├── log
│ └── .keep
└── public
│ ├── 404.html
│ ├── 406-unsupported-browser.html
│ ├── 422.html
│ ├── 500.html
│ ├── icon.png
│ └── icon.svg
├── fixtures
├── files
│ └── .keep
├── refer
│ ├── referral_codes.yml
│ ├── referrals.yml
│ └── visits.yml
└── users.yml
├── integration
├── .keep
└── referral_test.rb
├── models
├── .keep
└── refer
│ ├── referral_code_test.rb
│ ├── referral_test.rb
│ └── visit_test.rb
├── refer_test.rb
└── test_helper.rb
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [excid3] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Get Help
4 | url: https://github.com/excid3/refer/discussions/new?category=help
5 | about: If you can't get something to work the way you expect, open a question in our discussion forums.
6 | - name: Feature Request
7 | url: https://github.com/excid3/refer/discussions/new?category=ideas
8 | about: 'Suggest any ideas you have using our discussion forums.'
9 | - name: Bug Report
10 | url: https://github.com/excid3/refer/issues/new?body=%3C%21--%20Please%20provide%20all%20of%20the%20information%20requested%20below.%20We%27re%20a%20small%20team%20and%20without%20all%20of%20this%20information%20it%27s%20not%20possible%20for%20us%20to%20help%20and%20your%20bug%20report%20will%20be%20closed.%20--%3E%0A%0A%2A%2AWhat%20version%20of%20Noticed%20are%20you%20using%3F%2A%2A%0A%0AFor%20example%3A%20v2.0.4%0A%0A%2A%2AWhat%20version%20of%20Rails%20are%20you%20using%3F%2A%2A%0A%0AFor%20example%3A%20v7.1.1%0A%0A%2A%2ADescribe%20your%20issue%2A%2A%0A%0ADescribe%20the%20problem%20you%27re%20seeing%2C%20any%20important%20steps%20to%20reproduce%20and%20what%20behavior%20you%20expect%20instead.
11 | about: If you've already asked for help with a problem and confirmed something is broken with Noticed itself, create a bug report.
12 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/issue_template.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 🐞 Bug
3 | about: File a bug/issue
4 | title: '[BUG]
'
5 | labels: Bug, Needs Triage
6 | assignees: ''
7 |
8 | ---
9 |
10 | ## Bug Report
11 |
12 | **Describe the Bug:**
13 |
14 |
15 | **To Reproduce:**
16 |
17 |
18 | 1. Step 1
19 | 2. Step 2
20 | 3. ...
21 |
22 | **Expected Behavior:**
23 |
24 |
25 | **Actual Behavior:**
26 |
27 |
28 | **Screenshots (if applicable):**
29 |
30 |
31 | **Environment:**
32 | - Gem version:
33 | - Ruby version:
34 | - Rails version:
35 | - Operating System:
36 |
37 | **Additional Context:**
38 |
39 |
40 | **Possible Fix:**
41 |
42 |
43 | **Steps to Reproduce with Fix (if available):**
44 |
45 |
46 | **Related Issues:**
47 |
48 |
49 | **Labels to Apply:**
50 |
51 |
52 | **Checklist:**
53 |
54 |
55 | - [ ] I have searched for similar issues and couldn't find any
56 | - [ ] I have checked the documentation for relevant information
57 | - [ ] I have included all the required information
58 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: bundler
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | open-pull-requests-limit: 10
8 | - package-ecosystem: github-actions
9 | directory: "/"
10 | schedule:
11 | interval: daily
12 | open-pull-requests-limit: 10
13 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## Pull Request
2 |
3 | **Summary:**
4 |
5 |
6 | **Related Issue:**
7 |
8 |
9 | **Description:**
10 |
11 |
12 | **Testing:**
13 |
14 |
15 | **Screenshots (if applicable):**
16 |
17 |
18 | **Checklist:**
19 |
20 |
21 | - [ ] Code follows the project's coding standards
22 | - [ ] Tests have been added or updated to cover the changes
23 | - [ ] Documentation has been updated (if applicable)
24 | - [ ] All existing tests pass
25 | - [ ] Conforms to the contributing guidelines
26 |
27 | **Additional Notes:**
28 |
29 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches: [ main ]
7 |
8 | jobs:
9 | lint:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout code
13 | uses: actions/checkout@v4
14 |
15 | - name: Set up Ruby
16 | uses: ruby/setup-ruby@v1
17 | with:
18 | ruby-version: .ruby-version
19 | bundler-cache: true
20 |
21 | - name: Lint code for consistent style
22 | run: bin/rubocop -f github
23 |
24 | test:
25 | runs-on: ubuntu-latest
26 | strategy:
27 | matrix:
28 | gemfile:
29 | - rails_7.1
30 | - rails_7.2
31 | - rails_8.0
32 | - rails_main
33 |
34 | steps:
35 | - name: Install packages
36 | run: sudo apt-get update && sudo apt-get install --no-install-recommends -y google-chrome-stable curl libjemalloc2 libsqlite3-0 libvips libsqlite3-0
37 |
38 | - name: Checkout code
39 | uses: actions/checkout@v4
40 |
41 | - name: Set up Ruby
42 | uses: ruby/setup-ruby@v1
43 | with:
44 | ruby-version: .ruby-version
45 | bundler-cache: true
46 |
47 | - name: Run tests
48 | env:
49 | RAILS_ENV: test
50 | # REDIS_URL: redis://localhost:6379/0
51 | run: bin/rails db:test:prepare && bin/rails test
52 |
53 | - name: Keep screenshots from failed system tests
54 | uses: actions/upload-artifact@v4
55 | if: failure()
56 | with:
57 | name: screenshots
58 | path: ${{ github.workspace }}/tmp/screenshots
59 | if-no-files-found: ignore
60 |
61 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.bundle/
2 | /doc/
3 | /log/*.log
4 | /pkg/
5 | /tmp/
6 | /test/dummy/db/*.sqlite3
7 | /test/dummy/db/*.sqlite3-*
8 | /test/dummy/log/*.log
9 | /test/dummy/storage/
10 | /test/dummy/tmp/
11 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | # Omakase Ruby styling for Rails
2 | inherit_gem: { rubocop-rails-omakase: rubocop.yml }
3 |
4 | # Overwrite or add rules to create your own house style
5 | #
6 | # # Use `[a, [b, c]]` not `[ a, [ b, c ] ]`
7 | # Layout/SpaceInsideArrayLiteralBrackets:
8 | # Enabled: false
9 |
--------------------------------------------------------------------------------
/.ruby-version:
--------------------------------------------------------------------------------
1 | 3.4.3
2 |
--------------------------------------------------------------------------------
/Appraisals:
--------------------------------------------------------------------------------
1 | appraise "rails-7.1" do
2 | gem "rails", "~> 7.1.0"
3 | end
4 |
5 | appraise "rails-7.2" do
6 | gem "rails", "~> 7.2.0"
7 | end
8 |
9 | appraise "rails-8.0" do
10 | gem "rails", "~> 8.0.0"
11 | end
12 |
13 | appraise "rails-main" do
14 | gem "rails", github: "rails/rails"
15 | end
16 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ### Unreleased
2 |
3 | * Add lazy load hooks for models #59
4 |
5 | ### 1.0.2
6 |
7 | * Ensure self-referrals aren't allowed #47
8 |
9 | ### 1.0.1
10 |
11 | * Use `as:` for polymorphic associations
12 | * Add tests against Rails 7.1, 7.2, 8.0 and Rails main
13 |
14 | ### 1.0.0
15 |
16 | * Require Rails 7.1+ for `normalizes` support
17 |
18 | ### 0.5.3
19 |
20 | * Only set ReferralCode#code if it is blank #20
21 |
22 | ### 0.5.2
23 |
24 | * Move controller constant to lib
25 |
26 | ### 0.5.1
27 |
28 | * Fixes HasReferrals constant lookup
29 |
30 | ### 0.5.0
31 |
32 | * Add `Refer.referral_completed = ->(referral) { }` callback that runs when a referral is marked as completed
33 | * `referral.complete!` does nothing if already completed
34 |
35 | ### 0.4.0
36 |
37 | * Add `completed` scope to `Refer::Referral`
38 |
39 | ### 0.3.0
40 |
41 | * Add visit tracking #5
42 | * Configurable referral cookie overwrites #4
43 | Choose between the original referral code or the most recent referral code to receive the referral
44 | * Fix referral code default generator
45 |
46 | ### 0.2.1
47 |
48 | * Change migrations to use Rails 6.1 version for compatibility
49 |
50 | ### 0.2.0
51 |
52 | * Add `set_referral_cookie` controller method
53 | * Add `rails g refer:install` generator to inject `set_referral_cookie`
54 |
55 | ### 0.1.1
56 |
57 | * Added `dependent: :nullify` so ReferralCodes persist Referral records when deleted.
58 | * Added `dependent: :destroy` so Referrals and ReferralCodes are deleted when users are deleted.
59 | * Fixed missing `referral_codes` association on users
60 |
61 | ### 0.1.0
62 |
63 | * Initial release
64 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | # Specify your gem's dependencies in refer.gemspec.
4 | gemspec
5 |
6 | gem "puma"
7 |
8 | gem "sqlite3"
9 |
10 | gem "sprockets-rails"
11 |
12 | # Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/]
13 | gem "rubocop-rails-omakase", require: false
14 |
15 | # Start debugger with binding.b [https://github.com/ruby/debug]
16 | # gem "debug", ">= 1.0.0"
17 |
18 | gem "appraisal"
19 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | refer (1.0.3)
5 | rails (>= 7.1.0)
6 |
7 | GEM
8 | remote: https://rubygems.org/
9 | specs:
10 | actioncable (8.0.2)
11 | actionpack (= 8.0.2)
12 | activesupport (= 8.0.2)
13 | nio4r (~> 2.0)
14 | websocket-driver (>= 0.6.1)
15 | zeitwerk (~> 2.6)
16 | actionmailbox (8.0.2)
17 | actionpack (= 8.0.2)
18 | activejob (= 8.0.2)
19 | activerecord (= 8.0.2)
20 | activestorage (= 8.0.2)
21 | activesupport (= 8.0.2)
22 | mail (>= 2.8.0)
23 | actionmailer (8.0.2)
24 | actionpack (= 8.0.2)
25 | actionview (= 8.0.2)
26 | activejob (= 8.0.2)
27 | activesupport (= 8.0.2)
28 | mail (>= 2.8.0)
29 | rails-dom-testing (~> 2.2)
30 | actionpack (8.0.2)
31 | actionview (= 8.0.2)
32 | activesupport (= 8.0.2)
33 | nokogiri (>= 1.8.5)
34 | rack (>= 2.2.4)
35 | rack-session (>= 1.0.1)
36 | rack-test (>= 0.6.3)
37 | rails-dom-testing (~> 2.2)
38 | rails-html-sanitizer (~> 1.6)
39 | useragent (~> 0.16)
40 | actiontext (8.0.2)
41 | actionpack (= 8.0.2)
42 | activerecord (= 8.0.2)
43 | activestorage (= 8.0.2)
44 | activesupport (= 8.0.2)
45 | globalid (>= 0.6.0)
46 | nokogiri (>= 1.8.5)
47 | actionview (8.0.2)
48 | activesupport (= 8.0.2)
49 | builder (~> 3.1)
50 | erubi (~> 1.11)
51 | rails-dom-testing (~> 2.2)
52 | rails-html-sanitizer (~> 1.6)
53 | activejob (8.0.2)
54 | activesupport (= 8.0.2)
55 | globalid (>= 0.3.6)
56 | activemodel (8.0.2)
57 | activesupport (= 8.0.2)
58 | activerecord (8.0.2)
59 | activemodel (= 8.0.2)
60 | activesupport (= 8.0.2)
61 | timeout (>= 0.4.0)
62 | activestorage (8.0.2)
63 | actionpack (= 8.0.2)
64 | activejob (= 8.0.2)
65 | activerecord (= 8.0.2)
66 | activesupport (= 8.0.2)
67 | marcel (~> 1.0)
68 | activesupport (8.0.2)
69 | base64
70 | benchmark (>= 0.3)
71 | bigdecimal
72 | concurrent-ruby (~> 1.0, >= 1.3.1)
73 | connection_pool (>= 2.2.5)
74 | drb
75 | i18n (>= 1.6, < 2)
76 | logger (>= 1.4.2)
77 | minitest (>= 5.1)
78 | securerandom (>= 0.3)
79 | tzinfo (~> 2.0, >= 2.0.5)
80 | uri (>= 0.13.1)
81 | appraisal (2.5.0)
82 | bundler
83 | rake
84 | thor (>= 0.14.0)
85 | ast (2.4.3)
86 | base64 (0.2.0)
87 | benchmark (0.4.0)
88 | bigdecimal (3.1.9)
89 | builder (3.3.0)
90 | concurrent-ruby (1.3.5)
91 | connection_pool (2.5.2)
92 | crass (1.0.6)
93 | date (3.4.1)
94 | drb (2.2.1)
95 | erubi (1.13.1)
96 | globalid (1.2.1)
97 | activesupport (>= 6.1)
98 | i18n (1.14.7)
99 | concurrent-ruby (~> 1.0)
100 | io-console (0.8.0)
101 | irb (1.15.2)
102 | pp (>= 0.6.0)
103 | rdoc (>= 4.0.0)
104 | reline (>= 0.4.2)
105 | json (2.11.3)
106 | language_server-protocol (3.17.0.4)
107 | lint_roller (1.1.0)
108 | logger (1.7.0)
109 | loofah (2.24.0)
110 | crass (~> 1.0.2)
111 | nokogiri (>= 1.12.0)
112 | mail (2.8.1)
113 | mini_mime (>= 0.1.1)
114 | net-imap
115 | net-pop
116 | net-smtp
117 | marcel (1.0.4)
118 | mini_mime (1.1.5)
119 | minitest (5.25.5)
120 | net-imap (0.5.7)
121 | date
122 | net-protocol
123 | net-pop (0.1.2)
124 | net-protocol
125 | net-protocol (0.2.2)
126 | timeout
127 | net-smtp (0.5.1)
128 | net-protocol
129 | nio4r (2.7.4)
130 | nokogiri (1.18.8-arm64-darwin)
131 | racc (~> 1.4)
132 | nokogiri (1.18.8-x86_64-darwin)
133 | racc (~> 1.4)
134 | nokogiri (1.18.8-x86_64-linux-gnu)
135 | racc (~> 1.4)
136 | parallel (1.27.0)
137 | parser (3.3.8.0)
138 | ast (~> 2.4.1)
139 | racc
140 | pp (0.6.2)
141 | prettyprint
142 | prettyprint (0.2.0)
143 | prism (1.4.0)
144 | psych (5.2.3)
145 | date
146 | stringio
147 | puma (6.6.0)
148 | nio4r (~> 2.0)
149 | racc (1.8.1)
150 | rack (3.1.13)
151 | rack-session (2.1.0)
152 | base64 (>= 0.1.0)
153 | rack (>= 3.0.0)
154 | rack-test (2.2.0)
155 | rack (>= 1.3)
156 | rackup (2.2.1)
157 | rack (>= 3)
158 | rails (8.0.2)
159 | actioncable (= 8.0.2)
160 | actionmailbox (= 8.0.2)
161 | actionmailer (= 8.0.2)
162 | actionpack (= 8.0.2)
163 | actiontext (= 8.0.2)
164 | actionview (= 8.0.2)
165 | activejob (= 8.0.2)
166 | activemodel (= 8.0.2)
167 | activerecord (= 8.0.2)
168 | activestorage (= 8.0.2)
169 | activesupport (= 8.0.2)
170 | bundler (>= 1.15.0)
171 | railties (= 8.0.2)
172 | rails-dom-testing (2.2.0)
173 | activesupport (>= 5.0.0)
174 | minitest
175 | nokogiri (>= 1.6)
176 | rails-html-sanitizer (1.6.2)
177 | loofah (~> 2.21)
178 | nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
179 | railties (8.0.2)
180 | actionpack (= 8.0.2)
181 | activesupport (= 8.0.2)
182 | irb (~> 1.13)
183 | rackup (>= 1.0.0)
184 | rake (>= 12.2)
185 | thor (~> 1.0, >= 1.2.2)
186 | zeitwerk (~> 2.6)
187 | rainbow (3.1.1)
188 | rake (13.2.1)
189 | rdoc (6.13.1)
190 | psych (>= 4.0.0)
191 | regexp_parser (2.10.0)
192 | reline (0.6.1)
193 | io-console (~> 0.5)
194 | rubocop (1.75.3)
195 | json (~> 2.3)
196 | language_server-protocol (~> 3.17.0.2)
197 | lint_roller (~> 1.1.0)
198 | parallel (~> 1.10)
199 | parser (>= 3.3.0.2)
200 | rainbow (>= 2.2.2, < 4.0)
201 | regexp_parser (>= 2.9.3, < 3.0)
202 | rubocop-ast (>= 1.44.0, < 2.0)
203 | ruby-progressbar (~> 1.7)
204 | unicode-display_width (>= 2.4.0, < 4.0)
205 | rubocop-ast (1.44.1)
206 | parser (>= 3.3.7.2)
207 | prism (~> 1.4)
208 | rubocop-performance (1.25.0)
209 | lint_roller (~> 1.1)
210 | rubocop (>= 1.75.0, < 2.0)
211 | rubocop-ast (>= 1.38.0, < 2.0)
212 | rubocop-rails (2.31.0)
213 | activesupport (>= 4.2.0)
214 | lint_roller (~> 1.1)
215 | rack (>= 1.1)
216 | rubocop (>= 1.75.0, < 2.0)
217 | rubocop-ast (>= 1.38.0, < 2.0)
218 | rubocop-rails-omakase (1.1.0)
219 | rubocop (>= 1.72)
220 | rubocop-performance (>= 1.24)
221 | rubocop-rails (>= 2.30)
222 | ruby-progressbar (1.13.0)
223 | securerandom (0.4.1)
224 | sprockets (4.2.2)
225 | concurrent-ruby (~> 1.0)
226 | logger
227 | rack (>= 2.2.4, < 4)
228 | sprockets-rails (3.5.2)
229 | actionpack (>= 6.1)
230 | activesupport (>= 6.1)
231 | sprockets (>= 3.0.0)
232 | sqlite3 (2.6.0-arm64-darwin)
233 | sqlite3 (2.6.0-x86_64-darwin)
234 | sqlite3 (2.6.0-x86_64-linux-gnu)
235 | stringio (3.1.7)
236 | thor (1.3.2)
237 | timeout (0.4.3)
238 | tzinfo (2.0.6)
239 | concurrent-ruby (~> 1.0)
240 | unicode-display_width (3.1.4)
241 | unicode-emoji (~> 4.0, >= 4.0.4)
242 | unicode-emoji (4.0.4)
243 | uri (1.0.3)
244 | useragent (0.16.11)
245 | websocket-driver (0.7.7)
246 | base64
247 | websocket-extensions (>= 0.1.0)
248 | websocket-extensions (0.1.5)
249 | zeitwerk (2.7.2)
250 |
251 | PLATFORMS
252 | arm64-darwin
253 | x86_64-darwin-23
254 | x86_64-linux
255 |
256 | DEPENDENCIES
257 | appraisal
258 | puma
259 | refer!
260 | rubocop-rails-omakase
261 | sprockets-rails
262 | sqlite3
263 |
264 | BUNDLED WITH
265 | 2.6.8
266 |
--------------------------------------------------------------------------------
/MIT-LICENSE:
--------------------------------------------------------------------------------
1 | Copyright Chris Oliver
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Refer
2 |
3 | Referral codes and affiliate links for Ruby on Rails applications.
4 |
5 | ## 📦 Installation
6 | Add this line to your application's Gemfile:
7 |
8 | ```ruby
9 | gem "refer"
10 | ```
11 |
12 | And then execute:
13 | ```bash
14 | $ bundle
15 | ```
16 |
17 | Add Refer to your controllers to store referral cookies:
18 | ```bash
19 | bin/rails generate refer:install
20 | ```
21 |
22 | And add Refer to your model:
23 | ```bash
24 | bin/rails generate refer:model User
25 | bin/rails db:migrate
26 | ```
27 |
28 | ## 🧑💻 Usage
29 |
30 | Refer adds a model to your Rails application for tracking referrals and referral codes.
31 |
32 | To track referrals, you'll need to
33 |
34 | 1. Create a referral code
35 | 2. Set a cookie with the referral code
36 | 3. Create the referral
37 | 4. (Optional) Provide a reward for successful referral
38 |
39 | ##### Create a referral code
40 |
41 | You can create referral codes through the association:
42 |
43 | ```ruby
44 | user.referral_codes.create #=> randomly generated code
45 | user.referral_codes.create(code: "chris")
46 | ```
47 |
48 | To customize the referral code generator:
49 |
50 | ```ruby
51 | Refer.code_generator = ->(referrer) { [referrer.id, SecureRandom.alphanumeric(8)].join("-") }
52 | #=> generates codes like "1-7frb5fUw"
53 | ```
54 |
55 | By default, Refer will generate 8 character alphanumeric codes.
56 |
57 | ##### Set a referral cookie
58 |
59 | To track users, we need to stash the referral code in a cookie when present. By default, Refer will look for `?ref=code` and save this in a cookie.
60 |
61 | ```ruby
62 | class ApplicationController < ActionController::Base
63 | set_referral_cookie
64 | end
65 | ```
66 |
67 | Move `set_referral_cookie` to specific controllers if you'd only like cookies set in certain areas like marketing pages for example.
68 |
69 | ```ruby
70 | class MarketingController < ActionController::Base
71 | set_referral_cookie except: [:about_us]
72 | end
73 | ```
74 |
75 | You can customize the param name with:
76 |
77 | ```ruby
78 | Refer.param_name = :ref
79 | ```
80 |
81 | You can customize the cookie name with:
82 |
83 | ```ruby
84 | Refer.cookie_name = :refer_code
85 | ```
86 |
87 | ##### Refer a user:
88 |
89 | To create a referral, you can run the following
90 |
91 | ```ruby
92 | class RegistrationsController < ApplicationController
93 | def create
94 | @user = User.new(user_params)
95 | if @user.save
96 | refer @user #=> Looks up cookie and attempts referral
97 | redirect_to root_path
98 | else
99 | render :new, status: :unprocessable_entity
100 | end
101 | end
102 | end
103 | ```
104 |
105 | You can also do this manually:
106 |
107 | ```ruby
108 | Refer.refer(code: "referral_code", referee: user)
109 | ```
110 |
111 | Refer will make sure the user has not already been referred and create a Referral.
112 |
113 | ##### Check if a user was referred already:
114 |
115 | ```ruby
116 | Refer.referred?(user)
117 | #=> true/false
118 | ```
119 |
120 | ##### Accessing Referrals
121 |
122 | To access a user's referrals, you can use the `referrals` association:
123 |
124 | ```ruby
125 | user.referrals #=> [Refer::Referral, Refer::Referral]
126 | ```
127 |
128 | This returns a list of `Refer::Referral` objects.
129 |
130 | ##### Accessing Referral
131 |
132 | To access a user's referral, you can use the `referral` association:
133 |
134 | ```ruby
135 | user.referral #=> Refer::Referral
136 | ```
137 |
138 | To access a user's referrer, you can use `referrer`:
139 | ```ruby
140 | user.referrer #=> User that referred this User
141 | ```
142 | ## Refer with Devise
143 |
144 | To use Refer with Devise, you'll need to customize the Devise controller to track the referral after a successful registration.
145 |
146 | We can do this by telling Devise to use a custom controller in the routes and hooking into the `create` action to track the referral.
147 |
148 | ```ruby
149 | # config/routes.rb
150 | devise_for :users, controllers: { registrations: "users/registrations" }
151 | ```
152 |
153 | ```ruby
154 | # app/controllers/users/registrations_controller.rb
155 | class Users::RegistrationsController < Devise::RegistrationsController
156 | def create
157 | super do
158 | refer resource if resource.persisted?
159 | end
160 | end
161 | end
162 | ```
163 |
164 | ## Providing Referral Rewards
165 |
166 | There are several common ways of handling rewards for successful referrals:
167 |
168 | * Immediate rewards
169 | When the referral is successfully created, you can immediately credit the referrer with their reward.
170 |
171 | * Reward after user actions
172 | You can check if a user was referred after they complete the action and provide a reward to the referrer.
173 |
174 | * Time-based rewards
175 | To provide a reward X days after a successful referral, you can use a schedule job to check for referrals X days ago and provide rewards to those referrers.
176 |
177 | We recommend keeping records for each reward given to a referral so you can limit rewards.
178 |
179 | ## Customizing Models
180 |
181 | You can add features to Refer's models by using lazy load hooks.
182 |
183 | ```ruby
184 | # config/initializers/refer.rb
185 | ActiveSupport.on_load :refer_referral do
186 | # Add features to Refer::Referral model
187 | end
188 |
189 | ActiveSupport.on_load :refer_referral_code do
190 | # Add features to Refer::ReferralCode model
191 | end
192 |
193 | ActiveSupport.on_load :refer_visit do
194 | # Add features to Refer::Visit model
195 | end
196 | ```
197 |
198 | ## 🙏 Contributing
199 | If you have an issue you'd like to submit, please do so using the issue tracker in GitHub. In order for us to help you in the best way possible, please be as detailed as you can.
200 |
201 | ## 📝 License
202 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
203 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/setup"
2 |
3 | APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4 | load "rails/tasks/engine.rake"
5 |
6 | load "rails/tasks/statistics.rake"
7 |
8 | require "bundler/gem_tasks"
9 |
--------------------------------------------------------------------------------
/app/controllers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/excid3/refer/a64a19487a2b1da31e6875bc19eee0be5e42703d/app/controllers/.keep
--------------------------------------------------------------------------------
/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/excid3/refer/a64a19487a2b1da31e6875bc19eee0be5e42703d/app/controllers/concerns/.keep
--------------------------------------------------------------------------------
/app/models/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/excid3/refer/a64a19487a2b1da31e6875bc19eee0be5e42703d/app/models/.keep
--------------------------------------------------------------------------------
/app/models/refer/application_record.rb:
--------------------------------------------------------------------------------
1 | module Refer
2 | class ApplicationRecord < ActiveRecord::Base
3 | self.abstract_class = true
4 | self.table_name_prefix = "refer_"
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/app/models/refer/referral.rb:
--------------------------------------------------------------------------------
1 | module Refer
2 | class Referral < ApplicationRecord
3 | belongs_to :referrer, polymorphic: true
4 | belongs_to :referee, polymorphic: true
5 | belongs_to :referral_code, optional: true, counter_cache: true
6 |
7 | scope :completed, -> { where.not(completed_at: nil) }
8 |
9 | before_validation do
10 | self.referrer = referral_code&.referrer
11 | end
12 |
13 | validate :ensure_not_self_referral
14 |
15 | def ensure_not_self_referral
16 | errors.add(:base, "Self-referrals are not allowed") if referrer == referee
17 | end
18 |
19 | def complete!(**attributes)
20 | return if completed_at?
21 |
22 | update attributes.with_defaults(completed_at: Time.current)
23 | Refer.referral_completed&.call(self)
24 | end
25 | end
26 | end
27 |
28 | ActiveSupport.run_load_hooks :refer_referral, Refer::Referral
29 |
--------------------------------------------------------------------------------
/app/models/refer/referral_code.rb:
--------------------------------------------------------------------------------
1 | module Refer
2 | class ReferralCode < ApplicationRecord
3 | belongs_to :referrer, polymorphic: true
4 | has_many :referrals, dependent: :nullify
5 | has_many :visits, dependent: :delete_all
6 |
7 | validates :code, presence: true, uniqueness: true
8 |
9 | before_validation if: -> { !code? && Refer.code_generator } do
10 | self.code = Refer.code_generator.call(referrer)
11 | end
12 |
13 | def to_param
14 | code
15 | end
16 |
17 | def track_visit(request)
18 | visits.from_request(request).save
19 | end
20 | end
21 | end
22 |
23 | ActiveSupport.run_load_hooks :refer_referral_code, Refer::ReferralCode
24 |
--------------------------------------------------------------------------------
/app/models/refer/visit.rb:
--------------------------------------------------------------------------------
1 | module Refer
2 | class Visit < ApplicationRecord
3 | belongs_to :referral_code, counter_cache: true
4 |
5 | normalizes :ip, with: -> { Refer.mask_ip(_1) }
6 |
7 | def self.from_request(request)
8 | new(
9 | ip: request.ip,
10 | user_agent: request.user_agent,
11 | referrer: request.referrer,
12 | referring_domain: (URI.parse(request.referrer).try(:host) rescue nil)
13 | )
14 | end
15 | end
16 | end
17 |
18 | ActiveSupport.run_load_hooks :refer_visit, Refer::Visit
19 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # This command will automatically be run when you run "rails" with Rails gems
3 | # installed from the root of your application.
4 |
5 | ENGINE_ROOT = File.expand_path("..", __dir__)
6 | ENGINE_PATH = File.expand_path("../lib/refer/engine", __dir__)
7 | APP_PATH = File.expand_path("../test/dummy/config/application", __dir__)
8 |
9 | # Set up gems listed in the Gemfile.
10 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
11 | require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"])
12 |
13 | require "rails/all"
14 | require "rails/engine/commands"
15 |
--------------------------------------------------------------------------------
/bin/rubocop:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require "rubygems"
3 | require "bundler/setup"
4 |
5 | # explicit rubocop config increases performance slightly while avoiding config confusion.
6 | ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__))
7 |
8 | load Gem.bin_path("rubocop", "rubocop")
9 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | end
3 |
--------------------------------------------------------------------------------
/db/migrate/20240611180738_create_refer_referrals.rb:
--------------------------------------------------------------------------------
1 | class CreateReferReferrals < ActiveRecord::Migration[6.1]
2 | def change
3 | create_table :refer_referrals do |t|
4 | t.belongs_to :referrer, polymorphic: true, null: false
5 | t.belongs_to :referee, polymorphic: true, null: false
6 | t.belongs_to :referral_code
7 |
8 | t.timestamps
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/db/migrate/20240611183349_create_refer_referral_codes.rb:
--------------------------------------------------------------------------------
1 | class CreateReferReferralCodes < ActiveRecord::Migration[6.1]
2 | def change
3 | create_table :refer_referral_codes do |t|
4 | t.belongs_to :referrer, polymorphic: true, null: false
5 | t.string :code, null: false, index: { unique: true }
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/db/migrate/20240701172643_create_refer_visits.rb:
--------------------------------------------------------------------------------
1 | class CreateReferVisits < ActiveRecord::Migration[6.1]
2 | def change
3 | create_table :refer_visits do |t|
4 | t.belongs_to :referral_code, null: false, foreign_key: { to_table: :refer_referral_codes }
5 | t.string :ip
6 | t.text :user_agent
7 | t.text :referrer
8 | t.string :referring_domain
9 |
10 | t.timestamps
11 | end
12 |
13 | add_column :refer_referral_codes, :referrals_count, :integer, default: 0
14 | add_column :refer_referral_codes, :visits_count, :integer, default: 0
15 | add_column :refer_referrals, :completed_at, :datetime
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/gemfiles/rails_7.1.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "puma"
6 | gem "sqlite3"
7 | gem "sprockets-rails"
8 | gem "rubocop-rails-omakase", require: false
9 | gem "appraisal"
10 | gem "rails", "~> 7.1.0"
11 |
12 | gemspec path: "../"
13 |
--------------------------------------------------------------------------------
/gemfiles/rails_7.1.gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ..
3 | specs:
4 | refer (1.0.3)
5 | rails (>= 7.1.0)
6 |
7 | GEM
8 | remote: https://rubygems.org/
9 | specs:
10 | actioncable (7.1.5.1)
11 | actionpack (= 7.1.5.1)
12 | activesupport (= 7.1.5.1)
13 | nio4r (~> 2.0)
14 | websocket-driver (>= 0.6.1)
15 | zeitwerk (~> 2.6)
16 | actionmailbox (7.1.5.1)
17 | actionpack (= 7.1.5.1)
18 | activejob (= 7.1.5.1)
19 | activerecord (= 7.1.5.1)
20 | activestorage (= 7.1.5.1)
21 | activesupport (= 7.1.5.1)
22 | mail (>= 2.7.1)
23 | net-imap
24 | net-pop
25 | net-smtp
26 | actionmailer (7.1.5.1)
27 | actionpack (= 7.1.5.1)
28 | actionview (= 7.1.5.1)
29 | activejob (= 7.1.5.1)
30 | activesupport (= 7.1.5.1)
31 | mail (~> 2.5, >= 2.5.4)
32 | net-imap
33 | net-pop
34 | net-smtp
35 | rails-dom-testing (~> 2.2)
36 | actionpack (7.1.5.1)
37 | actionview (= 7.1.5.1)
38 | activesupport (= 7.1.5.1)
39 | nokogiri (>= 1.8.5)
40 | racc
41 | rack (>= 2.2.4)
42 | rack-session (>= 1.0.1)
43 | rack-test (>= 0.6.3)
44 | rails-dom-testing (~> 2.2)
45 | rails-html-sanitizer (~> 1.6)
46 | actiontext (7.1.5.1)
47 | actionpack (= 7.1.5.1)
48 | activerecord (= 7.1.5.1)
49 | activestorage (= 7.1.5.1)
50 | activesupport (= 7.1.5.1)
51 | globalid (>= 0.6.0)
52 | nokogiri (>= 1.8.5)
53 | actionview (7.1.5.1)
54 | activesupport (= 7.1.5.1)
55 | builder (~> 3.1)
56 | erubi (~> 1.11)
57 | rails-dom-testing (~> 2.2)
58 | rails-html-sanitizer (~> 1.6)
59 | activejob (7.1.5.1)
60 | activesupport (= 7.1.5.1)
61 | globalid (>= 0.3.6)
62 | activemodel (7.1.5.1)
63 | activesupport (= 7.1.5.1)
64 | activerecord (7.1.5.1)
65 | activemodel (= 7.1.5.1)
66 | activesupport (= 7.1.5.1)
67 | timeout (>= 0.4.0)
68 | activestorage (7.1.5.1)
69 | actionpack (= 7.1.5.1)
70 | activejob (= 7.1.5.1)
71 | activerecord (= 7.1.5.1)
72 | activesupport (= 7.1.5.1)
73 | marcel (~> 1.0)
74 | activesupport (7.1.5.1)
75 | base64
76 | benchmark (>= 0.3)
77 | bigdecimal
78 | concurrent-ruby (~> 1.0, >= 1.0.2)
79 | connection_pool (>= 2.2.5)
80 | drb
81 | i18n (>= 1.6, < 2)
82 | logger (>= 1.4.2)
83 | minitest (>= 5.1)
84 | mutex_m
85 | securerandom (>= 0.3)
86 | tzinfo (~> 2.0)
87 | appraisal (2.5.0)
88 | bundler
89 | rake
90 | thor (>= 0.14.0)
91 | ast (2.4.3)
92 | base64 (0.2.0)
93 | benchmark (0.4.0)
94 | bigdecimal (3.1.9)
95 | builder (3.3.0)
96 | concurrent-ruby (1.3.5)
97 | connection_pool (2.5.2)
98 | crass (1.0.6)
99 | date (3.4.1)
100 | drb (2.2.1)
101 | erubi (1.13.1)
102 | globalid (1.2.1)
103 | activesupport (>= 6.1)
104 | i18n (1.14.7)
105 | concurrent-ruby (~> 1.0)
106 | io-console (0.8.0)
107 | irb (1.15.2)
108 | pp (>= 0.6.0)
109 | rdoc (>= 4.0.0)
110 | reline (>= 0.4.2)
111 | json (2.11.3)
112 | language_server-protocol (3.17.0.4)
113 | lint_roller (1.1.0)
114 | logger (1.7.0)
115 | loofah (2.24.0)
116 | crass (~> 1.0.2)
117 | nokogiri (>= 1.12.0)
118 | mail (2.8.1)
119 | mini_mime (>= 0.1.1)
120 | net-imap
121 | net-pop
122 | net-smtp
123 | marcel (1.0.4)
124 | mini_mime (1.1.5)
125 | minitest (5.25.5)
126 | mutex_m (0.3.0)
127 | net-imap (0.5.7)
128 | date
129 | net-protocol
130 | net-pop (0.1.2)
131 | net-protocol
132 | net-protocol (0.2.2)
133 | timeout
134 | net-smtp (0.5.1)
135 | net-protocol
136 | nio4r (2.7.4)
137 | nokogiri (1.18.8-arm64-darwin)
138 | racc (~> 1.4)
139 | parallel (1.27.0)
140 | parser (3.3.8.0)
141 | ast (~> 2.4.1)
142 | racc
143 | pp (0.6.2)
144 | prettyprint
145 | prettyprint (0.2.0)
146 | prism (1.4.0)
147 | psych (5.2.3)
148 | date
149 | stringio
150 | puma (6.6.0)
151 | nio4r (~> 2.0)
152 | racc (1.8.1)
153 | rack (3.1.13)
154 | rack-session (2.1.0)
155 | base64 (>= 0.1.0)
156 | rack (>= 3.0.0)
157 | rack-test (2.2.0)
158 | rack (>= 1.3)
159 | rackup (2.2.1)
160 | rack (>= 3)
161 | rails (7.1.5.1)
162 | actioncable (= 7.1.5.1)
163 | actionmailbox (= 7.1.5.1)
164 | actionmailer (= 7.1.5.1)
165 | actionpack (= 7.1.5.1)
166 | actiontext (= 7.1.5.1)
167 | actionview (= 7.1.5.1)
168 | activejob (= 7.1.5.1)
169 | activemodel (= 7.1.5.1)
170 | activerecord (= 7.1.5.1)
171 | activestorage (= 7.1.5.1)
172 | activesupport (= 7.1.5.1)
173 | bundler (>= 1.15.0)
174 | railties (= 7.1.5.1)
175 | rails-dom-testing (2.2.0)
176 | activesupport (>= 5.0.0)
177 | minitest
178 | nokogiri (>= 1.6)
179 | rails-html-sanitizer (1.6.2)
180 | loofah (~> 2.21)
181 | nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
182 | railties (7.1.5.1)
183 | actionpack (= 7.1.5.1)
184 | activesupport (= 7.1.5.1)
185 | irb
186 | rackup (>= 1.0.0)
187 | rake (>= 12.2)
188 | thor (~> 1.0, >= 1.2.2)
189 | zeitwerk (~> 2.6)
190 | rainbow (3.1.1)
191 | rake (13.2.1)
192 | rdoc (6.13.1)
193 | psych (>= 4.0.0)
194 | regexp_parser (2.10.0)
195 | reline (0.6.1)
196 | io-console (~> 0.5)
197 | rubocop (1.75.3)
198 | json (~> 2.3)
199 | language_server-protocol (~> 3.17.0.2)
200 | lint_roller (~> 1.1.0)
201 | parallel (~> 1.10)
202 | parser (>= 3.3.0.2)
203 | rainbow (>= 2.2.2, < 4.0)
204 | regexp_parser (>= 2.9.3, < 3.0)
205 | rubocop-ast (>= 1.44.0, < 2.0)
206 | ruby-progressbar (~> 1.7)
207 | unicode-display_width (>= 2.4.0, < 4.0)
208 | rubocop-ast (1.44.1)
209 | parser (>= 3.3.7.2)
210 | prism (~> 1.4)
211 | rubocop-performance (1.25.0)
212 | lint_roller (~> 1.1)
213 | rubocop (>= 1.75.0, < 2.0)
214 | rubocop-ast (>= 1.38.0, < 2.0)
215 | rubocop-rails (2.31.0)
216 | activesupport (>= 4.2.0)
217 | lint_roller (~> 1.1)
218 | rack (>= 1.1)
219 | rubocop (>= 1.75.0, < 2.0)
220 | rubocop-ast (>= 1.38.0, < 2.0)
221 | rubocop-rails-omakase (1.1.0)
222 | rubocop (>= 1.72)
223 | rubocop-performance (>= 1.24)
224 | rubocop-rails (>= 2.30)
225 | ruby-progressbar (1.13.0)
226 | securerandom (0.4.1)
227 | sprockets (4.2.2)
228 | concurrent-ruby (~> 1.0)
229 | logger
230 | rack (>= 2.2.4, < 4)
231 | sprockets-rails (3.5.2)
232 | actionpack (>= 6.1)
233 | activesupport (>= 6.1)
234 | sprockets (>= 3.0.0)
235 | sqlite3 (2.6.0-arm64-darwin)
236 | stringio (3.1.7)
237 | thor (1.3.2)
238 | timeout (0.4.3)
239 | tzinfo (2.0.6)
240 | concurrent-ruby (~> 1.0)
241 | unicode-display_width (3.1.4)
242 | unicode-emoji (~> 4.0, >= 4.0.4)
243 | unicode-emoji (4.0.4)
244 | websocket-driver (0.7.7)
245 | base64
246 | websocket-extensions (>= 0.1.0)
247 | websocket-extensions (0.1.5)
248 | zeitwerk (2.7.2)
249 |
250 | PLATFORMS
251 | arm64-darwin
252 |
253 | DEPENDENCIES
254 | appraisal
255 | puma
256 | rails (~> 7.1.0)
257 | refer!
258 | rubocop-rails-omakase
259 | sprockets-rails
260 | sqlite3
261 |
262 | BUNDLED WITH
263 | 2.5.18
264 |
--------------------------------------------------------------------------------
/gemfiles/rails_7.2.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "puma"
6 | gem "sqlite3"
7 | gem "sprockets-rails"
8 | gem "rubocop-rails-omakase", require: false
9 | gem "appraisal"
10 | gem "rails", "~> 7.2.0"
11 |
12 | gemspec path: "../"
13 |
--------------------------------------------------------------------------------
/gemfiles/rails_7.2.gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ..
3 | specs:
4 | refer (1.0.3)
5 | rails (>= 7.1.0)
6 |
7 | GEM
8 | remote: https://rubygems.org/
9 | specs:
10 | actioncable (7.2.2.1)
11 | actionpack (= 7.2.2.1)
12 | activesupport (= 7.2.2.1)
13 | nio4r (~> 2.0)
14 | websocket-driver (>= 0.6.1)
15 | zeitwerk (~> 2.6)
16 | actionmailbox (7.2.2.1)
17 | actionpack (= 7.2.2.1)
18 | activejob (= 7.2.2.1)
19 | activerecord (= 7.2.2.1)
20 | activestorage (= 7.2.2.1)
21 | activesupport (= 7.2.2.1)
22 | mail (>= 2.8.0)
23 | actionmailer (7.2.2.1)
24 | actionpack (= 7.2.2.1)
25 | actionview (= 7.2.2.1)
26 | activejob (= 7.2.2.1)
27 | activesupport (= 7.2.2.1)
28 | mail (>= 2.8.0)
29 | rails-dom-testing (~> 2.2)
30 | actionpack (7.2.2.1)
31 | actionview (= 7.2.2.1)
32 | activesupport (= 7.2.2.1)
33 | nokogiri (>= 1.8.5)
34 | racc
35 | rack (>= 2.2.4, < 3.2)
36 | rack-session (>= 1.0.1)
37 | rack-test (>= 0.6.3)
38 | rails-dom-testing (~> 2.2)
39 | rails-html-sanitizer (~> 1.6)
40 | useragent (~> 0.16)
41 | actiontext (7.2.2.1)
42 | actionpack (= 7.2.2.1)
43 | activerecord (= 7.2.2.1)
44 | activestorage (= 7.2.2.1)
45 | activesupport (= 7.2.2.1)
46 | globalid (>= 0.6.0)
47 | nokogiri (>= 1.8.5)
48 | actionview (7.2.2.1)
49 | activesupport (= 7.2.2.1)
50 | builder (~> 3.1)
51 | erubi (~> 1.11)
52 | rails-dom-testing (~> 2.2)
53 | rails-html-sanitizer (~> 1.6)
54 | activejob (7.2.2.1)
55 | activesupport (= 7.2.2.1)
56 | globalid (>= 0.3.6)
57 | activemodel (7.2.2.1)
58 | activesupport (= 7.2.2.1)
59 | activerecord (7.2.2.1)
60 | activemodel (= 7.2.2.1)
61 | activesupport (= 7.2.2.1)
62 | timeout (>= 0.4.0)
63 | activestorage (7.2.2.1)
64 | actionpack (= 7.2.2.1)
65 | activejob (= 7.2.2.1)
66 | activerecord (= 7.2.2.1)
67 | activesupport (= 7.2.2.1)
68 | marcel (~> 1.0)
69 | activesupport (7.2.2.1)
70 | base64
71 | benchmark (>= 0.3)
72 | bigdecimal
73 | concurrent-ruby (~> 1.0, >= 1.3.1)
74 | connection_pool (>= 2.2.5)
75 | drb
76 | i18n (>= 1.6, < 2)
77 | logger (>= 1.4.2)
78 | minitest (>= 5.1)
79 | securerandom (>= 0.3)
80 | tzinfo (~> 2.0, >= 2.0.5)
81 | appraisal (2.5.0)
82 | bundler
83 | rake
84 | thor (>= 0.14.0)
85 | ast (2.4.3)
86 | base64 (0.2.0)
87 | benchmark (0.4.0)
88 | bigdecimal (3.1.9)
89 | builder (3.3.0)
90 | concurrent-ruby (1.3.5)
91 | connection_pool (2.5.2)
92 | crass (1.0.6)
93 | date (3.4.1)
94 | drb (2.2.1)
95 | erubi (1.13.1)
96 | globalid (1.2.1)
97 | activesupport (>= 6.1)
98 | i18n (1.14.7)
99 | concurrent-ruby (~> 1.0)
100 | io-console (0.8.0)
101 | irb (1.15.2)
102 | pp (>= 0.6.0)
103 | rdoc (>= 4.0.0)
104 | reline (>= 0.4.2)
105 | json (2.11.3)
106 | language_server-protocol (3.17.0.4)
107 | lint_roller (1.1.0)
108 | logger (1.7.0)
109 | loofah (2.24.0)
110 | crass (~> 1.0.2)
111 | nokogiri (>= 1.12.0)
112 | mail (2.8.1)
113 | mini_mime (>= 0.1.1)
114 | net-imap
115 | net-pop
116 | net-smtp
117 | marcel (1.0.4)
118 | mini_mime (1.1.5)
119 | minitest (5.25.5)
120 | net-imap (0.5.7)
121 | date
122 | net-protocol
123 | net-pop (0.1.2)
124 | net-protocol
125 | net-protocol (0.2.2)
126 | timeout
127 | net-smtp (0.5.1)
128 | net-protocol
129 | nio4r (2.7.4)
130 | nokogiri (1.18.8-arm64-darwin)
131 | racc (~> 1.4)
132 | parallel (1.27.0)
133 | parser (3.3.8.0)
134 | ast (~> 2.4.1)
135 | racc
136 | pp (0.6.2)
137 | prettyprint
138 | prettyprint (0.2.0)
139 | prism (1.4.0)
140 | psych (5.2.3)
141 | date
142 | stringio
143 | puma (6.6.0)
144 | nio4r (~> 2.0)
145 | racc (1.8.1)
146 | rack (3.1.13)
147 | rack-session (2.1.0)
148 | base64 (>= 0.1.0)
149 | rack (>= 3.0.0)
150 | rack-test (2.2.0)
151 | rack (>= 1.3)
152 | rackup (2.2.1)
153 | rack (>= 3)
154 | rails (7.2.2.1)
155 | actioncable (= 7.2.2.1)
156 | actionmailbox (= 7.2.2.1)
157 | actionmailer (= 7.2.2.1)
158 | actionpack (= 7.2.2.1)
159 | actiontext (= 7.2.2.1)
160 | actionview (= 7.2.2.1)
161 | activejob (= 7.2.2.1)
162 | activemodel (= 7.2.2.1)
163 | activerecord (= 7.2.2.1)
164 | activestorage (= 7.2.2.1)
165 | activesupport (= 7.2.2.1)
166 | bundler (>= 1.15.0)
167 | railties (= 7.2.2.1)
168 | rails-dom-testing (2.2.0)
169 | activesupport (>= 5.0.0)
170 | minitest
171 | nokogiri (>= 1.6)
172 | rails-html-sanitizer (1.6.2)
173 | loofah (~> 2.21)
174 | nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
175 | railties (7.2.2.1)
176 | actionpack (= 7.2.2.1)
177 | activesupport (= 7.2.2.1)
178 | irb (~> 1.13)
179 | rackup (>= 1.0.0)
180 | rake (>= 12.2)
181 | thor (~> 1.0, >= 1.2.2)
182 | zeitwerk (~> 2.6)
183 | rainbow (3.1.1)
184 | rake (13.2.1)
185 | rdoc (6.13.1)
186 | psych (>= 4.0.0)
187 | regexp_parser (2.10.0)
188 | reline (0.6.1)
189 | io-console (~> 0.5)
190 | rubocop (1.75.3)
191 | json (~> 2.3)
192 | language_server-protocol (~> 3.17.0.2)
193 | lint_roller (~> 1.1.0)
194 | parallel (~> 1.10)
195 | parser (>= 3.3.0.2)
196 | rainbow (>= 2.2.2, < 4.0)
197 | regexp_parser (>= 2.9.3, < 3.0)
198 | rubocop-ast (>= 1.44.0, < 2.0)
199 | ruby-progressbar (~> 1.7)
200 | unicode-display_width (>= 2.4.0, < 4.0)
201 | rubocop-ast (1.44.1)
202 | parser (>= 3.3.7.2)
203 | prism (~> 1.4)
204 | rubocop-performance (1.25.0)
205 | lint_roller (~> 1.1)
206 | rubocop (>= 1.75.0, < 2.0)
207 | rubocop-ast (>= 1.38.0, < 2.0)
208 | rubocop-rails (2.31.0)
209 | activesupport (>= 4.2.0)
210 | lint_roller (~> 1.1)
211 | rack (>= 1.1)
212 | rubocop (>= 1.75.0, < 2.0)
213 | rubocop-ast (>= 1.38.0, < 2.0)
214 | rubocop-rails-omakase (1.1.0)
215 | rubocop (>= 1.72)
216 | rubocop-performance (>= 1.24)
217 | rubocop-rails (>= 2.30)
218 | ruby-progressbar (1.13.0)
219 | securerandom (0.4.1)
220 | sprockets (4.2.2)
221 | concurrent-ruby (~> 1.0)
222 | logger
223 | rack (>= 2.2.4, < 4)
224 | sprockets-rails (3.5.2)
225 | actionpack (>= 6.1)
226 | activesupport (>= 6.1)
227 | sprockets (>= 3.0.0)
228 | sqlite3 (2.6.0-arm64-darwin)
229 | stringio (3.1.7)
230 | thor (1.3.2)
231 | timeout (0.4.3)
232 | tzinfo (2.0.6)
233 | concurrent-ruby (~> 1.0)
234 | unicode-display_width (3.1.4)
235 | unicode-emoji (~> 4.0, >= 4.0.4)
236 | unicode-emoji (4.0.4)
237 | useragent (0.16.11)
238 | websocket-driver (0.7.7)
239 | base64
240 | websocket-extensions (>= 0.1.0)
241 | websocket-extensions (0.1.5)
242 | zeitwerk (2.7.2)
243 |
244 | PLATFORMS
245 | arm64-darwin
246 |
247 | DEPENDENCIES
248 | appraisal
249 | puma
250 | rails (~> 7.2.0)
251 | refer!
252 | rubocop-rails-omakase
253 | sprockets-rails
254 | sqlite3
255 |
256 | BUNDLED WITH
257 | 2.5.18
258 |
--------------------------------------------------------------------------------
/gemfiles/rails_8.0.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "puma"
6 | gem "sqlite3"
7 | gem "sprockets-rails"
8 | gem "rubocop-rails-omakase", require: false
9 | gem "appraisal"
10 | gem "rails", "~> 8.0.0"
11 |
12 | gemspec path: "../"
13 |
--------------------------------------------------------------------------------
/gemfiles/rails_8.0.gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ..
3 | specs:
4 | refer (1.0.3)
5 | rails (>= 7.1.0)
6 |
7 | GEM
8 | remote: https://rubygems.org/
9 | specs:
10 | actioncable (8.0.2)
11 | actionpack (= 8.0.2)
12 | activesupport (= 8.0.2)
13 | nio4r (~> 2.0)
14 | websocket-driver (>= 0.6.1)
15 | zeitwerk (~> 2.6)
16 | actionmailbox (8.0.2)
17 | actionpack (= 8.0.2)
18 | activejob (= 8.0.2)
19 | activerecord (= 8.0.2)
20 | activestorage (= 8.0.2)
21 | activesupport (= 8.0.2)
22 | mail (>= 2.8.0)
23 | actionmailer (8.0.2)
24 | actionpack (= 8.0.2)
25 | actionview (= 8.0.2)
26 | activejob (= 8.0.2)
27 | activesupport (= 8.0.2)
28 | mail (>= 2.8.0)
29 | rails-dom-testing (~> 2.2)
30 | actionpack (8.0.2)
31 | actionview (= 8.0.2)
32 | activesupport (= 8.0.2)
33 | nokogiri (>= 1.8.5)
34 | rack (>= 2.2.4)
35 | rack-session (>= 1.0.1)
36 | rack-test (>= 0.6.3)
37 | rails-dom-testing (~> 2.2)
38 | rails-html-sanitizer (~> 1.6)
39 | useragent (~> 0.16)
40 | actiontext (8.0.2)
41 | actionpack (= 8.0.2)
42 | activerecord (= 8.0.2)
43 | activestorage (= 8.0.2)
44 | activesupport (= 8.0.2)
45 | globalid (>= 0.6.0)
46 | nokogiri (>= 1.8.5)
47 | actionview (8.0.2)
48 | activesupport (= 8.0.2)
49 | builder (~> 3.1)
50 | erubi (~> 1.11)
51 | rails-dom-testing (~> 2.2)
52 | rails-html-sanitizer (~> 1.6)
53 | activejob (8.0.2)
54 | activesupport (= 8.0.2)
55 | globalid (>= 0.3.6)
56 | activemodel (8.0.2)
57 | activesupport (= 8.0.2)
58 | activerecord (8.0.2)
59 | activemodel (= 8.0.2)
60 | activesupport (= 8.0.2)
61 | timeout (>= 0.4.0)
62 | activestorage (8.0.2)
63 | actionpack (= 8.0.2)
64 | activejob (= 8.0.2)
65 | activerecord (= 8.0.2)
66 | activesupport (= 8.0.2)
67 | marcel (~> 1.0)
68 | activesupport (8.0.2)
69 | base64
70 | benchmark (>= 0.3)
71 | bigdecimal
72 | concurrent-ruby (~> 1.0, >= 1.3.1)
73 | connection_pool (>= 2.2.5)
74 | drb
75 | i18n (>= 1.6, < 2)
76 | logger (>= 1.4.2)
77 | minitest (>= 5.1)
78 | securerandom (>= 0.3)
79 | tzinfo (~> 2.0, >= 2.0.5)
80 | uri (>= 0.13.1)
81 | appraisal (2.5.0)
82 | bundler
83 | rake
84 | thor (>= 0.14.0)
85 | ast (2.4.3)
86 | base64 (0.2.0)
87 | benchmark (0.4.0)
88 | bigdecimal (3.1.9)
89 | builder (3.3.0)
90 | concurrent-ruby (1.3.5)
91 | connection_pool (2.5.2)
92 | crass (1.0.6)
93 | date (3.4.1)
94 | drb (2.2.1)
95 | erubi (1.13.1)
96 | globalid (1.2.1)
97 | activesupport (>= 6.1)
98 | i18n (1.14.7)
99 | concurrent-ruby (~> 1.0)
100 | io-console (0.8.0)
101 | irb (1.15.2)
102 | pp (>= 0.6.0)
103 | rdoc (>= 4.0.0)
104 | reline (>= 0.4.2)
105 | json (2.11.3)
106 | language_server-protocol (3.17.0.4)
107 | lint_roller (1.1.0)
108 | logger (1.7.0)
109 | loofah (2.24.0)
110 | crass (~> 1.0.2)
111 | nokogiri (>= 1.12.0)
112 | mail (2.8.1)
113 | mini_mime (>= 0.1.1)
114 | net-imap
115 | net-pop
116 | net-smtp
117 | marcel (1.0.4)
118 | mini_mime (1.1.5)
119 | minitest (5.25.5)
120 | net-imap (0.5.7)
121 | date
122 | net-protocol
123 | net-pop (0.1.2)
124 | net-protocol
125 | net-protocol (0.2.2)
126 | timeout
127 | net-smtp (0.5.1)
128 | net-protocol
129 | nio4r (2.7.4)
130 | nokogiri (1.18.8-arm64-darwin)
131 | racc (~> 1.4)
132 | parallel (1.27.0)
133 | parser (3.3.8.0)
134 | ast (~> 2.4.1)
135 | racc
136 | pp (0.6.2)
137 | prettyprint
138 | prettyprint (0.2.0)
139 | prism (1.4.0)
140 | psych (5.2.3)
141 | date
142 | stringio
143 | puma (6.6.0)
144 | nio4r (~> 2.0)
145 | racc (1.8.1)
146 | rack (3.1.13)
147 | rack-session (2.1.0)
148 | base64 (>= 0.1.0)
149 | rack (>= 3.0.0)
150 | rack-test (2.2.0)
151 | rack (>= 1.3)
152 | rackup (2.2.1)
153 | rack (>= 3)
154 | rails (8.0.2)
155 | actioncable (= 8.0.2)
156 | actionmailbox (= 8.0.2)
157 | actionmailer (= 8.0.2)
158 | actionpack (= 8.0.2)
159 | actiontext (= 8.0.2)
160 | actionview (= 8.0.2)
161 | activejob (= 8.0.2)
162 | activemodel (= 8.0.2)
163 | activerecord (= 8.0.2)
164 | activestorage (= 8.0.2)
165 | activesupport (= 8.0.2)
166 | bundler (>= 1.15.0)
167 | railties (= 8.0.2)
168 | rails-dom-testing (2.2.0)
169 | activesupport (>= 5.0.0)
170 | minitest
171 | nokogiri (>= 1.6)
172 | rails-html-sanitizer (1.6.2)
173 | loofah (~> 2.21)
174 | nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
175 | railties (8.0.2)
176 | actionpack (= 8.0.2)
177 | activesupport (= 8.0.2)
178 | irb (~> 1.13)
179 | rackup (>= 1.0.0)
180 | rake (>= 12.2)
181 | thor (~> 1.0, >= 1.2.2)
182 | zeitwerk (~> 2.6)
183 | rainbow (3.1.1)
184 | rake (13.2.1)
185 | rdoc (6.13.1)
186 | psych (>= 4.0.0)
187 | regexp_parser (2.10.0)
188 | reline (0.6.1)
189 | io-console (~> 0.5)
190 | rubocop (1.75.3)
191 | json (~> 2.3)
192 | language_server-protocol (~> 3.17.0.2)
193 | lint_roller (~> 1.1.0)
194 | parallel (~> 1.10)
195 | parser (>= 3.3.0.2)
196 | rainbow (>= 2.2.2, < 4.0)
197 | regexp_parser (>= 2.9.3, < 3.0)
198 | rubocop-ast (>= 1.44.0, < 2.0)
199 | ruby-progressbar (~> 1.7)
200 | unicode-display_width (>= 2.4.0, < 4.0)
201 | rubocop-ast (1.44.1)
202 | parser (>= 3.3.7.2)
203 | prism (~> 1.4)
204 | rubocop-performance (1.25.0)
205 | lint_roller (~> 1.1)
206 | rubocop (>= 1.75.0, < 2.0)
207 | rubocop-ast (>= 1.38.0, < 2.0)
208 | rubocop-rails (2.31.0)
209 | activesupport (>= 4.2.0)
210 | lint_roller (~> 1.1)
211 | rack (>= 1.1)
212 | rubocop (>= 1.75.0, < 2.0)
213 | rubocop-ast (>= 1.38.0, < 2.0)
214 | rubocop-rails-omakase (1.1.0)
215 | rubocop (>= 1.72)
216 | rubocop-performance (>= 1.24)
217 | rubocop-rails (>= 2.30)
218 | ruby-progressbar (1.13.0)
219 | securerandom (0.4.1)
220 | sprockets (4.2.2)
221 | concurrent-ruby (~> 1.0)
222 | logger
223 | rack (>= 2.2.4, < 4)
224 | sprockets-rails (3.5.2)
225 | actionpack (>= 6.1)
226 | activesupport (>= 6.1)
227 | sprockets (>= 3.0.0)
228 | sqlite3 (2.6.0-arm64-darwin)
229 | stringio (3.1.7)
230 | thor (1.3.2)
231 | timeout (0.4.3)
232 | tzinfo (2.0.6)
233 | concurrent-ruby (~> 1.0)
234 | unicode-display_width (3.1.4)
235 | unicode-emoji (~> 4.0, >= 4.0.4)
236 | unicode-emoji (4.0.4)
237 | uri (1.0.3)
238 | useragent (0.16.11)
239 | websocket-driver (0.7.7)
240 | base64
241 | websocket-extensions (>= 0.1.0)
242 | websocket-extensions (0.1.5)
243 | zeitwerk (2.7.2)
244 |
245 | PLATFORMS
246 | arm64-darwin
247 |
248 | DEPENDENCIES
249 | appraisal
250 | puma
251 | rails (~> 8.0.0)
252 | refer!
253 | rubocop-rails-omakase
254 | sprockets-rails
255 | sqlite3
256 |
257 | BUNDLED WITH
258 | 2.5.18
259 |
--------------------------------------------------------------------------------
/gemfiles/rails_main.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "puma"
6 | gem "sqlite3"
7 | gem "sprockets-rails"
8 | gem "rubocop-rails-omakase", require: false
9 | gem "appraisal"
10 | gem "rails", github: "rails/rails"
11 |
12 | gemspec path: "../"
13 |
--------------------------------------------------------------------------------
/gemfiles/rails_main.gemfile.lock:
--------------------------------------------------------------------------------
1 | GIT
2 | remote: https://github.com/rails/rails.git
3 | revision: 99e27fa586af7db2b5334124a62eb3a464cdffd8
4 | specs:
5 | actioncable (8.1.0.alpha)
6 | actionpack (= 8.1.0.alpha)
7 | activesupport (= 8.1.0.alpha)
8 | nio4r (~> 2.0)
9 | websocket-driver (>= 0.6.1)
10 | zeitwerk (~> 2.6)
11 | actionmailbox (8.1.0.alpha)
12 | actionpack (= 8.1.0.alpha)
13 | activejob (= 8.1.0.alpha)
14 | activerecord (= 8.1.0.alpha)
15 | activestorage (= 8.1.0.alpha)
16 | activesupport (= 8.1.0.alpha)
17 | mail (>= 2.8.0)
18 | actionmailer (8.1.0.alpha)
19 | actionpack (= 8.1.0.alpha)
20 | actionview (= 8.1.0.alpha)
21 | activejob (= 8.1.0.alpha)
22 | activesupport (= 8.1.0.alpha)
23 | mail (>= 2.8.0)
24 | rails-dom-testing (~> 2.2)
25 | actionpack (8.1.0.alpha)
26 | actionview (= 8.1.0.alpha)
27 | activesupport (= 8.1.0.alpha)
28 | nokogiri (>= 1.8.5)
29 | rack (>= 2.2.4)
30 | rack-session (>= 1.0.1)
31 | rack-test (>= 0.6.3)
32 | rails-dom-testing (~> 2.2)
33 | rails-html-sanitizer (~> 1.6)
34 | useragent (~> 0.16)
35 | actiontext (8.1.0.alpha)
36 | actionpack (= 8.1.0.alpha)
37 | activerecord (= 8.1.0.alpha)
38 | activestorage (= 8.1.0.alpha)
39 | activesupport (= 8.1.0.alpha)
40 | globalid (>= 0.6.0)
41 | nokogiri (>= 1.8.5)
42 | actionview (8.1.0.alpha)
43 | activesupport (= 8.1.0.alpha)
44 | builder (~> 3.1)
45 | erubi (~> 1.11)
46 | rails-dom-testing (~> 2.2)
47 | rails-html-sanitizer (~> 1.6)
48 | activejob (8.1.0.alpha)
49 | activesupport (= 8.1.0.alpha)
50 | globalid (>= 0.3.6)
51 | activemodel (8.1.0.alpha)
52 | activesupport (= 8.1.0.alpha)
53 | activerecord (8.1.0.alpha)
54 | activemodel (= 8.1.0.alpha)
55 | activesupport (= 8.1.0.alpha)
56 | timeout (>= 0.4.0)
57 | activestorage (8.1.0.alpha)
58 | actionpack (= 8.1.0.alpha)
59 | activejob (= 8.1.0.alpha)
60 | activerecord (= 8.1.0.alpha)
61 | activesupport (= 8.1.0.alpha)
62 | marcel (~> 1.0)
63 | activesupport (8.1.0.alpha)
64 | base64
65 | benchmark (>= 0.3)
66 | bigdecimal
67 | concurrent-ruby (~> 1.0, >= 1.3.1)
68 | connection_pool (>= 2.2.5)
69 | drb
70 | i18n (>= 1.6, < 2)
71 | logger (>= 1.4.2)
72 | minitest (>= 5.1)
73 | securerandom (>= 0.3)
74 | tzinfo (~> 2.0, >= 2.0.5)
75 | uri (>= 0.13.1)
76 | rails (8.1.0.alpha)
77 | actioncable (= 8.1.0.alpha)
78 | actionmailbox (= 8.1.0.alpha)
79 | actionmailer (= 8.1.0.alpha)
80 | actionpack (= 8.1.0.alpha)
81 | actiontext (= 8.1.0.alpha)
82 | actionview (= 8.1.0.alpha)
83 | activejob (= 8.1.0.alpha)
84 | activemodel (= 8.1.0.alpha)
85 | activerecord (= 8.1.0.alpha)
86 | activestorage (= 8.1.0.alpha)
87 | activesupport (= 8.1.0.alpha)
88 | bundler (>= 1.15.0)
89 | railties (= 8.1.0.alpha)
90 | railties (8.1.0.alpha)
91 | actionpack (= 8.1.0.alpha)
92 | activesupport (= 8.1.0.alpha)
93 | irb (~> 1.13)
94 | rackup (>= 1.0.0)
95 | rake (>= 12.2)
96 | thor (~> 1.0, >= 1.2.2)
97 | zeitwerk (~> 2.6)
98 |
99 | PATH
100 | remote: ..
101 | specs:
102 | refer (1.0.3)
103 | rails (>= 7.1.0)
104 |
105 | GEM
106 | remote: https://rubygems.org/
107 | specs:
108 | appraisal (2.5.0)
109 | bundler
110 | rake
111 | thor (>= 0.14.0)
112 | ast (2.4.3)
113 | base64 (0.2.0)
114 | benchmark (0.4.0)
115 | bigdecimal (3.1.9)
116 | builder (3.3.0)
117 | concurrent-ruby (1.3.5)
118 | connection_pool (2.5.2)
119 | crass (1.0.6)
120 | date (3.4.1)
121 | drb (2.2.1)
122 | erubi (1.13.1)
123 | globalid (1.2.1)
124 | activesupport (>= 6.1)
125 | i18n (1.14.7)
126 | concurrent-ruby (~> 1.0)
127 | io-console (0.8.0)
128 | irb (1.15.2)
129 | pp (>= 0.6.0)
130 | rdoc (>= 4.0.0)
131 | reline (>= 0.4.2)
132 | json (2.11.3)
133 | language_server-protocol (3.17.0.4)
134 | lint_roller (1.1.0)
135 | logger (1.7.0)
136 | loofah (2.24.0)
137 | crass (~> 1.0.2)
138 | nokogiri (>= 1.12.0)
139 | mail (2.8.1)
140 | mini_mime (>= 0.1.1)
141 | net-imap
142 | net-pop
143 | net-smtp
144 | marcel (1.0.4)
145 | mini_mime (1.1.5)
146 | mini_portile2 (2.8.8)
147 | minitest (5.25.5)
148 | net-imap (0.5.7)
149 | date
150 | net-protocol
151 | net-pop (0.1.2)
152 | net-protocol
153 | net-protocol (0.2.2)
154 | timeout
155 | net-smtp (0.5.1)
156 | net-protocol
157 | nio4r (2.7.4)
158 | nokogiri (1.18.8)
159 | mini_portile2 (~> 2.8.2)
160 | racc (~> 1.4)
161 | nokogiri (1.18.8-aarch64-linux-gnu)
162 | racc (~> 1.4)
163 | nokogiri (1.18.8-aarch64-linux-musl)
164 | racc (~> 1.4)
165 | nokogiri (1.18.8-arm-linux-gnu)
166 | racc (~> 1.4)
167 | nokogiri (1.18.8-arm-linux-musl)
168 | racc (~> 1.4)
169 | nokogiri (1.18.8-arm64-darwin)
170 | racc (~> 1.4)
171 | nokogiri (1.18.8-x86_64-darwin)
172 | racc (~> 1.4)
173 | nokogiri (1.18.8-x86_64-linux-gnu)
174 | racc (~> 1.4)
175 | nokogiri (1.18.8-x86_64-linux-musl)
176 | racc (~> 1.4)
177 | parallel (1.27.0)
178 | parser (3.3.8.0)
179 | ast (~> 2.4.1)
180 | racc
181 | pp (0.6.2)
182 | prettyprint
183 | prettyprint (0.2.0)
184 | prism (1.4.0)
185 | psych (5.2.3)
186 | date
187 | stringio
188 | puma (6.6.0)
189 | nio4r (~> 2.0)
190 | racc (1.8.1)
191 | rack (3.1.13)
192 | rack-session (2.1.0)
193 | base64 (>= 0.1.0)
194 | rack (>= 3.0.0)
195 | rack-test (2.2.0)
196 | rack (>= 1.3)
197 | rackup (2.2.1)
198 | rack (>= 3)
199 | rails-dom-testing (2.2.0)
200 | activesupport (>= 5.0.0)
201 | minitest
202 | nokogiri (>= 1.6)
203 | rails-html-sanitizer (1.6.2)
204 | loofah (~> 2.21)
205 | nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
206 | rainbow (3.1.1)
207 | rake (13.2.1)
208 | rdoc (6.13.1)
209 | psych (>= 4.0.0)
210 | regexp_parser (2.10.0)
211 | reline (0.6.1)
212 | io-console (~> 0.5)
213 | rubocop (1.75.3)
214 | json (~> 2.3)
215 | language_server-protocol (~> 3.17.0.2)
216 | lint_roller (~> 1.1.0)
217 | parallel (~> 1.10)
218 | parser (>= 3.3.0.2)
219 | rainbow (>= 2.2.2, < 4.0)
220 | regexp_parser (>= 2.9.3, < 3.0)
221 | rubocop-ast (>= 1.44.0, < 2.0)
222 | ruby-progressbar (~> 1.7)
223 | unicode-display_width (>= 2.4.0, < 4.0)
224 | rubocop-ast (1.44.1)
225 | parser (>= 3.3.7.2)
226 | prism (~> 1.4)
227 | rubocop-performance (1.25.0)
228 | lint_roller (~> 1.1)
229 | rubocop (>= 1.75.0, < 2.0)
230 | rubocop-ast (>= 1.38.0, < 2.0)
231 | rubocop-rails (2.31.0)
232 | activesupport (>= 4.2.0)
233 | lint_roller (~> 1.1)
234 | rack (>= 1.1)
235 | rubocop (>= 1.75.0, < 2.0)
236 | rubocop-ast (>= 1.38.0, < 2.0)
237 | rubocop-rails-omakase (1.1.0)
238 | rubocop (>= 1.72)
239 | rubocop-performance (>= 1.24)
240 | rubocop-rails (>= 2.30)
241 | ruby-progressbar (1.13.0)
242 | securerandom (0.4.1)
243 | sprockets (4.2.2)
244 | concurrent-ruby (~> 1.0)
245 | logger
246 | rack (>= 2.2.4, < 4)
247 | sprockets-rails (3.5.2)
248 | actionpack (>= 6.1)
249 | activesupport (>= 6.1)
250 | sprockets (>= 3.0.0)
251 | sqlite3 (2.6.0-aarch64-linux-gnu)
252 | sqlite3 (2.6.0-aarch64-linux-musl)
253 | sqlite3 (2.6.0-arm-linux-gnu)
254 | sqlite3 (2.6.0-arm-linux-musl)
255 | sqlite3 (2.6.0-arm64-darwin)
256 | sqlite3 (2.6.0-x86-linux-gnu)
257 | sqlite3 (2.6.0-x86-linux-musl)
258 | sqlite3 (2.6.0-x86_64-darwin)
259 | sqlite3 (2.6.0-x86_64-linux-gnu)
260 | sqlite3 (2.6.0-x86_64-linux-musl)
261 | stringio (3.1.7)
262 | thor (1.3.2)
263 | timeout (0.4.3)
264 | tzinfo (2.0.6)
265 | concurrent-ruby (~> 1.0)
266 | unicode-display_width (3.1.4)
267 | unicode-emoji (~> 4.0, >= 4.0.4)
268 | unicode-emoji (4.0.4)
269 | uri (1.0.3)
270 | useragent (0.16.11)
271 | websocket-driver (0.7.7)
272 | base64
273 | websocket-extensions (>= 0.1.0)
274 | websocket-extensions (0.1.5)
275 | zeitwerk (2.7.2)
276 |
277 | PLATFORMS
278 | aarch64-linux
279 | aarch64-linux-gnu
280 | aarch64-linux-musl
281 | arm-linux
282 | arm-linux-gnu
283 | arm-linux-musl
284 | arm64-darwin
285 | x86-linux
286 | x86-linux-gnu
287 | x86-linux-musl
288 | x86_64-darwin
289 | x86_64-linux
290 | x86_64-linux-gnu
291 | x86_64-linux-musl
292 |
293 | DEPENDENCIES
294 | appraisal
295 | puma
296 | rails!
297 | refer!
298 | rubocop-rails-omakase
299 | sprockets-rails
300 | sqlite3
301 |
302 | BUNDLED WITH
303 | 2.5.18
304 |
--------------------------------------------------------------------------------
/lib/generators/refer/install/USAGE:
--------------------------------------------------------------------------------
1 | Description:
2 | Adds Refer to ApplicationController to set referral cookies
3 |
4 | Example:
5 | bin/rails generate refer:install
6 |
7 | This will add `sets_refer_cookie` to the ApplicationController class.
8 |
9 |
--------------------------------------------------------------------------------
/lib/generators/refer/install/install_generator.rb:
--------------------------------------------------------------------------------
1 | class Refer::InstallGenerator < Rails::Generators::Base
2 | source_root File.expand_path("templates", __dir__)
3 |
4 | def add_refer
5 | inject_into_class File.join("app", "controllers", "application_controller.rb"), "ApplicationController", " set_referral_cookie\n"
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/lib/generators/refer/model/USAGE:
--------------------------------------------------------------------------------
1 | Description:
2 | Adds Refer associations to a model
3 |
4 | Example:
5 | bin/rails generate refer:model User
6 |
7 | This will add `has_referrals` to the User class.
8 |
--------------------------------------------------------------------------------
/lib/generators/refer/model/model_generator.rb:
--------------------------------------------------------------------------------
1 | class Refer::ModelGenerator < Rails::Generators::NamedBase
2 | source_root File.expand_path("templates", __dir__)
3 |
4 | def migrations
5 | rails_command "refer:install:migrations"
6 | end
7 |
8 | def add_refer
9 | inject_into_class File.join("app", "models", "#{file_path}.rb"), class_name, " has_referrals\n"
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/lib/refer.rb:
--------------------------------------------------------------------------------
1 | require "refer/version"
2 | require "refer/engine"
3 | require "securerandom"
4 |
5 | module Refer
6 | include ActiveSupport::Configurable
7 |
8 | autoload :Controller, "refer/controller"
9 | autoload :HasReferrals, "refer/has_referrals"
10 | autoload :Model, "refer/model"
11 |
12 | config_accessor :code_generator, default: ->(referrer) { SecureRandom.alphanumeric(8) }
13 | config_accessor :cookie_length, default: 30.days
14 | config_accessor :cookie_name, default: :refer_code
15 | config_accessor :param_name, default: :ref
16 | config_accessor :overwrite_cookie, default: true
17 | config_accessor :track_visits, default: true
18 | config_accessor :mask_ips, default: true
19 | config_accessor :referral_completed
20 |
21 | class Error < StandardError; end
22 | class AlreadyReferred < Error; end
23 |
24 | def self.referred?(referee)
25 | Referral.where(referee: referee).exists?
26 | end
27 |
28 | def self.refer(code:, referee:)
29 | return if referred?(referee)
30 | ReferralCode.find_by(code: code)&.referrals&.create(referee: referee)
31 | end
32 |
33 | def self.refer!(code:, referee:)
34 | raise AlreadyReferred, "#{referee} has already been referred" if referred?(referee)
35 | ReferralCode.find_by!(code: code).referrals.create!(referee: referee)
36 | end
37 |
38 | def self.cookie(code)
39 | {
40 | value: code,
41 | expires: Refer.cookie_length.from_now
42 | }
43 | end
44 |
45 | # From Ahoy gem: https://github.com/ankane/ahoy/blob/v5.1.0/lib/ahoy.rb#L133-L142
46 | def self.mask_ip(ip)
47 | return ip unless mask_ips
48 |
49 | addr = IPAddr.new(ip)
50 | if addr.ipv4?
51 | # set last octet to 0
52 | addr.mask(24).to_s
53 | else
54 | # set last 80 bits to zeros
55 | addr.mask(48).to_s
56 | end
57 | end
58 | end
59 |
60 | ActiveSupport.run_load_hooks(:refer, Refer)
61 |
--------------------------------------------------------------------------------
/lib/refer/controller.rb:
--------------------------------------------------------------------------------
1 | module Refer
2 | module Controller
3 | extend ActiveSupport::Concern
4 |
5 | class_methods do
6 | def set_referral_cookie(param_name: Refer.param_name, cookie_name: Refer.cookie_name, **options)
7 | before_action -> { set_refer_cookie(param_name: param_name, cookie_name: cookie_name) }, **options
8 | end
9 | end
10 |
11 | def refer(referee, cookie_name: Refer.cookie_name)
12 | Refer.refer(code: cookies[cookie_name], referee: referee)
13 | end
14 |
15 | private
16 |
17 | def set_refer_cookie(param_name: Refer.param_name, cookie_name: Refer.cookie_name, code: nil, track_visit: Refer.track_visits)
18 | code ||= params[param_name]
19 | return if code.blank?
20 |
21 | cookies[cookie_name] = Refer.cookie(code) if Refer.overwrite_cookie || cookies[cookie_name].blank?
22 | ReferralCode.find_by(code: code)&.track_visit(request) if track_visit
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/lib/refer/engine.rb:
--------------------------------------------------------------------------------
1 | module Refer
2 | class Engine < ::Rails::Engine
3 | isolate_namespace Refer
4 |
5 | initializer "refer.hooks" do
6 | ActiveSupport.on_load(:active_record) do
7 | include Refer::HasReferrals
8 | end
9 |
10 | ActiveSupport.on_load(:action_controller) do
11 | include Refer::Controller
12 | end
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/refer/has_referrals.rb:
--------------------------------------------------------------------------------
1 | module Refer
2 | module HasReferrals
3 | extend ActiveSupport::Concern
4 |
5 | class_methods do
6 | def has_referrals
7 | include Refer::Model
8 | end
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/lib/refer/model.rb:
--------------------------------------------------------------------------------
1 | module Refer
2 | module Model
3 | extend ActiveSupport::Concern
4 |
5 | included do
6 | has_many :referral_codes, as: :referrer, class_name: "Refer::ReferralCode", dependent: :destroy
7 | has_many :referrals, as: :referrer, class_name: "Refer::Referral", dependent: :destroy
8 | has_one :referral, as: :referee, class_name: "Refer::Referral", dependent: :destroy
9 | delegate :referrer, to: :referral, allow_nil: true
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/lib/refer/version.rb:
--------------------------------------------------------------------------------
1 | module Refer
2 | VERSION = "1.0.3"
3 | end
4 |
--------------------------------------------------------------------------------
/lib/tasks/refer_tasks.rake:
--------------------------------------------------------------------------------
1 | # desc "Explaining what the task does"
2 | # task :refer do
3 | # # Task goes here
4 | # end
5 |
--------------------------------------------------------------------------------
/refer.gemspec:
--------------------------------------------------------------------------------
1 | require_relative "lib/refer/version"
2 |
3 | Gem::Specification.new do |spec|
4 | spec.name = "refer"
5 | spec.version = Refer::VERSION
6 | spec.authors = [ "Chris Oliver" ]
7 | spec.email = [ "excid3@gmail.com" ]
8 | spec.homepage = "https://github.com/excid3/refer"
9 | spec.summary = "Referral codes & affiliate links for Ruby on Rails apps"
10 | spec.description = "Referral codes & affiliate links for Ruby on Rails apps"
11 | spec.license = "MIT"
12 |
13 | spec.metadata["homepage_uri"] = spec.homepage
14 | spec.metadata["source_code_uri"] = spec.homepage
15 | spec.metadata["changelog_uri"] = spec.homepage + "/blob/main/CHANGELOG.md"
16 |
17 | spec.files = Dir.chdir(File.expand_path(__dir__)) do
18 | Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]
19 | end
20 |
21 | spec.add_dependency "rails", ">= 7.1.0"
22 | end
23 |
--------------------------------------------------------------------------------
/test/dummy/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require_relative "config/application"
5 |
6 | Rails.application.load_tasks
7 |
--------------------------------------------------------------------------------
/test/dummy/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link_directory ../stylesheets .css
3 |
--------------------------------------------------------------------------------
/test/dummy/app/assets/images/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/excid3/refer/a64a19487a2b1da31e6875bc19eee0be5e42703d/test/dummy/app/assets/images/.keep
--------------------------------------------------------------------------------
/test/dummy/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10 | * files in this directory. Styles in this file should be added after the last require_* statement.
11 | * It is generally better to create a new file per style scope.
12 | *
13 | *= require_tree .
14 | *= require_self
15 | */
16 |
--------------------------------------------------------------------------------
/test/dummy/app/channels/application_cable/channel.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Channel < ActionCable::Channel::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/test/dummy/app/channels/application_cable/connection.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Connection < ActionCable::Connection::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/test/dummy/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | end
3 |
--------------------------------------------------------------------------------
/test/dummy/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/excid3/refer/a64a19487a2b1da31e6875bc19eee0be5e42703d/test/dummy/app/controllers/concerns/.keep
--------------------------------------------------------------------------------
/test/dummy/app/controllers/referrals_controller.rb:
--------------------------------------------------------------------------------
1 | class ReferralsController < ApplicationController
2 | set_referral_cookie
3 | before_action :set_user, only: [ :create ]
4 |
5 | def show
6 | head :ok
7 | end
8 |
9 | def create
10 | refer @user
11 | head :ok
12 | end
13 |
14 | private
15 |
16 | def set_user
17 | @user = User.find(params[:user_id])
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/test/dummy/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/test/dummy/app/jobs/application_job.rb:
--------------------------------------------------------------------------------
1 | class ApplicationJob < ActiveJob::Base
2 | # Automatically retry jobs that encountered a deadlock
3 | # retry_on ActiveRecord::Deadlocked
4 |
5 | # Most jobs are safe to ignore if the underlying records are no longer available
6 | # discard_on ActiveJob::DeserializationError
7 | end
8 |
--------------------------------------------------------------------------------
/test/dummy/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | class ApplicationMailer < ActionMailer::Base
2 | default from: "from@example.com"
3 | layout "mailer"
4 | end
5 |
--------------------------------------------------------------------------------
/test/dummy/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 | primary_abstract_class
3 | end
4 |
--------------------------------------------------------------------------------
/test/dummy/app/models/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/excid3/refer/a64a19487a2b1da31e6875bc19eee0be5e42703d/test/dummy/app/models/concerns/.keep
--------------------------------------------------------------------------------
/test/dummy/app/models/user.rb:
--------------------------------------------------------------------------------
1 | class User < ApplicationRecord
2 | has_referrals
3 | end
4 |
--------------------------------------------------------------------------------
/test/dummy/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%= content_for(:title) || "Dummy" %>
5 |
6 |
7 | <%= csrf_meta_tags %>
8 | <%= csp_meta_tag %>
9 |
10 | <%= yield :head %>
11 |
12 |
13 |
14 |
15 |
16 | <%= stylesheet_link_tag "application" %>
17 |
18 |
19 |
20 | <%= yield %>
21 |
22 |
23 |
--------------------------------------------------------------------------------
/test/dummy/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/test/dummy/app/views/layouts/mailer.text.erb:
--------------------------------------------------------------------------------
1 | <%= yield %>
2 |
--------------------------------------------------------------------------------
/test/dummy/app/views/pwa/manifest.json.erb:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Dummy",
3 | "icons": [
4 | {
5 | "src": "/icon.png",
6 | "type": "image/png",
7 | "sizes": "512x512"
8 | },
9 | {
10 | "src": "/icon.png",
11 | "type": "image/png",
12 | "sizes": "512x512",
13 | "purpose": "maskable"
14 | }
15 | ],
16 | "start_url": "/",
17 | "display": "standalone",
18 | "scope": "/",
19 | "description": "Dummy.",
20 | "theme_color": "red",
21 | "background_color": "red"
22 | }
23 |
--------------------------------------------------------------------------------
/test/dummy/app/views/pwa/service-worker.js:
--------------------------------------------------------------------------------
1 | // Add a service worker for processing Web Push notifications:
2 | //
3 | // self.addEventListener("push", async (event) => {
4 | // const { title, options } = await event.data.json()
5 | // event.waitUntil(self.registration.showNotification(title, options))
6 | // })
7 | //
8 | // self.addEventListener("notificationclick", function(event) {
9 | // event.notification.close()
10 | // event.waitUntil(
11 | // clients.matchAll({ type: "window" }).then((clientList) => {
12 | // for (let i = 0; i < clientList.length; i++) {
13 | // let client = clientList[i]
14 | // let clientPath = (new URL(client.url)).pathname
15 | //
16 | // if (clientPath == event.notification.data.path && "focus" in client) {
17 | // return client.focus()
18 | // }
19 | // }
20 | //
21 | // if (clients.openWindow) {
22 | // return clients.openWindow(event.notification.data.path)
23 | // }
24 | // })
25 | // )
26 | // })
27 |
--------------------------------------------------------------------------------
/test/dummy/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path("../config/application", __dir__)
3 | require_relative "../config/boot"
4 | require "rails/commands"
5 |
--------------------------------------------------------------------------------
/test/dummy/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative "../config/boot"
3 | require "rake"
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/test/dummy/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require "fileutils"
3 |
4 | APP_ROOT = File.expand_path("..", __dir__)
5 | APP_NAME = "dummy"
6 |
7 | def system!(*args)
8 | system(*args, exception: true)
9 | end
10 |
11 | FileUtils.chdir APP_ROOT do
12 | # This script is a way to set up or update your development environment automatically.
13 | # This script is idempotent, so that you can run it at any time and get an expectable outcome.
14 | # Add necessary setup steps to this file.
15 |
16 | puts "== Installing dependencies =="
17 | system! "gem install bundler --conservative"
18 | system("bundle check") || system!("bundle install")
19 |
20 | # puts "\n== Copying sample files =="
21 | # unless File.exist?("config/database.yml")
22 | # FileUtils.cp "config/database.yml.sample", "config/database.yml"
23 | # end
24 |
25 | puts "\n== Preparing database =="
26 | system! "bin/rails db:prepare"
27 |
28 | puts "\n== Removing old logs and tempfiles =="
29 | system! "bin/rails log:clear tmp:clear"
30 |
31 | puts "\n== Restarting application server =="
32 | system! "bin/rails restart"
33 |
34 | # puts "\n== Configuring puma-dev =="
35 | # system "ln -nfs #{APP_ROOT} ~/.puma-dev/#{APP_NAME}"
36 | # system "curl -Is https://#{APP_NAME}.test/up | head -n 1"
37 | end
38 |
--------------------------------------------------------------------------------
/test/dummy/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require_relative "config/environment"
4 |
5 | run Rails.application
6 | Rails.application.load_server
7 |
--------------------------------------------------------------------------------
/test/dummy/config/application.rb:
--------------------------------------------------------------------------------
1 | require_relative "boot"
2 |
3 | require "rails/all"
4 |
5 | # Require the gems listed in Gemfile, including any gems
6 | # you've limited to :test, :development, or :production.
7 | Bundler.require(*Rails.groups)
8 |
9 | module Dummy
10 | class Application < Rails::Application
11 | config.load_defaults Rails::VERSION::STRING.to_f
12 |
13 | # For compatibility with applications that use this config
14 | config.action_controller.include_all_helpers = false
15 |
16 | # Please, add to the `ignore` list any other `lib` subdirectories that do
17 | # not contain `.rb` files, or that should not be reloaded or eager loaded.
18 | # Common ones are `templates`, `generators`, or `middleware`, for example.
19 | config.autoload_lib(ignore: %w[assets tasks])
20 |
21 | # Configuration for the application, engines, and railties goes here.
22 | #
23 | # These settings can be overridden in specific environments using the files
24 | # in config/environments, which are processed later.
25 | #
26 | # config.time_zone = "Central Time (US & Canada)"
27 | # config.eager_load_paths << Rails.root.join("extras")
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/test/dummy/config/boot.rb:
--------------------------------------------------------------------------------
1 | # Set up gems listed in the Gemfile.
2 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile", __dir__)
3 |
4 | require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"])
5 | $LOAD_PATH.unshift File.expand_path("../../../lib", __dir__)
6 |
--------------------------------------------------------------------------------
/test/dummy/config/cable.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: async
3 |
4 | test:
5 | adapter: test
6 |
7 | production:
8 | adapter: redis
9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
10 | channel_prefix: dummy_production
11 |
--------------------------------------------------------------------------------
/test/dummy/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite. Versions 3.8.0 and up are supported.
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem "sqlite3"
6 | #
7 | default: &default
8 | adapter: sqlite3
9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
10 | timeout: 5000
11 |
12 | development:
13 | <<: *default
14 | database: storage/development.sqlite3
15 |
16 | # Warning: The database defined as "test" will be erased and
17 | # re-generated from your development database when you run "rake".
18 | # Do not set this db to the same as development or production.
19 | test:
20 | <<: *default
21 | database: storage/test.sqlite3
22 |
23 |
24 | # SQLite3 write its data on the local filesystem, as such it requires
25 | # persistent disks. If you are deploying to a managed service, you should
26 | # make sure it provides disk persistence, as many don't.
27 | #
28 | # Similarly, if you deploy your application as a Docker container, you must
29 | # ensure the database is located in a persisted volume.
30 | production:
31 | <<: *default
32 | # database: path/to/persistent/storage/production.sqlite3
33 |
--------------------------------------------------------------------------------
/test/dummy/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative "application"
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/test/dummy/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | require "active_support/core_ext/integer/time"
2 |
3 | Rails.application.configure do
4 | # Settings specified here will take precedence over those in config/application.rb.
5 |
6 | # In the development environment your application's code is reloaded any time
7 | # it changes. This slows down response time but is perfect for development
8 | # since you don't have to restart the web server when you make code changes.
9 | config.enable_reloading = true
10 |
11 | # Do not eager load code on boot.
12 | config.eager_load = false
13 |
14 | # Show full error reports.
15 | config.consider_all_requests_local = true
16 |
17 | # Enable server timing.
18 | config.server_timing = true
19 |
20 | # Enable/disable caching. By default caching is disabled.
21 | # Run rails dev:cache to toggle caching.
22 | if Rails.root.join("tmp/caching-dev.txt").exist?
23 | config.action_controller.perform_caching = true
24 | config.action_controller.enable_fragment_cache_logging = true
25 |
26 | config.cache_store = :memory_store
27 | config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" }
28 | else
29 | config.action_controller.perform_caching = false
30 |
31 | config.cache_store = :null_store
32 | end
33 |
34 | # Store uploaded files on the local file system (see config/storage.yml for options).
35 | config.active_storage.service = :local
36 |
37 | # Don't care if the mailer can't send.
38 | config.action_mailer.raise_delivery_errors = false
39 |
40 | config.action_mailer.perform_caching = false
41 |
42 | config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
43 |
44 | # Print deprecation notices to the Rails logger.
45 | config.active_support.deprecation = :log
46 |
47 | # Raise exceptions for disallowed deprecations.
48 | config.active_support.disallowed_deprecation = :raise
49 |
50 | # Tell Active Support which deprecation messages to disallow.
51 | config.active_support.disallowed_deprecation_warnings = []
52 |
53 | # Raise an error on page load if there are pending migrations.
54 | config.active_record.migration_error = :page_load
55 |
56 | # Highlight code that triggered database queries in logs.
57 | config.active_record.verbose_query_logs = true
58 |
59 | # Highlight code that enqueued background job in logs.
60 | config.active_job.verbose_enqueue_logs = true
61 |
62 | # Suppress logger output for asset requests.
63 | config.assets.quiet = true
64 |
65 | # Raises error for missing translations.
66 | # config.i18n.raise_on_missing_translations = true
67 |
68 | # Annotate rendered view with file names.
69 | config.action_view.annotate_rendered_view_with_filenames = true
70 |
71 | # Uncomment if you wish to allow Action Cable access from any origin.
72 | # config.action_cable.disable_request_forgery_protection = true
73 |
74 | # Raise error when a before_action's only/except options reference missing actions.
75 | config.action_controller.raise_on_missing_callback_actions = true
76 | end
77 |
--------------------------------------------------------------------------------
/test/dummy/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | require "active_support/core_ext/integer/time"
2 |
3 | Rails.application.configure do
4 | # Settings specified here will take precedence over those in config/application.rb.
5 |
6 | # Code is not reloaded between requests.
7 | config.enable_reloading = false
8 |
9 | # Eager load code on boot. This eager loads most of Rails and
10 | # your application in memory, allowing both threaded web servers
11 | # and those relying on copy on write to perform better.
12 | # Rake tasks automatically ignore this option for performance.
13 | config.eager_load = true
14 |
15 | # Full error reports are disabled and caching is turned on.
16 | config.consider_all_requests_local = false
17 | config.action_controller.perform_caching = true
18 |
19 | # Ensures that a master key has been made available in ENV["RAILS_MASTER_KEY"], config/master.key, or an environment
20 | # key such as config/credentials/production.key. This key is used to decrypt credentials (and other encrypted files).
21 | # config.require_master_key = true
22 |
23 | # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead.
24 | # config.public_file_server.enabled = false
25 |
26 | # Compress CSS using a preprocessor.
27 | # config.assets.css_compressor = :sass
28 |
29 | # Do not fall back to assets pipeline if a precompiled asset is missed.
30 | config.assets.compile = false
31 |
32 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
33 | # config.asset_host = "http://assets.example.com"
34 |
35 | # Specifies the header that your server uses for sending files.
36 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache
37 | # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX
38 |
39 | # Store uploaded files on the local file system (see config/storage.yml for options).
40 | config.active_storage.service = :local
41 |
42 | # Mount Action Cable outside main process or domain.
43 | # config.action_cable.mount_path = nil
44 | # config.action_cable.url = "wss://example.com/cable"
45 | # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ]
46 |
47 | # Assume all access to the app is happening through a SSL-terminating reverse proxy.
48 | # Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies.
49 | # config.assume_ssl = true
50 |
51 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
52 | config.force_ssl = true
53 | # Skip http-to-https redirect for the default health check endpoint.
54 | # config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } }
55 |
56 | # Log to STDOUT by default
57 | config.logger = ActiveSupport::Logger.new(STDOUT)
58 | .tap { |logger| logger.formatter = ::Logger::Formatter.new }
59 | .then { |logger| ActiveSupport::TaggedLogging.new(logger) }
60 |
61 | # Prepend all log lines with the following tags.
62 | config.log_tags = [ :request_id ]
63 |
64 | # "info" includes generic and useful information about system operation, but avoids logging too much
65 | # information to avoid inadvertent exposure of personally identifiable information (PII). If you
66 | # want to log everything, set the level to "debug".
67 | config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info")
68 |
69 | # Use a different cache store in production.
70 | # config.cache_store = :mem_cache_store
71 |
72 | # Use a real queuing backend for Active Job (and separate queues per environment).
73 | # config.active_job.queue_adapter = :resque
74 | # config.active_job.queue_name_prefix = "dummy_production"
75 |
76 | config.action_mailer.perform_caching = false
77 |
78 | # Ignore bad email addresses and do not raise email delivery errors.
79 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
80 | # config.action_mailer.raise_delivery_errors = false
81 |
82 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
83 | # the I18n.default_locale when a translation cannot be found).
84 | config.i18n.fallbacks = true
85 |
86 | # Don't log any deprecations.
87 | config.active_support.report_deprecations = false
88 |
89 | # Do not dump schema after migrations.
90 | config.active_record.dump_schema_after_migration = false
91 |
92 | # Enable DNS rebinding protection and other `Host` header attacks.
93 | # config.hosts = [
94 | # "example.com", # Allow requests from example.com
95 | # /.*\.example\.com/ # Allow requests from subdomains like `www.example.com`
96 | # ]
97 | # Skip DNS rebinding protection for the default health check endpoint.
98 | # config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
99 | end
100 |
--------------------------------------------------------------------------------
/test/dummy/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | require "active_support/core_ext/integer/time"
2 |
3 | # The test environment is used exclusively to run your application's
4 | # test suite. You never need to work with it otherwise. Remember that
5 | # your test database is "scratch space" for the test suite and is wiped
6 | # and recreated between test runs. Don't rely on the data there!
7 |
8 | Rails.application.configure do
9 | # Settings specified here will take precedence over those in config/application.rb.
10 |
11 | # While tests run files are not watched, reloading is not necessary.
12 | config.enable_reloading = false
13 |
14 | # Eager loading loads your entire application. When running a single test locally,
15 | # this is usually not necessary, and can slow down your test suite. However, it's
16 | # recommended that you enable it in continuous integration systems to ensure eager
17 | # loading is working properly before deploying your code.
18 | config.eager_load = ENV["CI"].present?
19 |
20 | # Configure public file server for tests with Cache-Control for performance.
21 | config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{1.hour.to_i}" }
22 |
23 | # Show full error reports and disable caching.
24 | config.consider_all_requests_local = true
25 | config.action_controller.perform_caching = false
26 | config.cache_store = :null_store
27 |
28 | # Render exception templates for rescuable exceptions and raise for other exceptions.
29 | config.action_dispatch.show_exceptions = :rescuable
30 |
31 | # Disable request forgery protection in test environment.
32 | config.action_controller.allow_forgery_protection = false
33 |
34 | # Store uploaded files on the local file system in a temporary directory.
35 | config.active_storage.service = :test
36 |
37 | config.action_mailer.perform_caching = false
38 |
39 | # Tell Action Mailer not to deliver emails to the real world.
40 | # The :test delivery method accumulates sent emails in the
41 | # ActionMailer::Base.deliveries array.
42 | config.action_mailer.delivery_method = :test
43 |
44 | # Unlike controllers, the mailer instance doesn't have any context about the
45 | # incoming request so you'll need to provide the :host parameter yourself.
46 | config.action_mailer.default_url_options = { host: "www.example.com" }
47 |
48 | # Print deprecation notices to the stderr.
49 | config.active_support.deprecation = :stderr
50 |
51 | # Raise exceptions for disallowed deprecations.
52 | config.active_support.disallowed_deprecation = :raise
53 |
54 | # Tell Active Support which deprecation messages to disallow.
55 | config.active_support.disallowed_deprecation_warnings = []
56 |
57 | # Raises error for missing translations.
58 | # config.i18n.raise_on_missing_translations = true
59 |
60 | # Annotate rendered view with file names.
61 | # config.action_view.annotate_rendered_view_with_filenames = true
62 |
63 | # Raise error when a before_action's only/except options reference missing actions.
64 | config.action_controller.raise_on_missing_callback_actions = true
65 | end
66 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = "1.0"
5 |
6 | # Add additional assets to the asset load path.
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 |
9 | # Precompile additional assets.
10 | # application.js, application.css, and all non-JS/CSS in the app/assets
11 | # folder are already added.
12 | # Rails.application.config.assets.precompile += %w( admin.js admin.css )
13 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/content_security_policy.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Define an application-wide content security policy.
4 | # See the Securing Rails Applications Guide for more information:
5 | # https://guides.rubyonrails.org/security.html#content-security-policy-header
6 |
7 | # Rails.application.configure do
8 | # config.content_security_policy do |policy|
9 | # policy.default_src :self, :https
10 | # policy.font_src :self, :https, :data
11 | # policy.img_src :self, :https, :data
12 | # policy.object_src :none
13 | # policy.script_src :self, :https
14 | # policy.style_src :self, :https
15 | # # Specify URI for violation reports
16 | # # policy.report_uri "/csp-violation-report-endpoint"
17 | # end
18 | #
19 | # # Generate session nonces for permitted importmap, inline scripts, and inline styles.
20 | # config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
21 | # config.content_security_policy_nonce_directives = %w(script-src style-src)
22 | #
23 | # # Report violations without enforcing the policy.
24 | # # config.content_security_policy_report_only = true
25 | # end
26 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file.
4 | # Use this to limit dissemination of sensitive information.
5 | # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors.
6 | Rails.application.config.filter_parameters += [
7 | :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn
8 | ]
9 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, "\\1en"
8 | # inflect.singular /^(ox)en/i, "\\1"
9 | # inflect.irregular "person", "people"
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym "RESTful"
16 | # end
17 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/permissions_policy.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Define an application-wide HTTP permissions policy. For further
4 | # information see: https://developers.google.com/web/updates/2018/06/feature-policy
5 |
6 | # Rails.application.config.permissions_policy do |policy|
7 | # policy.camera :none
8 | # policy.gyroscope :none
9 | # policy.microphone :none
10 | # policy.usb :none
11 | # policy.fullscreen :self
12 | # policy.payment :self, "https://secure.example.com"
13 | # end
14 |
--------------------------------------------------------------------------------
/test/dummy/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization and
2 | # are automatically loaded by Rails. If you want to use locales other than
3 | # English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t "hello"
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t("hello") %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # To learn more about the API, please read the Rails Internationalization guide
20 | # at https://guides.rubyonrails.org/i18n.html.
21 | #
22 | # Be aware that YAML interprets the following case-insensitive strings as
23 | # booleans: `true`, `false`, `on`, `off`, `yes`, `no`. Therefore, these strings
24 | # must be quoted to be interpreted as strings. For example:
25 | #
26 | # en:
27 | # "yes": yup
28 | # enabled: "ON"
29 |
30 | en:
31 | hello: "Hello world"
32 |
--------------------------------------------------------------------------------
/test/dummy/config/puma.rb:
--------------------------------------------------------------------------------
1 | # This configuration file will be evaluated by Puma. The top-level methods that
2 | # are invoked here are part of Puma's configuration DSL. For more information
3 | # about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html.
4 |
5 | # Puma starts a configurable number of processes (workers) and each process
6 | # serves each request in a thread from an internal thread pool.
7 | #
8 | # The ideal number of threads per worker depends both on how much time the
9 | # application spends waiting for IO operations and on how much you wish to
10 | # to prioritize throughput over latency.
11 | #
12 | # As a rule of thumb, increasing the number of threads will increase how much
13 | # traffic a given process can handle (throughput), but due to CRuby's
14 | # Global VM Lock (GVL) it has diminishing returns and will degrade the
15 | # response time (latency) of the application.
16 | #
17 | # The default is set to 3 threads as it's deemed a decent compromise between
18 | # throughput and latency for the average Rails application.
19 | #
20 | # Any libraries that use a connection pool or another resource pool should
21 | # be configured to provide at least as many connections as the number of
22 | # threads. This includes Active Record's `pool` parameter in `database.yml`.
23 | threads_count = ENV.fetch("RAILS_MAX_THREADS", 3)
24 | threads threads_count, threads_count
25 |
26 | # Specifies the `environment` that Puma will run in.
27 | rails_env = ENV.fetch("RAILS_ENV", "development")
28 | environment rails_env
29 |
30 | case rails_env
31 | when "production"
32 | # If you are running more than 1 thread per process, the workers count
33 | # should be equal to the number of processors (CPU cores) in production.
34 | #
35 | # Automatically detect the number of available processors in production.
36 | require "concurrent-ruby"
37 | workers_count = Integer(ENV.fetch("WEB_CONCURRENCY") { Concurrent.available_processor_count })
38 | workers workers_count if workers_count > 1
39 |
40 | preload_app!
41 | when "development"
42 | # Specifies a very generous `worker_timeout` so that the worker
43 | # isn't killed by Puma when suspended by a debugger.
44 | worker_timeout 3600
45 | end
46 |
47 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
48 | port ENV.fetch("PORT", 3000)
49 |
50 | # Allow puma to be restarted by `bin/rails restart` command.
51 | plugin :tmp_restart
52 |
53 | # Only use a pidfile when requested
54 | pidfile ENV["PIDFILE"] if ENV["PIDFILE"]
55 |
--------------------------------------------------------------------------------
/test/dummy/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
3 | post :refer, to: "referrals#create"
4 |
5 | # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
6 | # Can be used by load balancers and uptime monitors to verify that the app is live.
7 | get "up" => "rails/health#show", as: :rails_health_check
8 |
9 | # Render dynamic PWA files from app/views/pwa/*
10 | get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker
11 | get "manifest" => "rails/pwa#manifest", as: :pwa_manifest
12 |
13 | # Defines the root path route ("/")
14 | root to: "referrals#show"
15 | end
16 |
--------------------------------------------------------------------------------
/test/dummy/config/storage.yml:
--------------------------------------------------------------------------------
1 | test:
2 | service: Disk
3 | root: <%= Rails.root.join("tmp/storage") %>
4 |
5 | local:
6 | service: Disk
7 | root: <%= Rails.root.join("storage") %>
8 |
9 | # Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10 | # amazon:
11 | # service: S3
12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14 | # region: us-east-1
15 | # bucket: your_own_bucket-<%= Rails.env %>
16 |
17 | # Remember not to checkin your GCS keyfile to a repository
18 | # google:
19 | # service: GCS
20 | # project: your_project
21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
22 | # bucket: your_own_bucket-<%= Rails.env %>
23 |
24 | # Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25 | # microsoft:
26 | # service: AzureStorage
27 | # storage_account_name: your_account_name
28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
29 | # container: your_container_name-<%= Rails.env %>
30 |
31 | # mirror:
32 | # service: Mirror
33 | # primary: local
34 | # mirrors: [ amazon, google, microsoft ]
35 |
--------------------------------------------------------------------------------
/test/dummy/db/migrate/20240611180748_create_users.rb:
--------------------------------------------------------------------------------
1 | class CreateUsers < ActiveRecord::Migration[7.2]
2 | def change
3 | create_table :users do |t|
4 | t.string :name
5 |
6 | t.timestamps
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/test/dummy/db/schema.rb:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from the current state of the database. Instead
2 | # of editing this file, please use the migrations feature of Active Record to
3 | # incrementally modify your database, and then regenerate this schema definition.
4 | #
5 | # This file is the source Rails uses to define your schema when running `bin/rails
6 | # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
7 | # be faster and is potentially less error prone than running all of your
8 | # migrations from scratch. Old migrations may fail to apply correctly if those
9 | # migrations use external dependencies or application code.
10 | #
11 | # It's strongly recommended that you check this file into your version control system.
12 |
13 | ActiveRecord::Schema[7.2].define(version: 2024_07_01_172643) do
14 | create_table "refer_referral_codes", force: :cascade do |t|
15 | t.string "referrer_type", null: false
16 | t.integer "referrer_id", null: false
17 | t.string "code", null: false
18 | t.datetime "created_at", null: false
19 | t.datetime "updated_at", null: false
20 | t.integer "referrals_count", default: 0
21 | t.integer "visits_count", default: 0
22 | t.index [ "code" ], name: "index_refer_referral_codes_on_code", unique: true
23 | t.index [ "referrer_type", "referrer_id" ], name: "index_refer_referral_codes_on_referrer"
24 | end
25 |
26 | create_table "refer_referrals", force: :cascade do |t|
27 | t.string "referrer_type", null: false
28 | t.integer "referrer_id", null: false
29 | t.string "referee_type", null: false
30 | t.integer "referee_id", null: false
31 | t.integer "referral_code_id"
32 | t.datetime "created_at", null: false
33 | t.datetime "updated_at", null: false
34 | t.datetime "completed_at"
35 | t.index [ "referee_type", "referee_id" ], name: "index_refer_referrals_on_referee"
36 | t.index [ "referral_code_id" ], name: "index_refer_referrals_on_referral_code_id"
37 | t.index [ "referrer_type", "referrer_id" ], name: "index_refer_referrals_on_referrer"
38 | end
39 |
40 | create_table "refer_visits", force: :cascade do |t|
41 | t.integer "referral_code_id", null: false
42 | t.string "ip"
43 | t.text "user_agent"
44 | t.text "referrer"
45 | t.string "referring_domain"
46 | t.datetime "created_at", null: false
47 | t.datetime "updated_at", null: false
48 | t.index [ "referral_code_id" ], name: "index_refer_visits_on_referral_code_id"
49 | end
50 |
51 | create_table "users", force: :cascade do |t|
52 | t.string "name"
53 | t.datetime "created_at", null: false
54 | t.datetime "updated_at", null: false
55 | end
56 |
57 | add_foreign_key "refer_visits", "refer_referral_codes", column: "referral_code_id"
58 | end
59 |
--------------------------------------------------------------------------------
/test/dummy/lib/assets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/excid3/refer/a64a19487a2b1da31e6875bc19eee0be5e42703d/test/dummy/lib/assets/.keep
--------------------------------------------------------------------------------
/test/dummy/log/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/excid3/refer/a64a19487a2b1da31e6875bc19eee0be5e42703d/test/dummy/log/.keep
--------------------------------------------------------------------------------
/test/dummy/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The page you were looking for doesn't exist.
62 |
You may have mistyped the address or the page may have moved.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/test/dummy/public/406-unsupported-browser.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Your browser is not supported (406)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
Your browser is not supported.
62 |
Please upgrade your browser to continue.
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/test/dummy/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The change you wanted was rejected.
62 |
Maybe you tried to change something you didn't have access to.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/test/dummy/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
We're sorry, but something went wrong.
62 |
63 |
If you are the application owner check the logs for more information.
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/test/dummy/public/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/excid3/refer/a64a19487a2b1da31e6875bc19eee0be5e42703d/test/dummy/public/icon.png
--------------------------------------------------------------------------------
/test/dummy/public/icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/test/fixtures/files/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/excid3/refer/a64a19487a2b1da31e6875bc19eee0be5e42703d/test/fixtures/files/.keep
--------------------------------------------------------------------------------
/test/fixtures/refer/referral_codes.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 |
3 | one:
4 | referrer: one (User)
5 | code: chris
6 |
7 | two:
8 | referrer: two (User)
9 | code: bob
10 |
--------------------------------------------------------------------------------
/test/fixtures/refer/referrals.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 |
3 | one:
4 | referrer: one (User)
5 | referee: two (User)
6 | referral_code: one
7 |
--------------------------------------------------------------------------------
/test/fixtures/refer/visits.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 |
3 | one:
4 | referral_code: one
5 | ip: MyString
6 | user_agent: MyText
7 | referrer: MyText
8 | referring_domain: MyString
9 |
10 | two:
11 | referral_code: two
12 | ip: MyString
13 | user_agent: MyText
14 | referrer: MyText
15 | referring_domain: MyString
16 |
--------------------------------------------------------------------------------
/test/fixtures/users.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 |
3 | one:
4 | name: Chris
5 |
6 | two:
7 | name: Bob
8 |
9 | new:
10 | name: David
11 |
--------------------------------------------------------------------------------
/test/integration/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/excid3/refer/a64a19487a2b1da31e6875bc19eee0be5e42703d/test/integration/.keep
--------------------------------------------------------------------------------
/test/integration/referral_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class ReferralIntegrationTest < ActionDispatch::IntegrationTest
4 | test "referral is created" do
5 | assert_difference "Refer::Referral.count" do
6 | post refer_path(ref: refer_referral_codes(:one), user_id: users(:new).id)
7 | end
8 | end
9 |
10 | test "doesn't set referral cookie if param missing" do
11 | get root_path(other: "example")
12 | assert_nil cookies[Refer.cookie_name]
13 | end
14 |
15 | test "sets referral cookie" do
16 | referral_code = refer_referral_codes(:one)
17 | get root_path(ref: referral_code)
18 | assert_equal referral_code.to_param, cookies[Refer.cookie_name]
19 | end
20 |
21 | test "tracks visits" do
22 | referral_code = refer_referral_codes(:one)
23 | assert_difference "referral_code.reload.visits_count" do
24 | get root_path(ref: referral_code)
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/test/models/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/excid3/refer/a64a19487a2b1da31e6875bc19eee0be5e42703d/test/models/.keep
--------------------------------------------------------------------------------
/test/models/refer/referral_code_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 | require "minitest/mock"
3 |
4 |
5 | class Refer::ReferralCodeTest < ActiveSupport::TestCase
6 | test "deleting referral code doesn't delete referral" do
7 | referral = refer_referrals(:one)
8 | assert_not_nil referral.referral_code
9 | assert_no_difference "Refer::Referral.count" do
10 | referral.referral_code.destroy
11 | end
12 | referral.reload
13 | assert_nil referral.referral_code
14 | end
15 |
16 | test "referral codes are dependent destroyed" do
17 | assert_difference "Refer::ReferralCode.count", -1 do
18 | users(:one).destroy
19 | end
20 | end
21 |
22 | test "generates referral codes automatically" do
23 | Refer.stub :code_generator, -> { ->(referrer) { SecureRandom.alphanumeric(8) } } do
24 | assert_not_nil users(:one).referral_codes.create!.code
25 | end
26 | end
27 |
28 | test "does not generate referral code automatically if empty generator config" do
29 | Refer.stub :code_generator, nil do
30 | assert_nil users(:one).referral_codes.create.code
31 | end
32 | end
33 |
34 | test "does not generate referral code automatically if using a custom code" do
35 | Refer.stub :code_generator, ->(referrer) { SecureRandom.alphanumeric(8) } do
36 | custom_referral = users(:one).referral_codes.create(code: "custom")
37 | assert_equal custom_referral.code, "custom"
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/test/models/refer/referral_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class Refer::ReferralTest < ActiveSupport::TestCase
4 | test "referrals are dependent destroyed" do
5 | assert_difference "Refer::Referral.count", -1 do
6 | users(:one).destroy
7 | end
8 | end
9 |
10 | test "can be associated with a referral code" do
11 | assert_equal refer_referral_codes(:one), refer_referrals(:one).referral_code
12 | end
13 |
14 | test "can be completed" do
15 | referral = refer_referrals(:one)
16 | assert_nil referral.completed_at
17 |
18 | travel_to Time.current do
19 | referral.complete!
20 | assert_equal Time.current, referral.completed_at
21 | end
22 | end
23 |
24 | test "complete with custom attributes" do
25 | referral = refer_referrals(:one)
26 | assert_nil referral.completed_at
27 |
28 | travel_to Time.current do
29 | referral.complete!(completed_at: 1.hour.ago)
30 | assert_equal 1.hour.ago, referral.completed_at
31 | end
32 | end
33 |
34 | test "complete! doesn't override previous completion" do
35 | referral = refer_referrals(:one)
36 | assert_nil referral.completed_at
37 |
38 | referral.complete!
39 |
40 | travel_to Time.current + 1.hour do
41 | assert_no_difference "referral.completed_at" do
42 | referral.complete!
43 | end
44 | end
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/test/models/refer/visit_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | module Refer
4 | class VisitTest < ActiveSupport::TestCase
5 | test "masks ips" do
6 | assert_equal "127.0.0.0", Refer::Visit.new(ip: "127.0.0.1").ip
7 | end
8 |
9 | test "referring_domain" do
10 | request = ActiveSupport::OrderedOptions.new.merge(referrer: "https://gorails.com/episodes/1")
11 | assert_equal "gorails.com", Refer::Visit.from_request(request).referring_domain
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/test/refer_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class ReferTest < ActiveSupport::TestCase
4 | test "it has a version number" do
5 | assert Refer::VERSION
6 | end
7 |
8 | test "refer" do
9 | assert_difference "Refer::Referral.count" do
10 | Refer.refer(code: refer_referral_codes(:one).code, referee: users(:new))
11 | end
12 | end
13 |
14 | test "refer invalid code" do
15 | assert_raises ActiveRecord::RecordNotFound do
16 | Refer.refer!(code: "invalid", referee: users(:new))
17 | end
18 | end
19 |
20 | test "refer self" do
21 | assert_no_difference "Refer::Referral.count" do
22 | Refer.refer(code: refer_referral_codes(:one).code, referee: users(:one))
23 | end
24 |
25 | assert_raises ActiveRecord::RecordInvalid, "Validation failed: Self-referrals are not allowed" do
26 | Refer.refer!(code: refer_referral_codes(:one).code, referee: users(:one))
27 | end
28 | end
29 |
30 | test "refer already referred" do
31 | assert_no_difference "Refer::Referral.count" do
32 | Refer.refer(code: refer_referral_codes(:one).code, referee: users(:two))
33 | end
34 |
35 | assert_raises Refer::AlreadyReferred do
36 | Refer.refer!(code: refer_referral_codes(:one).code, referee: users(:two))
37 | end
38 | end
39 |
40 | test "referred?" do
41 | assert Refer.referred?(users(:two))
42 | assert_not Refer.referred?(users(:new))
43 | end
44 |
45 | test "referral_codes" do
46 | assert_includes users(:one).referral_codes, refer_referral_codes(:one)
47 | end
48 |
49 | test "referrals" do
50 | assert_includes users(:one).referrals, refer_referrals(:one)
51 | end
52 |
53 | test "referral" do
54 | assert_equal refer_referrals(:one), users(:two).referral
55 | end
56 |
57 | test "referrer" do
58 | assert_equal users(:one), users(:two).referrer
59 | end
60 |
61 | test "referrer when nil" do
62 | assert_nil users(:new).referrer
63 | end
64 |
65 | test "referral_completed callback" do
66 | old_callback = Refer.referral_completed
67 | referral = refer_referrals(:one)
68 | assert_not referral.completed_at?
69 |
70 | completed = nil
71 | Refer.referral_completed = ->(referral) {
72 | completed = referral
73 | }
74 |
75 | # Called the first time a referral is completed
76 | referral.complete!
77 | assert_equal referral, completed
78 |
79 | # Does not get called second time because referral was already completed
80 | completed = nil
81 | referral.complete!
82 | assert_nil completed
83 | ensure
84 | Refer.referral_completed = old_callback
85 | end
86 | end
87 |
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | # Configure Rails Environment
2 | ENV["RAILS_ENV"] = "test"
3 |
4 | require_relative "../test/dummy/config/environment"
5 | ActiveRecord::Migrator.migrations_paths = [ File.expand_path("../test/dummy/db/migrate", __dir__) ]
6 | require "rails/test_help"
7 |
8 | # Load fixtures from the engine
9 | if ActiveSupport::TestCase.respond_to?(:fixture_paths=)
10 | ActiveSupport::TestCase.fixture_paths = [ File.expand_path("fixtures", __dir__) ]
11 | ActionDispatch::IntegrationTest.fixture_paths = ActiveSupport::TestCase.fixture_paths
12 | ActiveSupport::TestCase.file_fixture_path = File.expand_path("fixtures", __dir__) + "/files"
13 | ActiveSupport::TestCase.fixtures :all
14 | end
15 |
--------------------------------------------------------------------------------