instagram_connect 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (18) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -1
  3. data/README.md +29 -8
  4. data/app/assets/stylesheets/instagram_connect/application.css +106 -0
  5. data/app/helpers/instagram_connect/application_helper.rb +14 -0
  6. data/app/views/layouts/instagram_connect/application.html.erb +18 -9
  7. data/{lib/generators/instagram_connect/install/templates/create_instagram_connect_accounts.rb.tt → db/migrate/20260715072548_create_instagram_connect_accounts.rb} +1 -1
  8. data/{lib/generators/instagram_connect/install/templates/create_instagram_connect_conversations.rb.tt → db/migrate/20260715072549_create_instagram_connect_conversations.rb} +1 -1
  9. data/{lib/generators/instagram_connect/install/templates/create_instagram_connect_messages.rb.tt → db/migrate/20260715072550_create_instagram_connect_messages.rb} +1 -1
  10. data/{lib/generators/instagram_connect/install/templates/create_instagram_connect_inbound_messages.rb.tt → db/migrate/20260715072551_create_instagram_connect_inbound_messages.rb} +1 -1
  11. data/{lib/generators/instagram_connect/install/templates/create_instagram_connect_comments.rb.tt → db/migrate/20260715072552_create_instagram_connect_comments.rb} +1 -1
  12. data/lib/generators/instagram_connect/install/install_generator.rb +7 -21
  13. data/lib/generators/instagram_connect/install/templates/instagram_connect.rb.tt +16 -6
  14. data/lib/instagram_connect/configuration.rb +32 -1
  15. data/lib/instagram_connect/engine.rb +20 -0
  16. data/lib/instagram_connect/version.rb +1 -1
  17. data/lib/instagram_connect.rb +8 -0
  18. metadata +8 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '00201119165343bb2f5ab1e16f2894f4944fb452b241068691fe0f0ef2858c0e'
4
- data.tar.gz: 6ad13294368d8130252ada941018415855fc935ce32349be419064ab458aa718
3
+ metadata.gz: afc5d08b4207b0268104bbf7a4955406b26298e4da4406bd5034a93b8a43982f
4
+ data.tar.gz: 4d436bf9a398fc745f4a594b1346cd8315a949391213f83c45cc52c07694e993
5
5
  SHA512:
6
- metadata.gz: 38c055d770365ef01e4571105a2846bb727bb5f7f7d83ca7dd867ea6cdfadc8c9b70cae2d2005ae68e148aeafbb5799a3be1704d81474ec82979b8464766af2e
7
- data.tar.gz: cb99273b4e2e03d40a3f07e75494ced6de868a94a1c99468f7b85d04b981718a6de2aeae734f2b9ce5ccbd308c0aa76442a17529dce63f2ce25b6020892f8d07
6
+ metadata.gz: 3d4894411aa0ad5924a8d4d163215a880e8344aa3306a814400cd73e2ad717d5bca7f4a4284c4baa81162af98cabc987983c4715c3eaab8b0fb16ba9a69321ec
7
+ data.tar.gz: 9ce313d2dd27ce96e913da61d46eb9f4e87bcbac7681fcd6963447759286b6419a7453d50821d9909fe570eca0fe98b5620fc709ead569e4f8c88f3752b635c9
data/CHANGELOG.md CHANGED
@@ -4,7 +4,28 @@ All notable changes to this project are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/) and the project adheres to
5
5
  [Semantic Versioning](https://semver.org/).
6
6
 
7
- ## [0.1.0] — Unreleased
7
+ ## [0.2.0]
8
+
9
+ True plug-and-play: the gem now owns the data model and the UI, so a host adds
10
+ **only configuration** — no copied migrations, views, or CSS.
11
+
12
+ ### Changed
13
+ - **Migrations ship in the gem** (`db/migrate`) and run in place via an appended
14
+ migration path — the host just runs `bin/rails db:migrate`. The install
15
+ generator no longer copies migrations. The gem owns the schema, versioned with it.
16
+ - **Self-contained, themeable UI.** The gem ships its own layout + stylesheet and
17
+ renders in its own chrome by default (`inherit_host_layout` now defaults to
18
+ `false`, like an admin engine). Tint it entirely from the initializer via
19
+ `config.theme = { primary:, font:, radius:, … }` (merged over sensible defaults) —
20
+ no CSS or views in the host app.
21
+
22
+ ### Fixed
23
+ - Enable `encrypts :access_token` at runtime via an engine `to_prepare` hook, so
24
+ access tokens are encrypted at rest in host apps. Previously the decoration only
25
+ ran in the gem's own test suite, so a host stored tokens in plain text. Opt out
26
+ with `config.encrypt_tokens = false`.
27
+
28
+ ## [0.1.0]
8
29
 
9
30
  Initial release. A mountable Rails engine for Instagram over the official Meta Graph API.
10
31
 
data/README.md CHANGED
@@ -46,12 +46,15 @@ gem "instagram_connect"
46
46
 
47
47
  ```bash
48
48
  bundle install
49
- bin/rails g instagram_connect:install # initializer + engine mount + migrations
50
- bin/rails db:migrate
49
+ bin/rails g instagram_connect:install # writes the initializer + mounts the engine
50
+ bin/rails db:migrate # runs the gem's migrations in place
51
51
  ```
52
52
 
53
- The install generator writes `config/initializers/instagram_connect.rb`, mounts the
54
- engine at `/instagram`, and copies migrations for all five tables.
53
+ The install generator writes `config/initializers/instagram_connect.rb` and mounts
54
+ the engine at `/instagram`. **Migrations ship inside the gem and run in place** —
55
+ nothing is copied into your app, so every adopter gets exactly the same schema,
56
+ versioned with the gem. That's the whole install: **configuration only, no copied
57
+ migrations, views, or CSS.**
55
58
 
56
59
  ## Configuration
57
60
 
@@ -98,12 +101,30 @@ same `verify_token` you configured. Subscribe the fields you need: `messages`,
98
101
 
99
102
  ## The inbox UI
100
103
 
101
- Mounted at `/instagram`: a DM inbox with a window-aware reply composer, a comment
102
- moderation screen, and a publish screen. To restyle to your app's design system:
104
+ Mounted at `/instagram`: a self-contained, self-styled DM inbox (window-aware reply
105
+ composer), comment moderation, and publishing its own chrome, like an admin engine.
106
+ Nothing to build in your app.
107
+
108
+ ### Theming
109
+
110
+ Tint the whole UI to your brand from the initializer — no CSS or view files in your
111
+ app. Any subset of keys is merged over the gem defaults:
112
+
113
+ ```ruby
114
+ config.theme = {
115
+ primary: "#0057a8",
116
+ font: "Inter, system-ui, sans-serif",
117
+ radius: "12px"
118
+ }
119
+ ```
120
+
121
+ Deeper control (optional): render inside your own layout with
122
+ `config.inherit_host_layout = true` (then add `<%= instagram_connect_styles %>` to
123
+ your `<head>`), or copy the views/controllers to fully override them:
103
124
 
104
125
  ```bash
105
- bin/rails g instagram_connect:views # copies views into app/views/instagram_connect
106
- bin/rails g instagram_connect:controllers # (optional) copies controllers to override
126
+ bin/rails g instagram_connect:views # copy views into app/views/instagram_connect
127
+ bin/rails g instagram_connect:controllers # copy controllers to override
107
128
  ```
108
129
 
109
130
  ## CLI
@@ -0,0 +1,106 @@
1
+ /* instagram_connect — self-contained UI styles. All colours/spacing come from
2
+ CSS variables set from config.theme, so a host tints the whole UI from its
3
+ initializer without touching any CSS or view. */
4
+
5
+ .ic-body {
6
+ margin: 0;
7
+ background: var(--ic-bg);
8
+ color: var(--ic-text);
9
+ font-family: var(--ic-font);
10
+ font-size: 15px;
11
+ line-height: 1.5;
12
+ -webkit-font-smoothing: antialiased;
13
+ }
14
+
15
+ .ic-app { max-width: 900px; margin: 0 auto; padding: 0 0 4rem; }
16
+
17
+ .ic-appbar {
18
+ display: flex; align-items: center; gap: 1.25rem;
19
+ padding: 0.9rem 1rem; border-bottom: 1px solid var(--ic-border);
20
+ background: var(--ic-surface); position: sticky; top: 0; z-index: 5;
21
+ }
22
+ .ic-appbar__brand { font-weight: 700; letter-spacing: -0.01em; }
23
+ .ic-appbar__nav { display: flex; gap: 1rem; }
24
+ .ic-appbar__nav a { color: var(--ic-muted); text-decoration: none; font-weight: 500; }
25
+ .ic-appbar__nav a:hover { color: var(--ic-primary); }
26
+
27
+ .ic-main { padding: 1rem; }
28
+ .ic-screen { display: flex; flex-direction: column; gap: 1rem; }
29
+ .ic-pagehead h1 { margin: 0.25rem 0 0; font-size: 1.5rem; letter-spacing: -0.02em; }
30
+ .ic-pagehead p { margin: 0.15rem 0 0; color: var(--ic-muted); }
31
+
32
+ .ic-inbox__title, .ic-comments__title, .ic-posts__title, .ic-publish__title,
33
+ .ic-thread-view__title { margin: 0 0 1rem; font-size: 1.5rem; letter-spacing: -0.02em; }
34
+
35
+ .ic-card, .ic-inbox, .ic-comments, .ic-posts, .ic-publish {
36
+ background: var(--ic-surface); border: 1px solid var(--ic-border);
37
+ border-radius: var(--ic-radius); padding: 1rem;
38
+ }
39
+
40
+ /* Inbox rows */
41
+ .ic-conversations, .ic-comments__list, .ic-posts__list { list-style: none; margin: 0; padding: 0; }
42
+ .ic-conversation, .ic-comment, .ic-post {
43
+ display: flex; flex-direction: column; gap: 0.25rem;
44
+ padding: 0.75rem 0.5rem; border-bottom: 1px solid var(--ic-border);
45
+ }
46
+ .ic-conversation:last-child, .ic-comment:last-child, .ic-post:last-child { border-bottom: 0; }
47
+ .ic-conversation__link { display: flex; align-items: baseline; gap: 0.5rem; text-decoration: none; color: inherit; }
48
+ .ic-conversation__name { font-weight: 600; }
49
+ .ic-conversation__preview, .ic-comment__text { color: var(--ic-muted); font-size: 0.9rem; }
50
+ .ic-conversation__unread {
51
+ margin-left: auto; background: var(--ic-primary); color: var(--ic-primary-contrast);
52
+ border-radius: 9999px; padding: 0.05rem 0.5rem; font-size: 0.75rem; font-weight: 700;
53
+ }
54
+
55
+ /* Thread + bubbles */
56
+ .ic-thread-view__back { margin: 0 0 0.5rem; }
57
+ .ic-thread-view__back a, .ic-posts__new, .ic-post__link { color: var(--ic-primary); text-decoration: none; }
58
+ .ic-thread { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 1rem; }
59
+ .ic-message { max-width: 78%; padding: 0.6rem 0.8rem; border-radius: var(--ic-radius); }
60
+ .ic-message--inbound { align-self: flex-start; background: var(--ic-customer-bubble); }
61
+ .ic-message--outbound { align-self: flex-end; background: var(--ic-staff-bubble); }
62
+ .ic-message__body { white-space: pre-wrap; }
63
+ .ic-message__meta { display: block; margin-top: 0.25rem; font-size: 0.72rem; color: var(--ic-muted); }
64
+
65
+ /* Composer + forms */
66
+ .ic-composer, .ic-publish__form { display: flex; flex-direction: column; gap: 0.5rem; }
67
+ .ic-composer__input, .ic-field__input, .ic-input, textarea, input[type="url"], input[type="text"] {
68
+ width: 100%; box-sizing: border-box; padding: 0.6rem 0.75rem;
69
+ border: 1px solid var(--ic-border); border-radius: var(--ic-radius);
70
+ font: inherit; color: inherit; background: var(--ic-surface);
71
+ }
72
+ .ic-field { display: flex; flex-direction: column; gap: 0.3rem; margin-bottom: 0.75rem; }
73
+ .ic-field label, .ic-label { font-weight: 600; font-size: 0.85rem; }
74
+ .ic-composer__note, .ic-publish__hint { color: var(--ic-warn); font-size: 0.85rem; }
75
+ .ic-composer__closed { color: var(--ic-muted); }
76
+
77
+ /* Buttons */
78
+ .ic-btn, .ic-composer__send, .ic-publish__submit,
79
+ button[type="submit"] {
80
+ display: inline-block; padding: 0.55rem 1rem; border: 0; cursor: pointer;
81
+ border-radius: var(--ic-radius); font: inherit; font-weight: 600;
82
+ background: var(--ic-primary); color: var(--ic-primary-contrast); text-decoration: none;
83
+ }
84
+ .ic-btn--secondary { background: var(--ic-surface); color: var(--ic-text); border: 1px solid var(--ic-border); }
85
+ .ic-comment__actions { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; }
86
+ .ic-comment__actions form { display: inline-flex; gap: 0.3rem; }
87
+ .ic-comment__actions button { padding: 0.35rem 0.7rem; font-size: 0.85rem; }
88
+ .ic-comment--hidden { opacity: 0.55; }
89
+
90
+ /* Badges / flashes / empty */
91
+ .ic-badge { display: inline-block; padding: 0.05rem 0.5rem; border-radius: 9999px; font-size: 0.72rem; font-weight: 700; }
92
+ .ic-badge--green { background: color-mix(in srgb, var(--ic-ok) 15%, transparent); color: var(--ic-ok); }
93
+ .ic-badge--red { background: color-mix(in srgb, var(--ic-err) 15%, transparent); color: var(--ic-err); }
94
+ .ic-badge--amber { background: color-mix(in srgb, var(--ic-warn) 15%, transparent); color: var(--ic-warn); }
95
+ .ic-empty { color: var(--ic-muted); padding: 1rem 0.5rem; }
96
+ .ic-flash { margin: 0 0 0.75rem; padding: 0.6rem 0.9rem; border-radius: var(--ic-radius); }
97
+ .ic-flash--notice { background: color-mix(in srgb, var(--ic-ok) 12%, transparent); color: var(--ic-ok); }
98
+ .ic-flash--alert { background: color-mix(in srgb, var(--ic-err) 12%, transparent); color: var(--ic-err); }
99
+
100
+ .ic-posts__actions { margin: 0 0 0.75rem; }
101
+ .ic-posts__quota { color: var(--ic-muted); font-size: 0.9rem; }
102
+
103
+ @media (max-width: 480px) {
104
+ .ic-message { max-width: 88%; }
105
+ .ic-appbar { gap: 0.75rem; }
106
+ }
@@ -0,0 +1,14 @@
1
+ module InstagramConnect
2
+ module ApplicationHelper
3
+ # Emits a <style> block: the theme values from config as CSS variables,
4
+ # followed by the gem's base stylesheet. Included automatically in the gem's
5
+ # own layout; a host inheriting its own layout can drop this into its <head>.
6
+ def instagram_connect_styles
7
+ vars = InstagramConnect.configuration.resolved_theme.map do |key, value|
8
+ "--ic-#{key.to_s.tr('_', '-')}: #{value};"
9
+ end.join(" ")
10
+
11
+ content_tag(:style, ":root { #{vars} }\n#{InstagramConnect.base_css}".html_safe)
12
+ end
13
+ end
14
+ end
@@ -1,19 +1,28 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
3
  <head>
4
- <title>Instagram — instagram_connect</title>
4
+ <title>Instagram</title>
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <%= csrf_meta_tags %>
7
+ <%= csp_meta_tag if respond_to?(:csp_meta_tag) %>
8
+ <%= instagram_connect_styles %>
7
9
  <%= yield :head %>
8
10
  </head>
9
11
  <body class="ic-body">
10
- <nav class="ic-nav">
11
- <%= link_to "Inbox", conversations_path, class: "ic-nav__link" %>
12
- <%= link_to "Comments", comments_path, class: "ic-nav__link" %>
13
- <%= link_to "Publish", posts_path, class: "ic-nav__link" %>
14
- </nav>
15
- <% if flash[:notice] %><p class="ic-flash ic-flash--notice"><%= flash[:notice] %></p><% end %>
16
- <% if flash[:alert] %><p class="ic-flash ic-flash--alert"><%= flash[:alert] %></p><% end %>
17
- <main class="ic-main"><%= yield %></main>
12
+ <div class="ic-app">
13
+ <header class="ic-appbar">
14
+ <span class="ic-appbar__brand">Instagram</span>
15
+ <nav class="ic-appbar__nav">
16
+ <%= link_to "Inbox", conversations_path %>
17
+ <%= link_to "Comments", comments_path %>
18
+ <%= link_to "Publish", posts_path %>
19
+ </nav>
20
+ </header>
21
+
22
+ <% if flash[:notice] %><p class="ic-flash ic-flash--notice"><%= flash[:notice] %></p><% end %>
23
+ <% if flash[:alert] %><p class="ic-flash ic-flash--alert"><%= flash[:alert] %></p><% end %>
24
+
25
+ <main class="ic-main"><%= yield %></main>
26
+ </div>
18
27
  </body>
19
28
  </html>
@@ -1,4 +1,4 @@
1
- class CreateInstagramConnectAccounts < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
1
+ class CreateInstagramConnectAccounts < ActiveRecord::Migration[7.1]
2
2
  def change
3
3
  create_table :instagram_connect_accounts do |t|
4
4
  t.string :auth_path, null: false
@@ -1,4 +1,4 @@
1
- class CreateInstagramConnectConversations < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
1
+ class CreateInstagramConnectConversations < ActiveRecord::Migration[7.1]
2
2
  def change
3
3
  create_table :instagram_connect_conversations do |t|
4
4
  t.bigint :account_id, null: false
@@ -1,4 +1,4 @@
1
- class CreateInstagramConnectMessages < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
1
+ class CreateInstagramConnectMessages < ActiveRecord::Migration[7.1]
2
2
  def change
3
3
  create_table :instagram_connect_messages do |t|
4
4
  t.bigint :conversation_id, null: false
@@ -1,4 +1,4 @@
1
- class CreateInstagramConnectInboundMessages < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
1
+ class CreateInstagramConnectInboundMessages < ActiveRecord::Migration[7.1]
2
2
  def change
3
3
  create_table :instagram_connect_inbound_messages do |t|
4
4
  t.string :ig_message_id, null: false
@@ -1,4 +1,4 @@
1
- class CreateInstagramConnectComments < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
1
+ class CreateInstagramConnectComments < ActiveRecord::Migration[7.1]
2
2
  def change
3
3
  create_table :instagram_connect_comments do |t|
4
4
  t.bigint :account_id, null: false
@@ -1,27 +1,13 @@
1
1
  require "rails/generators"
2
- require "rails/generators/active_record"
3
2
 
4
3
  module InstagramConnect
5
4
  module Generators
6
- # `rails g instagram_connect:install` — writes the initializer, mounts the
7
- # engine at /instagram, and copies the database migrations.
5
+ # `rails g instagram_connect:install` — writes the initializer and mounts the
6
+ # engine. Migrations ship inside the gem and run in place (just
7
+ # `rails db:migrate`), so there is nothing to copy.
8
8
  class InstallGenerator < ::Rails::Generators::Base
9
- include ::Rails::Generators::Migration
10
-
11
9
  source_root File.expand_path("templates", __dir__)
12
10
 
13
- MIGRATIONS = %w[
14
- create_instagram_connect_accounts
15
- create_instagram_connect_conversations
16
- create_instagram_connect_messages
17
- create_instagram_connect_inbound_messages
18
- create_instagram_connect_comments
19
- ].freeze
20
-
21
- def self.next_migration_number(dirname)
22
- ActiveRecord::Generators::Base.next_migration_number(dirname)
23
- end
24
-
25
11
  def create_initializer
26
12
  template "instagram_connect.rb.tt", "config/initializers/instagram_connect.rb"
27
13
  end
@@ -30,10 +16,10 @@ module InstagramConnect
30
16
  route %(mount InstagramConnect::Engine => "/instagram")
31
17
  end
32
18
 
33
- def copy_migrations
34
- MIGRATIONS.each do |name|
35
- migration_template "#{name}.rb.tt", "db/migrate/#{name}.rb"
36
- end
19
+ def show_readme
20
+ say "\ninstagram_connect installed. Next:"
21
+ say " bin/rails db:migrate # runs the engine's migrations in place"
22
+ say " Configure config/initializers/instagram_connect.rb, then mount is at /instagram\n"
37
23
  end
38
24
  end
39
25
  end
@@ -11,25 +11,35 @@ InstagramConnect.configure do |config|
11
11
  # Pin the Graph API version.
12
12
  config.graph_version = "v21.0"
13
13
 
14
- # Rails integration.
14
+ # Rails integration. The engine's controllers inherit this controller, so they
15
+ # pick up its authentication before_actions automatically.
15
16
  config.parent_controller = "ApplicationController"
16
17
 
17
- # Require operators to be signed in to use the inbox (runs in controller
18
- # context — call your host auth here):
18
+ # Require operators to be signed in (runs in controller context call your
19
+ # host auth here):
19
20
  # config.authenticate_with = -> { authenticate_user! }
20
21
 
21
22
  # Attribute outbound replies to the current operator:
22
23
  # config.current_user_id_resolver = -> { current_user&.id }
23
24
 
24
25
  # Where the OAuth callback returns after connecting an account.
25
- config.after_connect_redirect = "/instagram"
26
+ config.after_connect_redirect = "/instagram/conversations"
27
+
28
+ # The gem ships a complete, self-styled UI. Tint it to your brand here — no
29
+ # CSS or view files needed in your app. Any subset of keys is merged over the
30
+ # gem defaults:
31
+ # config.theme = {
32
+ # primary: "#2563eb",
33
+ # font: "Inter, system-ui, sans-serif",
34
+ # radius: "12px"
35
+ # }
26
36
 
27
37
  # Optional host hooks — react to inbound events (notifications, AI, …):
28
38
  # config.on_message = ->(message) { }
29
39
  # config.on_comment = ->(comment) { }
30
40
 
31
41
  # Access tokens are encrypted at rest via Active Record Encryption. Run
32
- # `bin/rails db:encryption:init` once, or set this to false if your app has
33
- # no encryption configured (tokens will be stored in plain text).
42
+ # `bin/rails db:encryption:init` once, or set this to false if your app has no
43
+ # encryption configured.
34
44
  # config.encrypt_tokens = false
35
45
  end
@@ -12,6 +12,26 @@ module InstagramConnect
12
12
  class Configuration
13
13
  AUTH_PATHS = %i[instagram_login facebook_login].freeze
14
14
 
15
+ # The gem ships a complete, self-styled UI. A host tints it to its brand by
16
+ # overriding any of these via `config.theme = { primary: "#0057a8", ... }` —
17
+ # no CSS or view files needed in the host app.
18
+ DEFAULT_THEME = {
19
+ primary: "#2563eb",
20
+ primary_contrast: "#ffffff",
21
+ bg: "#f7f8fa",
22
+ surface: "#ffffff",
23
+ text: "#111827",
24
+ muted: "#6b7280",
25
+ border: "#e5e7eb",
26
+ radius: "12px",
27
+ font: "Inter, system-ui, -apple-system, Segoe UI, Roboto, sans-serif",
28
+ customer_bubble: "#f1f3f5",
29
+ staff_bubble: "#eef2ff",
30
+ ok: "#16a34a",
31
+ warn: "#d97706",
32
+ err: "#dc2626"
33
+ }.freeze
34
+
15
35
  attr_accessor :app_id, :app_secret, :verify_token,
16
36
  :graph_version, :redirect_uri, :encrypt_tokens,
17
37
  :parent_controller, :authenticate_with, :current_user_id_resolver,
@@ -19,6 +39,7 @@ module InstagramConnect
19
39
  :logger, :default_per_page, :inherit_host_layout,
20
40
  :media_max_bytes, :allowed_media_types, :after_connect_redirect
21
41
  attr_reader :auth_path
42
+ attr_writer :theme
22
43
 
23
44
  def initialize
24
45
  @auth_path = (ENV["INSTAGRAM_CONNECT_AUTH_PATH"] || "instagram_login").to_sym
@@ -36,7 +57,12 @@ module InstagramConnect
36
57
  @on_postback = nil
37
58
  @logger = Logger.new($stdout)
38
59
  @default_per_page = 25
39
- @inherit_host_layout = true
60
+ # Default false: the gem renders in its own self-contained layout (its own
61
+ # chrome + styles), like an admin engine. Set true to render inside the
62
+ # host's application layout instead (then include the gem styles in your
63
+ # <head> via `instagram_connect_styles`).
64
+ @inherit_host_layout = false
65
+ @theme = {}
40
66
  # Where the OAuth callback redirects after connecting an account.
41
67
  @after_connect_redirect = "/"
42
68
  @media_max_bytes = 25 * 1024 * 1024
@@ -52,6 +78,11 @@ module InstagramConnect
52
78
  @auth_path = value&.to_sym
53
79
  end
54
80
 
81
+ # The host's theme overrides merged over the gem defaults.
82
+ def resolved_theme
83
+ DEFAULT_THEME.merge(@theme || {})
84
+ end
85
+
55
86
  # Called by .configure at boot. Validates only structural config (a known
56
87
  # auth_path) so an unconfigured host can still boot; secret presence is
57
88
  # enforced lazily at the point of use (Client / OAuth).
@@ -14,6 +14,17 @@ module InstagramConnect
14
14
  app.config.filter_parameters += %i[access_token app_secret verify_token hub_verify_token]
15
15
  end
16
16
 
17
+ # Ship migrations inside the gem and run them in place — the host just runs
18
+ # `rails db:migrate`, no copying. The gem owns the data model, so every
19
+ # adopter gets exactly the same schema, versioned with the gem.
20
+ initializer "instagram_connect.append_migrations" do |app|
21
+ unless app.root.to_s == root.to_s
22
+ config.paths["db/migrate"].expanded.each do |expanded_path|
23
+ app.config.paths["db/migrate"] << expanded_path
24
+ end
25
+ end
26
+ end
27
+
17
28
  # Apply any host-provided config.instagram_connect = { ... } hash onto the
18
29
  # gem Configuration via setters (in addition to the initializer DSL).
19
30
  initializer "instagram_connect.configure" do |app|
@@ -24,5 +35,14 @@ module InstagramConnect
24
35
  end
25
36
  end
26
37
  end
38
+
39
+ # Enable at-rest token encryption on boot (and re-apply on each dev reload,
40
+ # since the Account class is reloaded fresh). Without this the `encrypts`
41
+ # decoration never runs in a host app and access tokens persist in plain
42
+ # text. Opt out with `config.encrypt_tokens = false` (e.g. no Active Record
43
+ # Encryption configured).
44
+ config.to_prepare do
45
+ InstagramConnect::Account.enable_token_encryption! if InstagramConnect.configuration.encrypt_tokens
46
+ end
27
47
  end
28
48
  end
@@ -1,3 +1,3 @@
1
1
  module InstagramConnect
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -41,6 +41,14 @@ module InstagramConnect
41
41
  def reset!
42
42
  @configuration = Configuration.new
43
43
  end
44
+
45
+ # The gem's base stylesheet, inlined into its layout so the UI is styled
46
+ # with no host asset-pipeline setup required.
47
+ def base_css
48
+ @base_css ||= File.read(
49
+ File.expand_path("../app/assets/stylesheets/instagram_connect/application.css", __dir__)
50
+ )
51
+ end
44
52
  end
45
53
  end
46
54
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: instagram_connect
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kshitiz Sinha
@@ -70,6 +70,7 @@ files:
70
70
  - MIT-LICENSE
71
71
  - README.md
72
72
  - Rakefile
73
+ - app/assets/stylesheets/instagram_connect/application.css
73
74
  - app/controllers/instagram_connect/application_controller.rb
74
75
  - app/controllers/instagram_connect/comments_controller.rb
75
76
  - app/controllers/instagram_connect/conversations_controller.rb
@@ -77,6 +78,7 @@ files:
77
78
  - app/controllers/instagram_connect/oauth_controller.rb
78
79
  - app/controllers/instagram_connect/posts_controller.rb
79
80
  - app/controllers/instagram_connect/webhooks_controller.rb
81
+ - app/helpers/instagram_connect/application_helper.rb
80
82
  - app/jobs/instagram_connect/application_job.rb
81
83
  - app/jobs/instagram_connect/ingest_job.rb
82
84
  - app/jobs/instagram_connect/refresh_tokens_job.rb
@@ -99,16 +101,16 @@ files:
99
101
  - app/views/layouts/instagram_connect/application.html.erb
100
102
  - bin/instagram_connect
101
103
  - config/routes.rb
104
+ - db/migrate/20260715072548_create_instagram_connect_accounts.rb
105
+ - db/migrate/20260715072549_create_instagram_connect_conversations.rb
106
+ - db/migrate/20260715072550_create_instagram_connect_messages.rb
107
+ - db/migrate/20260715072551_create_instagram_connect_inbound_messages.rb
108
+ - db/migrate/20260715072552_create_instagram_connect_comments.rb
102
109
  - docs/app_review_guide.md
103
110
  - docs/auth_paths.md
104
111
  - docs/roadmap.md
105
112
  - lib/generators/instagram_connect/controllers_generator.rb
106
113
  - lib/generators/instagram_connect/install/install_generator.rb
107
- - lib/generators/instagram_connect/install/templates/create_instagram_connect_accounts.rb.tt
108
- - lib/generators/instagram_connect/install/templates/create_instagram_connect_comments.rb.tt
109
- - lib/generators/instagram_connect/install/templates/create_instagram_connect_conversations.rb.tt
110
- - lib/generators/instagram_connect/install/templates/create_instagram_connect_inbound_messages.rb.tt
111
- - lib/generators/instagram_connect/install/templates/create_instagram_connect_messages.rb.tt
112
114
  - lib/generators/instagram_connect/install/templates/instagram_connect.rb.tt
113
115
  - lib/generators/instagram_connect/views_generator.rb
114
116
  - lib/instagram_connect.rb