fragmentary 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 47a728de19bdd3ebd2e42cd30cda6d6695117c4ee4d441d673c3fb768eb23edf
4
- data.tar.gz: 8fc6c056ddfead81b826ecb6b368b263eb44764771de9461db4263d56870a6c5
3
+ metadata.gz: e4f080639f4562093503dc31128557031c54fc9b3b8c7dca5502a3146645b6a2
4
+ data.tar.gz: e2a5964a78778703ffc96e57d9b760db87b61d1273643c4d5a85f598f7c6ea74
5
5
  SHA512:
6
- metadata.gz: 2f78656b88bcc12cd81a8e75f1e0dab8fb3db88ca0bfdda6d79e739b3633a08a48c458dadb2f10c842b34eaa5df5648d6e7fb20451db3921bc34b407a0ad5317
7
- data.tar.gz: 8f710ebb2a98800c676811c60dcbfd4cb2e558bb22676ad259994b9a4f8447cebe035fb51214adca26d3113d9f625590d36ae0e2621db43f1a32b354bb048a61
6
+ metadata.gz: 68f38663f547ca5961615e046c4f420d99a1d2d8a04b2b43cd143ceea17a3618787d5a58295bcd133b143aa0e1e23936fe2f91519e7def427edfcfe5b4b7e2c5
7
+ data.tar.gz: 932884c05012d780804cd4f6c50819b5b9f8abf90d0535be50d938291300aa5a5c5f26dea327febb7592c43f9805902670fb6967d98e749a0382709fe89ec959
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ### 0.5.0
2
+ - Update to support Rails 7.x and Ruby 3.x
3
+ - Asynchronous internal application requests enqueued while executing a Fragmentary::Handler are now offloaded to a separate async process to prevent them delaying other processes.
4
+ - When sending asynchronous internal requests, any queue_suffix that is present is included in the request parameters so that if the request spawns secondary requests, they can be run on the same ActiveJob queue (requires appropriate handling of the queue_suffix parameter by the application).
5
+ - Allows queued internal application requests to be removed from queues for an individual application instance.
6
+ - Destroying a fragment now destroys all of its children as well.
7
+ - The session scheme for internal requests is set to 'https' if the host_root_url of the request_queue sending the request requires it.
8
+ - Allows multiple session_users per user_type for internal application requests, each assigned to different user_groups, e.g. multiple 'admin' users, multiple 'signed_in' users etc.
9
+ - Allows RequestQueue.send_all to take additional arguments for delay, queue_suffix, priority and user_group
10
+
1
11
  ### 0.4.1
2
12
  - Fixes a problem with session initialization when sending requests generated by Handler subclasses
3
13
 
data/README.md CHANGED
@@ -18,7 +18,7 @@ In simple cases, Rails' native support for fragment caching assumes that a fragm
18
18
  ```
19
19
  The content is stored in the cache using a _key_ value derived from the record's `id` attribute. The key identifies where the cached content is stored. In addition, a _version_ value derived from the record's `updated_at` attribute is stored along with the content [^1]. If any attributes of the record change, the `updated_at` attribute also changes; the next time a browser requests the content, a mismatch will exist between the calculated and stored values of the version, causing the cache to expire and the fragment to be re-rendered using the current data.
20
20
 
21
- [^1]: Prior to Rails 5.2, the value of `updated_at` was incorporated into the cache key rather that the version. That behavior is still available by setting `config.active_record.cache_versioning = false` in `config/application.rb`.
21
+ [^1]: Prior to Rails 5.2, the value of `updated_at` was incorporated into the cache key rather than the version. That behavior is still available by setting `config.active_record.cache_versioning = false` in `config/application.rb`.
22
22
 
23
23
  For data models with a `has_one` or `has_many` association, nested (Russian Doll) caching is possible with the inner fragment representing the associated record. For example if a product has many games -
24
24
  ```
@@ -712,7 +712,7 @@ end
712
712
 
713
713
  Another scenario that occasionally arises is when internal requests created in the course of executing methods defined in a `subscribe_to` block include the path to the same page that the controller is going to render anyway in the normal course of responding to the user's current browser request. In effect, that one internal request is redundant. Although it generally won't cause any real harm to send a redundant request (remember the internal request is usually dispatched asynchronously, so it won't interfere with the synchronous response), redundancy is redundancy and you may wish to avoid it.
714
714
 
715
- In this case, it is possible within the controller to remove a request that has already been queued, after the application data has been created, updated or destroyed, by using the class method `Fragment.remove_queued_request`. The method takes two named parameters, the path of the request to be removed and the current user object, with the latter allowing it to remove the request from the correct queue, e.g.
715
+ In this case, it is possible within the controller to remove a request that has already been queued, after the application data has been created, updated or destroyed, by using the class method `Fragment.remove_queued_request`. The method takes two required keyword parameters, the path of the request to be removed and the current user object, with the latter allowing it to remove the request from only queues corresponding to the current_user's user_type, e.g.
716
716
 
717
717
  ```
718
718
  class ProductsController < ApplicationController
@@ -732,6 +732,12 @@ class ProductsController < ApplicationController
732
732
  end
733
733
  end
734
734
  ```
735
+ As written above, `remove_queued_request` will remove the specified request from all queues defined for the `user_type` of the current user. If you have multiple application instances defined, a request_queue for that user_type will exist for each instance. If you only want to remove the request from the queue for a specific instance, you can include an additional optional keyword parameter `:host_url` specifiying the root_url of that instance, e.g.
736
+ ```
737
+ ...
738
+ ProductTemplate.remove_queued_request(:request_path => "/products/#{@product.id}", :user => current_user, :host_url => Fragmentary.application_root_url)
739
+ ...
740
+ ```
735
741
 
736
742
  ### Handling AJAX Requests
737
743
 
data/fragmentary.gemspec CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.bindir = "exe"
23
23
  spec.require_paths = ["lib"]
24
24
 
25
- spec.add_runtime_dependency "rails", "~> 6.0"
25
+ spec.add_runtime_dependency "rails", ">= 6.0"
26
26
  spec.add_runtime_dependency "wisper-activerecord", "~> 1.0"
27
27
  spec.add_runtime_dependency "http", "~> 3.0.0"
28
28
  spec.add_runtime_dependency "nokogiri"
@@ -1,10 +1,14 @@
1
1
  module Fragmentary
2
2
 
3
3
  class Config
4
+
4
5
  include Singleton
6
+
5
7
  attr_accessor :current_user_method, :get_sign_in_path, :post_sign_in_path, :sign_out_path,
6
- :users, :default_user_type_mapping, :session_users, :application_root_url_column,
7
- :remote_urls, :insert_timestamps, :deployed_at, :release_name
8
+ :users, :default_user_type_mapping, :user_types, :remote_urls, :insert_timestamps,
9
+ :deployed_at, :release_name
10
+
11
+ attr_reader :user_types, :application_root_url_column
8
12
 
9
13
  def initialize
10
14
  # default
@@ -18,8 +22,7 @@ module Fragmentary
18
22
 
19
23
  def session_users=(session_users)
20
24
  raise "config.session_users must be a Hash" unless session_users.is_a?(Hash)
21
- Fragmentary.parse_session_users(session_users)
22
- @session_users = session_users
25
+ @user_types = Fragmentary.parse_session_users(session_users)
23
26
  end
24
27
 
25
28
  def application_root_url_column=(column_name)
@@ -33,12 +36,15 @@ module Fragmentary
33
36
 
34
37
  # Parse a set of session_user options, creating session_users where needed, and return a set of user_type keys.
35
38
  # session_users may take several forms:
36
- # (1) a hash whose keys are user_type strings and whose values have the form {:credentials => credentials},
37
- # where 'credentials' is either a hash of parameters to be submitted when logging in or a proc that
38
- # returns those parameters.
39
+ # (1) a hash whose keys are user_type strings and whose values consist of either:
40
+ # (a) a hash with the form {:credentials => credentials},
41
+ # where 'credentials' is either a hash of parameters to be submitted when logging in or a proc that
42
+ # returns those parameters.
43
+ # (b) an array of hashes, each of the form {:credentials => credentials, :group => group_name}
44
+ # where the ':group' element of the hash is optional
39
45
  # (2) an array of hashes as described in (1) above.
40
46
  # (3) an array of user_type strings corresponding to SessionUser objects already defined.
41
- # (4) an array containing a mixture of user_type strings and hashes as described in (1) above.
47
+ # (4) an array containing a mixture of user_type strings as described in (3) and hashes as described in (1) above.
42
48
  # Non-hash elements that don't represent existing SessionUser objects should raise an exception. Array
43
49
  # elements that are hashes should be parsed to create new SessionUser objects. Raise an exception on
44
50
  # any attempt to redefine an existing user_type.
@@ -58,11 +64,15 @@ module Fragmentary
58
64
  end
59
65
  end
60
66
  elsif session_users.is_a?(Hash)
61
- session_users.each_with_object([]) do |(k,v), acc|
62
- # k is the user_type, v is an options hash that typically looks like {:credentials => login_credentials} where
63
- # login_credentials is either a hash of parameters to be submitted at login or a proc that returns those parameters.
67
+ session_users.each_with_object([]) do |(user_type, users), acc|
68
+ # users is either an options hash that typically looks like {:credentials => credentials} or an array of such hashes,
69
+ # where credentials is either a hash of parameters to be submitted at login or a proc that returns those parameters.
64
70
  # In the latter case, the proc is executed when we actually log in to create a new session for the specified user.
65
- acc << k if user = SessionUser.new(k,v)
71
+ users = [users] unless users.is_a?(Array)
72
+ users.each do |options|
73
+ SessionUser.create(user_type, options)
74
+ end
75
+ acc << user_type
66
76
  end
67
77
  end
68
78
  end
@@ -13,13 +13,13 @@ module Fragmentary
13
13
  base.class_eval do
14
14
  include ActionView::Helpers::CacheHelper
15
15
 
16
- belongs_to :parent, :class_name => name
17
- belongs_to :root, :class_name => name
16
+ belongs_to :parent, :class_name => name, :optional => true # because a root fragment doesn't have a parent
17
+ belongs_to :root, :class_name => name, :optional => true # or a root_id
18
18
  has_many :children, :class_name => name, :foreign_key => :parent_id, :dependent => :destroy
19
- belongs_to :user
19
+ belongs_to :user, :optional => true # because only fragments that declare 'needs_user_id' require one
20
20
 
21
21
  # Don't touch the parent when we create the child - the child was created by
22
- # renderng the parent, which occured because the parent was touched, thus
22
+ # rendering the parent, which occured because the parent was touched, thus
23
23
  # triggering the current request. Touching it again would result in a
24
24
  # redundant duplicate request.
25
25
  after_commit :touch_parent, :on => [:update, :destroy]
@@ -52,7 +52,8 @@ module Fragmentary
52
52
  raise ArgumentError, "You passed Fragment #{fragment.id} to Fragment.root, but it's a child of Fragment #{fragment.parent_id}" if fragment.parent_id
53
53
  else
54
54
  klass, search_attributes, options = base_class.attributes(options)
55
- fragment = klass.where(search_attributes).includes(:children).first_or_initialize(options); fragment.save if fragment.new_record?
55
+ fragment = klass.where(search_attributes).includes(:children).first_or_initialize(options);
56
+ fragment.save if fragment.new_record?
56
57
  fragment.set_indexed_children if fragment.child_search_key
57
58
  end
58
59
  fragment
@@ -166,8 +167,13 @@ module Fragmentary
166
167
  super
167
168
  end
168
169
 
169
- def remove_queued_request(user:, request_path:)
170
- request_queues.each{|key, hsh| hsh[user_type(user)].remove_path(request_path)}
170
+ def remove_queued_request(host_url: nil, user:, request_path:)
171
+ u_type = user_type(user)
172
+ if host_url.is_a?(String) and (queue = request_queues[host_url][u_type])
173
+ queue.remove_path(request_path)
174
+ else
175
+ request_queues.each{|key, hsh| hsh[u_type].remove_path(request_path)}
176
+ end
171
177
  end
172
178
 
173
179
  def subscriber
@@ -209,7 +215,7 @@ module Fragmentary
209
215
  end
210
216
  @user_types = Fragmentary.parse_session_users(options[:session_users] || options[:types] || options[:user_types])
211
217
  def self.user_types
212
- @user_types || Fragmentary.config.session_users.keys
218
+ @user_types || Fragmentary.config.user_types
213
219
  end
214
220
  end
215
221
  end
@@ -493,14 +499,15 @@ module Fragmentary
493
499
  self.class.cache_store
494
500
  end
495
501
 
496
- def touch(*args, no_request: false)
502
+ def touch(*args, no_request: false, **kwargs)
497
503
  @no_request = no_request # stored for use in #touch_parent via the after_commit callback
498
504
  request_queues.each{|key, hsh| hsh.each{|key2, queue| queue << request}} if request && !no_request
499
- super(*args)
505
+ super(*args, **kwargs)
500
506
  end
501
507
 
502
508
  # delete the associated cache content before destroying the fragment
503
509
  def destroy(options = {})
510
+ children.each(&:destroy)
504
511
  options.delete(:delete_matches) ? delete_matched_cache : delete_cache
505
512
  @no_request = options.delete(:no_request) # stored for use in #touch_parent via the after_commit callback
506
513
  super()
@@ -596,7 +603,9 @@ module Fragmentary
596
603
 
597
604
  private
598
605
  def touch_parent
599
- parent.try(:touch, {:no_request => @no_request}) unless previous_changes["memo"]
606
+ if parent
607
+ parent.touch(no_request: @no_request) unless previous_changes["memo"]
608
+ end
600
609
  @no_request = false
601
610
  end
602
611
 
@@ -1,26 +1,4 @@
1
1
  module Fragmentary
2
-
3
- class HandlerSerializer < ActiveJob::Serializers::ObjectSerializer
4
-
5
- def serialize?(arg)
6
- arg.is_a? Fragmentary::Handler
7
- end
8
-
9
- def serialize(handler)
10
- super(
11
- {
12
- :class_name => handler.class.name,
13
- :args => handler.args
14
- }
15
- )
16
- end
17
-
18
- def deserialize(hsh)
19
- hsh[:class_name].constantize.new(hsh[:args])
20
- end
21
-
22
- end
23
-
24
2
  class Handler
25
3
  def self.all
26
4
  @@all
@@ -31,14 +9,14 @@ module Fragmentary
31
9
  end
32
10
  self.clear
33
11
 
34
- def self.create(**args)
12
+ def self.create(args)
35
13
  @@all << (handler = self.new(args))
36
14
  handler
37
15
  end
38
16
 
39
17
  attr_reader :args
40
18
 
41
- def initialize(**args)
19
+ def initialize(args)
42
20
  @args = args
43
21
  end
44
22
 
@@ -9,9 +9,7 @@ module Fragmentary
9
9
  Rails.logger.info "\n***** Dispatching task for handler class #{task.class.name}"
10
10
  task.call
11
11
  end
12
- RequestQueue.all.each do |queue|
13
- queue.start
14
- end
12
+ RequestQueue.send_all(:between => 10.seconds, :priority => 10)
15
13
  end
16
14
 
17
15
  end
@@ -12,7 +12,7 @@ module Fragmentary
12
12
  @between = between
13
13
  @queue_suffix = queue_suffix
14
14
  @priority = priority
15
- @between ? @queue.send_next_request : @queue.send_all_requests
15
+ (@between && @between.is_a?(ActiveSupport::Duration)) ? @queue.send_next_request : @queue.send_all_requests
16
16
  end
17
17
 
18
18
  def schedule_next
@@ -39,7 +39,9 @@ module Fragmentary
39
39
  end
40
40
 
41
41
  def after_destroy_broadcast
42
+ Rails.logger.info "\n***** #{start = Time.now} broadcasting :after_destroy from #{self.class.name} #{self.id}\n"
42
43
  broadcast(:after_destroy, self)
44
+ Rails.logger.info "\n***** #{Time.now} broadcast :after_destroy from #{self.class.name} #{self.id} took #{(Time.now - start) * 1000} ms\n"
43
45
  end
44
46
 
45
47
  def after_commit_broadcast
@@ -6,15 +6,19 @@ module Fragmentary
6
6
  @@all ||= []
7
7
  end
8
8
 
9
- def self.send_all(between: nil)
10
- unless between
11
- all.each{|q| q.start}
9
+ def self.send_all(delay: nil, between: nil, queue_suffix: '', priority: 0, user_group: 'default')
10
+ unless delay or between
11
+ all.each{|q| q.start(queue_suffix: queue_suffix, priority: priority, user_group: user_group)}
12
12
  else
13
+ delay ||= 0.seconds
14
+ between ||= 0.seconds
15
+ unless delay.is_a? ActiveSupport::Duration
16
+ raise TypeError, "Fragmentary::RequestQueue.send_all requires the keyword argument :delay to be of class ActiveSupport::Duration. The value provided is of class #{delay.class.name}."
17
+ end
13
18
  unless between.is_a? ActiveSupport::Duration
14
19
  raise TypeError, "Fragmentary::RequestQueue.send_all requires the keyword argument :between to be of class ActiveSupport::Duration. The value provided is of class #{between.class.name}."
15
20
  end
16
- delay = 0.seconds
17
- all.each{|q| q.start(:delay => delay += between)}
21
+ all.each{|q| q.start(delay: (delay += between), queue_suffix: queue_suffix, priority: priority, user_group: user_group)}
18
22
  end
19
23
  end
20
24
 
@@ -57,12 +61,12 @@ module Fragmentary
57
61
  @sender ||= Sender.new(self)
58
62
  end
59
63
 
60
- def send(**args)
61
- sender.start(args)
64
+ def send(**kwargs)
65
+ sender.start(**kwargs)
62
66
  end
63
67
 
64
- def method_missing(method, *args)
65
- sender.send(method, *args)
68
+ def method_missing(method, *args, **kwargs)
69
+ sender.send(method, *args, **kwargs)
66
70
  end
67
71
 
68
72
  class Sender
@@ -87,15 +91,17 @@ module Fragmentary
87
91
 
88
92
  end
89
93
 
90
- attr_reader :queue, :target, :delay, :between, :queue_suffix, :priority
94
+ attr_reader :queue, :target, :delay, :between, :queue_suffix, :priority, :user_group, :queue_name
91
95
 
92
96
  def initialize(queue)
93
97
  @queue = queue
94
98
  @target = Target.new(queue.host_root_url)
99
+ @user_group = 'default'
100
+ @queue_name = @target.queue_name
95
101
  end
96
102
 
97
103
  def session_user
98
- @session_user ||= Fragmentary::SessionUser.fetch(queue.user_type)
104
+ @session_user ||= Fragmentary::SessionUser.fetch(queue.user_type, user_group)
99
105
  end
100
106
 
101
107
  def session
@@ -103,9 +109,16 @@ module Fragmentary
103
109
  end
104
110
 
105
111
  # Send all requests, either directly or by schedule
106
- def start(delay: nil, between: nil, queue_suffix: '', priority: 0)
112
+ # Note that the :between argument here represents the time between sending consecutive *requests*, whereas in RequestQueue#send_all it
113
+ # represents the time between starting consecutive queues.
114
+ # The :between argument here is deprecated. There is no :between value passed from RequestQueue.send_all to RequestQueue::Sender#start
115
+ def start(delay: nil, between: nil, queue_suffix: '', priority: 0, user_group: 'default')
107
116
  Rails.logger.info "\n***** Processing request queue for user_type '#{queue.user_type}'\n"
108
- @delay = delay; @between = between; @queue_suffix = queue_suffix; @priority = priority
117
+ @delay = delay
118
+ @between = between
119
+ @queue_suffix = queue_suffix
120
+ @priority = priority
121
+ @user_group = user_group
109
122
  if @delay or @between
110
123
  schedule_requests(@delay)
111
124
  # sending requests by schedule makes a copy of the sender and queue objects for
@@ -124,13 +137,15 @@ module Fragmentary
124
137
  def send_next_request
125
138
  if queue.size > 0
126
139
  request = queue.next_request
127
- session.send_request(:method => request.method, :path => request.path, :parameters => request.parameters, :options => request.options)
140
+ parameters = (request.parameters || {}).merge(:queue_suffix => queue_suffix)
141
+ session.send_request(:method => request.method, :path => request.path, :parameters => parameters, :options => request.options)
128
142
  end
129
143
  end
130
144
 
131
145
  private
132
146
 
133
- def send_all_requests
147
+ def send_all_requests(user_group: 'default')
148
+ @user_group = user_group
134
149
  clear_session
135
150
  while queue.size > 0
136
151
  send_next_request
@@ -141,7 +156,7 @@ module Fragmentary
141
156
  if queue.size > 0
142
157
  clear_session
143
158
  job = SendRequestsJob.new(queue, delay: delay, between: between, queue_suffix: queue_suffix, priority: priority)
144
- job.enqueue(:wait => delay, :queue => target.queue_name + queue_suffix, :priority => priority)
159
+ job.enqueue({:wait => delay, :queue => queue_name + queue_suffix, :priority => priority})
145
160
  end
146
161
  end
147
162
 
@@ -2,26 +2,34 @@ module Fragmentary
2
2
 
3
3
  class SessionUser
4
4
 
5
+ attr_reader :options, :user_type
6
+
5
7
  def self.all
6
- @@all ||= Hash.new
8
+ @@all ||= Hash.new { |hsh, user_type| hsh[user_type] = Hash.new }
7
9
  end
10
+ private_class_method :all
11
+ private_class_method :new
8
12
 
9
- def self.fetch(key)
10
- all[key]
13
+ def self.fetch(user_type, user_group = 'default')
14
+ all[user_type][user_group]
11
15
  end
12
16
 
13
- def initialize(user_type, options={})
14
- if user = self.class.fetch(user_type)
15
- if user.options != options
16
- raise RangeError, "You can't redefine an existing SessionUser object: #{user_type.inspect}"
17
- else
18
- user
19
- end
17
+ def self.create(user_type, options={})
18
+ user_group = options[:group] || options[:user_group] || 'default'
19
+ if user = fetch(user_type, user_group)
20
+ raise RangeError, "You can't redefine an existing SessionUser object: #{user_type.inspect}" unless user.options == options
20
21
  else
21
- @user_type = user_type
22
+ @user_type = user_type # this probably not really necessary
22
23
  @options = options
23
- self.class.all.merge!({user_type => self})
24
+ user = new(user_type, options)
25
+ all[user_type].merge!(user_group => user)
24
26
  end
27
+ user
28
+ end
29
+
30
+ def initialize(user_type, options={})
31
+ @user_type = user_type # this probably not really necessary
32
+ @options = options
25
33
  end
26
34
 
27
35
  def credentials
@@ -2,19 +2,32 @@ require 'fragmentary/subscription'
2
2
 
3
3
  module Fragmentary
4
4
 
5
- # Each fragment subclass has a unique Subscriber instance reponsible for handling subscriptions
6
- # to publishers. Each subscriber maintains a hash of Subscriptions, one for each publisher it
7
- # subscribes to. The 'subscribe_to' method instantiates each new Subscription in turn and executes
8
- # its block against against the Subscriber in order to define handlers for each publisher event
9
- # of interest. Any other method invoked within a handler is delegated to the client, i.e. the
10
- # fragment subclass that the subscriber is reponsible for.
5
+ # Each fragment subclass has a unique Subscriber instance responsible for handling subscriptions
6
+ # to publishers. The subscriber is instantiated the first time it is referenced within the subscribe_to
7
+ # method called on the fragment. That method calls a method of the same name on the subscriber, which
8
+ # instantiates a new Subscription for each publisher it subscribes to and executes its block against
9
+ # the subscriber, thus defining handlers for each publisher event of interest on the subscriber,
10
+ # (rather than the fragment). Any other method invoked within a handler is delegated to the client, i.e.
11
+ # the Fragment subclass that the subscriber is responsible for.
12
+ #
13
+ # A separate subscription exists for each combination of publisher and subscriber (and thus Fragment subclass).
14
+ # When a subscription is instantiated for a given publisher, it registers itself with a Subscription::Proxy
15
+ # for that publisher. The proxy, is instantiated the first time it is referenced by a subscription to a
16
+ # particular publisher, and it subscribes on behalf of all its registered subscriptions to Wisper events
17
+ # broadcast by that publisher.
18
+ #
19
+ # The Proxy class contains the methods such as 'after_create', 'after_update' etc that are called automatically
20
+ # by Wisper when the publisher broacdcasts the corresponding events. These methods each invoke methods of the
21
+ # same name on each of proxy's registered subscriptions, and it is these that in turn invoke the methods
22
+ # defined by the subscribe_to blocks on the susbcriber associated with the subscription.
11
23
  class Subscriber
12
24
  attr_reader :client, :subscriptions
13
25
 
14
26
  def initialize(client)
15
27
  @client = client
16
28
  @subscriptions = Hash.new do |h, key|
17
- if Object.const_defined?(key) and (publisher = key.constantize) < ActiveRecord::Base
29
+ publisher = key.constantize # Ensures that the model is loaded so that the const below is defined.
30
+ if Object.const_defined?(key) and publisher < ActiveRecord::Base
18
31
  h[key] = Subscription.new(publisher, self)
19
32
  else
20
33
  nil
@@ -76,11 +76,13 @@ module Fragmentary
76
76
 
77
77
  private
78
78
  def call_method(method, record)
79
- Rails.logger.info "***** Calling #{method.inspect} on #{subscriber.client.name} with record #{record.class.name} #{record.id}"
80
- start = Time.now
81
- subscriber.public_send(method, record) if subscriber.respond_to? method
82
- finish = Time.now
83
- Rails.logger.info "***** #{method.inspect} duration: #{(finish - start) * 1000}ms\n\n"
79
+ if subscriber.respond_to? method
80
+ Rails.logger.info "***** Calling #{method.inspect} on #{subscriber.client.name} with record #{record.class.name} #{record.id}"
81
+ start = Time.now
82
+ subscriber.public_send(method, record)
83
+ finish = Time.now
84
+ Rails.logger.info "***** #{method.inspect} duration: #{(finish - start) * 1000}ms\n\n"
85
+ end
84
86
  end
85
87
 
86
88
  end
@@ -17,6 +17,7 @@ module Fragmentary
17
17
  @user = user
18
18
  @target = URI.parse(target)
19
19
  @session.host! session_host
20
+ @session.https! if (@target.scheme == 'https')
20
21
  sign_in if session_credentials
21
22
  instance_eval(&block) if block_given?
22
23
  end
@@ -43,6 +44,7 @@ module Fragmentary
43
44
 
44
45
  def relative_url_root
45
46
  @relative_url_root ||= Rails.application.config.relative_url_root
47
+ @relative_url_root ||= Rails.application.routes.default_url_options[:relative_url_root].try(:gsub, /\/$/,'')
46
48
  end
47
49
 
48
50
  def session_options
@@ -89,7 +91,7 @@ module Fragmentary
89
91
  puts " * Sending request '#{method.to_s} #{path}'" + (!parameters.nil? ? " with #{parameters.inspect}" : "")
90
92
  Rails.logger.info " * Sending request '#{method.to_s} #{path}'" + (!parameters.nil? ? " with #{parameters.inspect}" : "")
91
93
  end
92
- @session.send(method, path, options)
94
+ @session.send(method, path, **options)
93
95
  end
94
96
 
95
97
  def sign_out
@@ -1,3 +1,3 @@
1
1
  module Fragmentary
2
- VERSION = "0.4.1"
2
+ VERSION = "0.5.0"
3
3
  end
metadata CHANGED
@@ -1,27 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fragmentary
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Thomson
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-02-22 00:00:00.000000000 Z
11
+ date: 2026-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '6.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '6.0'
27
27
  - !ruby/object:Gem::Dependency
@@ -149,7 +149,7 @@ homepage: https://github.com/MarkMT/fragmentary
149
149
  licenses:
150
150
  - MIT
151
151
  metadata: {}
152
- post_install_message:
152
+ post_install_message:
153
153
  rdoc_options: []
154
154
  require_paths:
155
155
  - lib
@@ -164,8 +164,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
164
164
  - !ruby/object:Gem::Version
165
165
  version: '0'
166
166
  requirements: []
167
- rubygems_version: 3.0.9
168
- signing_key:
167
+ rubygems_version: 3.3.22
168
+ signing_key:
169
169
  specification_version: 4
170
170
  summary: Fragment modeling and caching for Rails
171
171
  test_files: []