fragmentary 0.1.0 → 0.3

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.
@@ -1,22 +1,21 @@
1
- require 'fragmentary/user_session'
2
-
3
1
  module Fragmentary
4
2
 
5
3
  class RequestQueue
6
4
 
7
- @@all = []
8
-
9
5
  def self.all
10
- @@all
6
+ @@all ||= []
11
7
  end
12
8
 
13
- attr_reader :requests, :user_type, :sender
9
+ attr_reader :requests, :user_type, :host_root_url
14
10
 
15
- def initialize(user_type)
11
+ def initialize(user_type, host_root_url)
16
12
  @user_type = user_type
13
+ # host_root_url represents where the queued *requests* are to be processed. For internal sessions it also represents where
14
+ # the *queue* will be processed by delayed_job. For external requests, the queue will be processed by the host creating the
15
+ # queue and the requests will be explicitly sent to the host_root_url.
16
+ @host_root_url = host_root_url
17
17
  @requests = []
18
- @sender = Sender.new(self)
19
- @@all << self
18
+ self.class.all << self
20
19
  end
21
20
 
22
21
  def <<(request)
@@ -30,21 +29,6 @@ module Fragmentary
30
29
  @requests.size
31
30
  end
32
31
 
33
- def session
34
- @session ||= new_session
35
- end
36
-
37
- def new_session
38
- case user_type
39
- when 'signed_in'
40
- UserSession.new('Bob')
41
- when 'admin'
42
- UserSession.new('Alice', :admin => true)
43
- else
44
- UserSession.new
45
- end
46
- end
47
-
48
32
  def next_request
49
33
  @requests.shift
50
34
  end
@@ -53,14 +37,14 @@ module Fragmentary
53
37
  @requests = []
54
38
  end
55
39
 
56
- def clear_session
57
- @session = nil
58
- end
59
-
60
40
  def remove_path(path)
61
41
  requests.delete_if{|r| r.path == path}
62
42
  end
63
43
 
44
+ def sender
45
+ @sender ||= Sender.new(self)
46
+ end
47
+
64
48
  def send(**args)
65
49
  sender.start(args)
66
50
  end
@@ -70,32 +54,39 @@ module Fragmentary
70
54
  end
71
55
 
72
56
  class Sender
57
+
73
58
  class << self
74
59
  def jobs
75
60
  ::Delayed::Job.where("(handler LIKE ?) OR (handler LIKE ?)", "--- !ruby/object:#{name} %", "--- !ruby/object:#{name}\n%")
76
61
  end
77
62
  end
78
63
 
64
+ class Target
65
+
66
+ attr_reader :url
67
+
68
+ def initialize(url)
69
+ @url = url
70
+ end
71
+
72
+ def queue_name
73
+ @url.gsub(%r{https?://}, '')
74
+ end
75
+ end
76
+
79
77
  attr_reader :queue
80
78
 
81
79
  def initialize(queue)
82
80
  @queue = queue
81
+ @target = Target.new(queue.host_root_url)
83
82
  end
84
83
 
85
- def next_request
86
- queue.next_request.to_proc
87
- end
88
-
89
- def send_next_request
90
- if queue.size > 0
91
- queue.session.instance_exec(&(next_request))
92
- end
84
+ def session_user
85
+ @session_user ||= Fragmentary::SessionUser.fetch(queue.user_type)
93
86
  end
94
87
 
95
- def send_all_requests
96
- while queue.size > 0
97
- send_next_request
98
- end
88
+ def session
89
+ @session ||= InternalUserSession.new(@target.url, session_user)
99
90
  end
100
91
 
101
92
  # Send all requests, either directly or by schedule
@@ -117,23 +108,41 @@ module Fragmentary
117
108
  @between ? send_next_request : send_all_requests
118
109
  end
119
110
 
111
+ def send_next_request
112
+ if queue.size > 0
113
+ request = queue.next_request
114
+ session.send_request(:method => request.method, :path => request.path, :parameters => request.parameters, :options => request.options)
115
+ end
116
+ end
117
+
120
118
  def success
121
119
  schedule_requests(@between) if queue.size > 0
122
120
  end
123
121
 
124
122
  private
125
123
 
124
+ def send_all_requests
125
+ while queue.size > 0
126
+ send_next_request
127
+ end
128
+ end
129
+
126
130
  def schedule_requests(delay=0.seconds)
127
131
  if queue.size > 0
128
- queue.clear_session
132
+ clear_session
129
133
  Delayed::Job.transaction do
130
134
  self.class.jobs.destroy_all
131
- Delayed::Job.enqueue self, :run_at => delay.from_now
135
+ Delayed::Job.enqueue self, :run_at => delay.from_now, :queue => @target.queue_name
132
136
  end
133
137
  end
134
138
  end
135
139
 
140
+ def clear_session
141
+ @session = nil
142
+ end
143
+
136
144
  end
137
145
 
138
146
  end
147
+
139
148
  end
@@ -0,0 +1,38 @@
1
+ module Fragmentary
2
+
3
+ class SessionUser
4
+
5
+ def self.all
6
+ @@all ||= Hash.new
7
+ end
8
+
9
+ def self.fetch(key)
10
+ all[key]
11
+ end
12
+
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
20
+ else
21
+ @user_type = user_type
22
+ @options = options
23
+ self.class.all.merge!({user_type => self})
24
+ end
25
+ end
26
+
27
+ def credentials
28
+ options[:credentials]
29
+ end
30
+
31
+ protected
32
+ def options
33
+ @options
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -24,7 +24,9 @@ module Fragmentary
24
24
 
25
25
  def subscribe_to(publisher, block)
26
26
  if subscriptions[publisher.name]
27
- instance_exec(&block)
27
+ mod = Module.new
28
+ mod.module_exec(&block)
29
+ self.extend mod
28
30
  end
29
31
  end
30
32
 
@@ -44,7 +44,7 @@ module Fragmentary
44
44
  end
45
45
 
46
46
  include ActiveSupport::Callbacks
47
- define_callbacks :after_destroy
47
+ define_callbacks :after_create, :after_destroy
48
48
 
49
49
  attr_reader :subscriber
50
50
  attr_accessor :record
@@ -55,7 +55,10 @@ module Fragmentary
55
55
  end
56
56
 
57
57
  def after_create(record)
58
- call_method(:"create_#{record.class.model_name.param_key}_successful", record)
58
+ run_callbacks :after_create do
59
+ @record = record
60
+ call_method(:"create_#{record.class.model_name.param_key}_successful", record)
61
+ end
59
62
  end
60
63
 
61
64
  def after_update(record)
@@ -1,53 +1,155 @@
1
1
  require 'rails/console/app'
2
+ require 'http'
3
+ require 'nokogiri'
2
4
 
3
5
  module Fragmentary
4
6
 
5
- class UserSession
7
+ class InternalUserSession
6
8
 
7
9
  include Rails::ConsoleMethods
8
10
 
9
- attr_reader :session, :user
10
-
11
- def initialize(*user, &block)
11
+ def initialize(target, user=nil, &block)
12
12
  # app is from Rails::ConsoleMethods. It returns an object ActionDispatch::Integration::Session.new(Rails.application)
13
13
  # with some extensions. See https://github.com/rails/rails/blob/master/railties/lib/rails/console/app.rb
14
14
  # The session object has instance methods get, post etc.
15
15
  # See https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/testing/integration.rb
16
16
  @session = app
17
- sign_in if @user = get_user(*user)
17
+ @user = user
18
+ @target = URI.parse(target)
19
+ @session.host! session_host
20
+ sign_in if session_credentials
18
21
  instance_eval(&block) if block_given?
19
22
  end
20
23
 
21
- def method_missing(method, *args)
22
- session.send(method, *args)
24
+ def session_host
25
+ @session_host ||= @target.host + (port=@target.port ? ":#{port}" : "")
23
26
  end
24
27
 
25
- def sign_out
26
- post '/users/sign_out', {:_method => 'delete', :authenticity_token => request.session[:_csrf_token]}
28
+ def session_sign_in_path
29
+ @sign_in_path ||= Fragmentary.config.get_sign_in_path
30
+ end
31
+
32
+ def session_sign_out_path
33
+ @sign_out_path ||= Fragmentary.config.sign_out_path
34
+ end
35
+
36
+ def session_credentials
37
+ return nil unless @user
38
+ @credentials ||= begin
39
+ credentials = @user.credentials
40
+ credentials.is_a?(Proc) ? credentials.call : credentials
41
+ end
42
+ end
43
+
44
+ def relative_url_root
45
+ @relative_url_root ||= Rails.application.config.relative_url_root
46
+ end
47
+
48
+ def session_options
49
+ @session_options ||= relative_url_root ? {:env => {'SCRIPT_NAME' => relative_url_root}} : {}
50
+ end
51
+
52
+ def method_missing(method, *args)
53
+ @session.send(method, *args)
27
54
  end
28
55
 
29
56
  def sign_in
30
- get "/users/sign_in" # necessary in order to get the csrf token
57
+ raise "Can't sign in without user credentials" unless session_credentials
58
+ send_request(:method => :get, :path => session_sign_in_path, :options => session_options) # necessary in order to get the csrf token
31
59
  # NOTE: In Rails 5, params is changed to a named argument, i.e. :params => {...}. Will need to be changed.
32
- post "/users/sign_in", {:user => {:email => user.email, :password => user.try(:password)},
33
- :authenticity_token => request.session[:_csrf_token]}
34
- if session.redirect?
60
+ # Note that request is called on session, returning an ActionDispatch::Request; request.session is an ActionDispatch::Request::Session
61
+ puts " * Signing in as #{session_credentials.inspect}"
62
+ parameters = session_credentials.merge(:authenticity_token => request.session[:_csrf_token])
63
+ send_request(:method => :post, :path => session_sign_in_path, :parameters => parameters, :options => session_options)
64
+ if @session.redirect?
35
65
  follow_redirect!
36
66
  else
37
- raise "Sign in failed for user #{user.name} #{user.password}"
67
+ raise "Sign in failed with credentials #{@credentials.inspect}"
68
+ end
69
+ end
70
+
71
+ def follow_redirect!
72
+ raise "not a redirect! #{status} #{status_message}" unless redirect?
73
+ if (url = response.location) =~ %r{://}
74
+ destination = URI.parse(url)
75
+ path = destination.query ? "#{destination.path}?#{destination.query}" : destination.path
76
+ end
77
+ path = relative_url_root ? path.gsub(Regexp.new("^#{relative_url_root}"), "") : path
78
+ send_request(:method => :get, :path => path, :options => session_options)
79
+ status
80
+ end
81
+
82
+ def send_request(method:, path:, parameters: nil, options: {})
83
+ options.merge!({:params => parameters})
84
+ options.merge!(session_options)
85
+ if options.try(:[], :xhr)
86
+ Rails.logger.info " * Sending xhr request '#{method.to_s} #{path}'" + (!parameters.nil? ? " with #{parameters.inspect}" : "")
87
+ else
88
+ Rails.logger.info " * Sending request '#{method.to_s} #{path}'" + (!parameters.nil? ? " with #{parameters.inspect}" : "")
38
89
  end
90
+ @session.send(method, path, options)
91
+ end
92
+
93
+ def sign_out
94
+ # request is called on session, returning an ActionDispatch::Request; request.session is an ActionDispatch::Request::Session
95
+ parameters = {:_method => 'delete', :authenticity_token => request.session[:_csrf_token]}
96
+ send_request(:method => :post, :path => session_sign_out_path, :parameters => parameters, :options => session_options)
97
+ end
98
+
99
+ end
100
+
101
+ class ExternalUserSession
102
+
103
+ def initialize(target, user=nil)
104
+ @target = URI.parse(target)
105
+ @relative_url_root = @target.path
106
+ @session = HTTP.persistent(target)
107
+ @cookie = nil
108
+ @authenticity_token = nil
109
+ sign_in if @credentials = session_credentials(user)
39
110
  end
40
111
 
41
- private
42
- def get_user(*attrs)
43
- return nil if attrs.nil?
44
- if (user = attrs.shift).is_a? User and user.password
45
- user
46
- elsif user.is_a? String
47
- User.test_user(user, :admin => attrs.shift.try(:delete, :admin))
112
+ def send_request(method:, path:, parameters: nil, options: {})
113
+ if options.try(:[], :xhr)
114
+ Rails.logger.info " * Sending xhr request '#{method.to_s} #{path}'" + (!parameters.nil? ? " with #{parameters.inspect}" : "")
115
+ else
116
+ Rails.logger.info " * Sending request '#{method.to_s} #{path}'" + (!parameters.nil? ? " with #{parameters.inspect}" : "")
117
+ end
118
+ unless path =~ %r{://}
119
+ path = @relative_url_root + path
120
+ end
121
+ cookies = @cookie ? {@cookie.name.to_sym => @cookie.value} : {}
122
+ headers = options.try(:delete, :headers) || {}
123
+ headers.merge!({:'X-Requested-With' => 'XMLHttpRequest'}) if options.try(:delete, :xhr)
124
+ response = @session.cookies(cookies).headers(headers).send(method, path, {:json => parameters})
125
+ @cookie = response.cookies.first
126
+ @authenticity_token = Nokogiri::HTML.parse(response.to_s).css('head meta[name="csrf-token"]').first.try(:[], 'content')
127
+ if (response.code >=300) && (response.code <=399)
128
+ location = response.headers[:location]
129
+ options = {:headers => {:accept => "text/html,application/xhtml+xml,application/xml"}}
130
+ response = send_request(:method => :get, :path => location, :parameters => nil, :options => options)
48
131
  end
132
+ response
133
+ end
134
+
135
+ def session_credentials(user)
136
+ credentials = user.try(:credentials)
137
+ credentials.is_a?(Proc) ? credentials.call : credentials
49
138
  end
50
139
 
140
+ def sign_in
141
+ # The first request retrieves the authentication token
142
+ response = send_request(:method => :get, :path => Fragmentary.config.get_sign_in_path)
143
+ puts " * Signing in as #{@credentials.inspect}"
144
+ response = send_request(:method => :post, :path => Fragmentary.config.post_sign_in_path,
145
+ :parameters => @credentials.merge(:authenticity_token => @authenticity_token),
146
+ :options => {:headers => {:accept => "text/html,application/xhtml+xml,application/xml"}})
147
+ end
148
+
149
+ def sign_out
150
+ send_request(:method => :delete, :path => Fragmentary.config.sign_out_path, :parameters => {:authenticity_token => @authenticity_token})
151
+ @session.close
152
+ end
51
153
  end
52
154
 
53
155
  end
@@ -1,3 +1,3 @@
1
1
  module Fragmentary
2
- VERSION = "0.1.0"
2
+ VERSION = "0.3"
3
3
  end
@@ -1,7 +1,7 @@
1
1
  module Fragmentary
2
2
 
3
3
  class Widget
4
- attr_reader :template, :key, :match
4
+ attr_reader :template, :match
5
5
 
6
6
  def self.inherited subclass
7
7
  super if defined? super
@@ -18,7 +18,6 @@ module Fragmentary
18
18
 
19
19
  def initialize(template, key)
20
20
  @template = template
21
- @key = key
22
21
  @match = key.match(pattern)
23
22
  end
24
23
 
@@ -30,6 +29,8 @@ module Fragmentary
30
29
  match ? content : nil
31
30
  end
32
31
 
32
+ private
33
+
33
34
  def content
34
35
  "Undefined Widget"
35
36
  end
@@ -41,13 +42,15 @@ module Fragmentary
41
42
 
42
43
  def initialize(template, key)
43
44
  super
44
- @current_user = template.respond_to?(:current_user) ? template.current_user : nil
45
+ @current_user = Fragmentary::Template.new(template).current_user
45
46
  end
46
47
 
47
48
  def _content
48
49
  match ? user_content : nil
49
50
  end
50
51
 
52
+ private
53
+
51
54
  def user_content
52
55
  current_user ? content : ""
53
56
  end
@@ -4,7 +4,7 @@ module Fragmentary
4
4
 
5
5
  include ActionView::Helpers::JavaScriptHelper
6
6
 
7
- attr_reader :template, :current_user, :widget_container
7
+ attr_reader :template, :widget_container
8
8
 
9
9
  def initialize(template)
10
10
  @template = template
@@ -31,7 +31,7 @@ module Fragmentary
31
31
  widget._content
32
32
  end
33
33
  else
34
- "Oops! Widget not found."
34
+ "Oops! Widget for #{widget_key} not found."
35
35
  end
36
36
  end
37
37
  # The gsub replaces instances of '%' that aren't part of widget specifications with '%%', preventing
data/lib/fragmentary.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'fragmentary/version'
2
+ require 'fragmentary/config'
2
3
  require 'fragmentary/fragments_helper'
3
4
  require 'fragmentary/subscriber'
4
5
  require 'fragmentary/request_queue'
@@ -6,6 +7,16 @@ require 'fragmentary/request'
6
7
  require 'fragmentary/fragment'
7
8
  require 'fragmentary/handler'
8
9
  require 'fragmentary/user_session'
10
+ require 'fragmentary/session_user'
9
11
  require 'fragmentary/widget_parser'
10
12
  require 'fragmentary/widget'
11
13
  require 'fragmentary/publisher'
14
+
15
+ module Fragmentary
16
+ def self.config
17
+ @config ||= Fragmentary::Config.instance
18
+ yield @config if block_given?
19
+ @config
20
+ end
21
+ class << self; alias setup config; end
22
+ end
metadata CHANGED
@@ -1,35 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fragmentary
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: '0.3'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Thomson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-12-07 00:00:00.000000000 Z
11
+ date: 2022-01-13 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
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: 4.0.0
20
- - - "<"
17
+ - - "~>"
21
18
  - !ruby/object:Gem::Version
22
- version: '5'
19
+ version: '5.0'
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- version: 4.0.0
30
- - - "<"
24
+ - - "~>"
31
25
  - !ruby/object:Gem::Version
32
- version: '5'
26
+ version: '5.0'
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: delayed_job_active_record
35
29
  requirement: !ruby/object:Gem::Requirement
@@ -58,6 +52,34 @@ dependencies:
58
52
  - - "~>"
59
53
  - !ruby/object:Gem::Version
60
54
  version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: http
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 3.0.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 3.0.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: nokogiri
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
61
83
  - !ruby/object:Gem::Dependency
62
84
  name: bundler
63
85
  requirement: !ruby/object:Gem::Requirement
@@ -110,6 +132,7 @@ files:
110
132
  - ".gitignore"
111
133
  - ".rspec"
112
134
  - ".travis.yml"
135
+ - CHANGELOG.md
113
136
  - Gemfile
114
137
  - LICENSE.txt
115
138
  - README.md
@@ -118,6 +141,7 @@ files:
118
141
  - bin/setup
119
142
  - fragmentary.gemspec
120
143
  - lib/fragmentary.rb
144
+ - lib/fragmentary/config.rb
121
145
  - lib/fragmentary/dispatcher.rb
122
146
  - lib/fragmentary/fragment.rb
123
147
  - lib/fragmentary/fragments_helper.rb
@@ -125,6 +149,7 @@ files:
125
149
  - lib/fragmentary/publisher.rb
126
150
  - lib/fragmentary/request.rb
127
151
  - lib/fragmentary/request_queue.rb
152
+ - lib/fragmentary/session_user.rb
128
153
  - lib/fragmentary/subscriber.rb
129
154
  - lib/fragmentary/subscription.rb
130
155
  - lib/fragmentary/user_session.rb
@@ -150,8 +175,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
150
175
  - !ruby/object:Gem::Version
151
176
  version: '0'
152
177
  requirements: []
153
- rubyforge_project:
154
- rubygems_version: 2.4.8
178
+ rubygems_version: 3.0.8
155
179
  signing_key:
156
180
  specification_version: 4
157
181
  summary: Fragment modeling and caching for Rails