puppeteer_entity 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 633fdd774eaaa00ceb3fe75f627b18f247839546a278bd8a978b2a6dc9bbc4b1
4
+ data.tar.gz: 89e521f5f856dbfba6dffc1d57de071a743b9a2708ef02a3831fef4b66dcc3e8
5
+ SHA512:
6
+ metadata.gz: fb2bf515eaa92f4b89375be8aef47568c1aca6727016ba9b23c04ae0795fd19f79752c029a719743bc17f1e6d08dff58179a5d32e238de77822c1550f1da844c
7
+ data.tar.gz: ac8b09ae2c7a883aa271a0500400fbb4ea80bff89d9ebea18f11e4f565fdf869218b6e983b669f21e16cebdc31345f992af166b48408ba78b9c21658d82261eb
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ # Omakase Ruby styling for Rails
2
+ inherit_gem: { rubocop-rails-omakase: rubocop.yml }
3
+ # Overwrite or add rules to create your own house style
4
+ #
5
+ # # Use `[a, [b, c]]` not `[ a, [ b, c ] ]`
6
+ # Layout/SpaceInsideArrayLiteralBrackets:
7
+ # Enabled: false
8
+
9
+ Style/HashSyntax:
10
+ EnforcedShorthandSyntax: always
11
+
12
+ AllCops:
13
+ TargetRubyVersion: 3.3.5
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.3.5
data/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # CHANGELOG
2
+
3
+ ## 0.1.1
4
+
5
+ * Drop support for Ruby < 3.1
6
+
7
+ ## 0.1.0
8
+
9
+ * First release, yay!
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Tomáš Celizna
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # PuppeteerEntity
2
+
3
+ [![Test](https://github.com/tomasc/puppeteer_entity/actions/workflows/test.yml/badge.svg)](https://github.com/tomasc/puppeteer_entity/actions/workflows/test.yml)
4
+
5
+ `PuppeteerEntity` is a Ruby gem that provides a simple and intuitive interface for interacting with Puppeteer, a headless Chrome browser. It allows you to take screenshots, capture content, and perform other actions on web pages.
6
+
7
+ ## Installation
8
+
9
+ To install PuppeteerEntity, add it to your application's Gemfile and run `bundle install`:
10
+
11
+ ```bash
12
+ bundle add puppeteer_entity
13
+ ```
14
+
15
+ If you're not using Bundler, you can install the gem directly:
16
+
17
+ ```bash
18
+ gem install puppeteer_entity
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ To use PuppeteerEntity, create a new instance of the `PuppeteerEntity::Screenshot` or `PuppeteerEntity::Content` class, passing the necessary arguments. Then, call the `response` method on the instance to get the HTTP response.
24
+
25
+ ```ruby
26
+ args = { url: "https://example.com" }
27
+ entity = PuppeteerEntity::Screenshot.new(args)
28
+ response = entity.response
29
+ ```
30
+
31
+ ## Development
32
+
33
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
34
+
35
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
36
+
37
+ ## Contributing
38
+
39
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/puppeteer_entity.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ task default: :test
@@ -0,0 +1,7 @@
1
+ services:
2
+ browserless:
3
+ image: ghcr.io/browserless/chromium:v2.21.1
4
+ ports:
5
+ - "4444:3000"
6
+ environment:
7
+ - MAX_CONCURRENT_SESSIONS=10
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppeteerEntity
4
+ module Attributes
5
+ class Authenticate < Dry::Struct
6
+ transform_keys(&:to_sym)
7
+
8
+ attribute :username, Types::Coercible::String
9
+ attribute :password, Types::Coercible::String
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppeteerEntity
4
+ module Attributes
5
+ class Content < Dry::Struct
6
+ transform_keys(&:to_sym)
7
+
8
+ attribute? :add_script_tag, Types::Array do
9
+ attributes_from Attributes::ScriptTag
10
+ end
11
+
12
+ attribute? :add_style_tag, Types::Array do
13
+ attributes_from Attributes::StyleTag
14
+ end
15
+
16
+ attribute? :authenticate do
17
+ attributes_from Attributes::Authenticate
18
+ end
19
+
20
+ # When bestAttempt is set to true, browserless attempt to proceed when "awaited" events fail or timeout. This includes things like goto, waitForSelector, and more.
21
+ attribute? :best_attempt, Types::Bool.optional
22
+
23
+ attribute? :cookies, Types::Array do
24
+ attributes_from Attributes::Cookie
25
+ end
26
+
27
+ attribute? :emulate_media_type, Types::Coercible::String.optional
28
+
29
+ attribute? :goto_options do
30
+ attributes_from Attributes::GotoOptions
31
+ end
32
+
33
+ attribute? :html, Types::Coercible::String.optional
34
+
35
+ attribute? :reject_request_pattern, Types::Array.of(Types::String).optional
36
+ attribute? :reject_resource_types, Types::Array.of(Types::Coercible::String.enum("cspviolationreport", "document", "eventsource", "fetch", "font", "image", "manifest", "media", "other", "ping", "prefetch", "preflight", "script", "signedexchange", "stylesheet", "texttrack", "websocket", "xhr")).optional
37
+ attribute? :request_interceptors, Types::Array.optional
38
+
39
+ attribute? :set_extra_http_headers, Types::Array.optional
40
+ attribute? :set_java_script_enabled, Types::Bool.optional
41
+
42
+ attribute? :url, Types::URL.optional
43
+ attribute? :user_agent, Types::Coercible::String.optional
44
+
45
+ attribute? :viewport do
46
+ attributes_from Attributes::Viewport
47
+ end
48
+
49
+ attribute? :wait_for_event do
50
+ attributes_from Attributes::WaitForEvent
51
+ end
52
+
53
+ attribute? :wait_for_function do
54
+ attributes_from Attributes::WaitForFunction
55
+ end
56
+
57
+ attribute? :wait_for_selector do
58
+ attributes_from Attributes::WaitForSelector
59
+ end
60
+
61
+ attribute? :wait_for_timeout, Types::Coercible::Integer.optional
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppeteerEntity
4
+ module Attributes
5
+ class Cookie < Dry::Struct
6
+ transform_keys(&:to_sym)
7
+ attribute :name, Types::Coercible::String
8
+ attribute :value, Types::Coercible::String
9
+ # The request-URI to associate with the setting of the cookie. This value can affect the default domain, path, source port, and source scheme values of the created cookie.
10
+ attribute? :url, Types::URL.optional
11
+ attribute? :domain, Types::Coercible::String.optional
12
+ attribute? :path, Types::Coercible::String.optional
13
+ attribute? :secure, Types::Bool.optional
14
+ attribute? :http_only, Types::Bool.optional
15
+ attribute? :same_site, Types::Coercible::String.enum("Lax", "None", "Strict").optional
16
+ attribute? :expires, Types::Coercible::Integer.optional
17
+ attribute? :priority, Types::Coercible::String.enum("High", "Low", "Medium").optional
18
+ attribute? :same_party, Types::Bool.optional
19
+ attribute? :source_scheme, Types::Coercible::String.enum("NonSecure", "Secure", "Unset").optional
20
+ # Cookie source port. Valid values are {-1, [1, 65535]}, -1 indicates an unspecified port. An unspecified port value allows protocol clients to emulate legacy cookie scope for the port. This is a temporary ability and it will be removed in the future.
21
+ attribute? :source_port, Types::Coercible::Integer.optional
22
+ # Cookie partition key. The site of the top-level URL the browser was visiting at the start of the request to the endpoint that set the cookie. If not set, the cookie will be set as not partitioned.
23
+ attribute? :partition_key, Types::Coercible::String.optional
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppeteerEntity
4
+ module Attributes
5
+ class GotoOptions < Dry::Struct
6
+ transform_keys(&:to_sym)
7
+
8
+ # If provided, it will take preference over the referer header value set by {@link Page.setExtraHTTPHeaderspage.setExtraHTTPHeaders()}.
9
+ attribute? :referer, Types::Coercible::String.optional
10
+ # If provided, it will take preference over the referer-policy header value set by {@link Page.setExtraHTTPHeaderspage.setExtraHTTPHeaders()}.
11
+ attribute? :referrer_policy, Types::Coercible::String.optional
12
+ # Maximum wait time in milliseconds. Pass 0 to disable the timeout.
13
+ # The default value can be changed by using the {@link Page.setDefaultTimeout} or {@link Page.setDefaultNavigationTimeout} methods.
14
+ attribute? :timeout, Types::Coercible::Integer.optional
15
+ # When to consider waiting succeeds. Given an array of event strings, waiting is considered to be successful after all events have been fired.
16
+ attribute? :wait_until, Types::Array.of(Types::Coercible::String.enum("domcontentloaded", "load", "networkidle0", "networkidle2")).optional
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppeteerEntity
4
+ module Attributes
5
+ class Pdf < Dry::Struct
6
+ transform_keys(&:to_sym)
7
+
8
+ attribute? :add_script_tag, Types::Array do
9
+ attributes_from Attributes::ScriptTag
10
+ end
11
+
12
+ attribute? :add_style_tag, Types::Array do
13
+ attributes_from Attributes::StyleTag
14
+ end
15
+
16
+ attribute? :authenticate do
17
+ attributes_from Attributes::Authenticate
18
+ end
19
+
20
+ # When bestAttempt is set to true, browserless attempt to proceed when "awaited" events fail or timeout. This includes things like goto, waitForSelector, and more.
21
+ attribute? :best_attempt, Types::Bool.optional
22
+
23
+ attribute? :block_modals, Types::Bool.optional
24
+
25
+ attribute? :cookies, Types::Array do
26
+ attributes_from Attributes::Cookie
27
+ end
28
+
29
+ attribute? :emulate_media_type, Types::Coercible::String.optional
30
+
31
+ attribute? :goto_options do
32
+ attributes_from Attributes::GotoOptions
33
+ end
34
+
35
+ attribute? :html, Types::Coercible::String.optional
36
+
37
+ attribute? :options do
38
+ attribute? :scale, Types::Float.optional.constrained(included_in: 0.1..2.0)
39
+ attribute? :display_header_footer, Types::Bool.optional
40
+ # HTML template for the print header. Should be valid HTML with the following classes used to inject values into them:
41
+ # * "date" formatted print date
42
+ # * "title" document title
43
+ # * "url" document location
44
+ # * "pageNumber" current page number
45
+ # * "totalPages" total pages in the document
46
+ attribute? :header_template, Types::Coercible::String.optional
47
+ # HTML template for the print footer. Has the same constraints and support for special classes as {@link PDFOptionsPDFOptions.headerTemplate}.
48
+ attribute? :footer_template, Types::Coercible::String.optional
49
+ # Print background graphics.
50
+ attribute? :print_background, Types::Bool.optional
51
+ attribute? :landscape, Types::Bool.optional
52
+ # Paper ranges to print, e.g. 1-5, 8, 11-13.
53
+ attribute? :page_ranges, Types::Coercible::String.optional
54
+ attribute? :format, Types::Coercible::String.enum("A0", "A1", "A2", "A3", "A4", "A5", "A6", "LEDGER", "LEGAL", "LETTER", "Ledger", "Legal", "Letter", "TABLOID", "Tabloid", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "ledger", "legal", "letter", "tabloid").optional
55
+ # Sets the width of paper. You can pass in a number or a string with a unit.
56
+ attribute? :width, Types::Coercible::String.optional
57
+ # Sets the height of paper. You can pass in a number or a string with a unit.
58
+ attribute? :height, Types::Coercible::String.optional
59
+ # Give any CSS @page size declared in the page priority over what is declared in the width or height or format option.
60
+ attribute? :prefer_css_page_size, Types::Bool.optional
61
+ attribute? :margin do
62
+ attribute? :top, Types::Coercible::String.optional
63
+ attribute? :bottom, Types::Coercible::String.optional
64
+ attribute? :left, Types::Coercible::String.optional
65
+ attribute? :right, Types::Coercible::String.optional
66
+ end
67
+ # The path to save the file to.
68
+ attribute? :path, Types::String.optional
69
+ # Hides default white background and allows generating pdfs with transparency.
70
+ attribute? :omit_background, Types::Bool.optional
71
+ # Generate tagged (accessible) PDF.
72
+ attribute? :tagged, Types::Bool.optional
73
+ # Timeout in milliseconds. Pass 0 to disable timeout.
74
+ attribute? :timeout, Types::Coercible::Integer.optional
75
+ end
76
+
77
+ attribute? :reject_request_pattern, Types::Array.of(Types::String).optional
78
+ attribute? :reject_resource_types, Types::Array.of(Types::Coercible::String.enum("cspviolationreport", "document", "eventsource", "fetch", "font", "image", "manifest", "media", "other", "ping", "prefetch", "preflight", "script", "signedexchange", "stylesheet", "texttrack", "websocket", "xhr")).optional
79
+ attribute? :request_interceptors, Types::Array.optional
80
+
81
+ attribute? :set_extra_http_headers, Types::Array.optional
82
+ attribute? :set_java_script_enabled, Types::Bool.optional
83
+
84
+ attribute? :url, Types::URL.optional
85
+ attribute? :user_agent, Types::Coercible::String.optional
86
+
87
+ attribute? :viewport do
88
+ attributes_from Attributes::Viewport
89
+ end
90
+
91
+ attribute? :wait_for_event do
92
+ attributes_from Attributes::WaitForEvent
93
+ end
94
+
95
+ attribute? :wait_for_function do
96
+ attributes_from Attributes::WaitForFunction
97
+ end
98
+
99
+ attribute? :wait_for_selector do
100
+ attributes_from Attributes::WaitForSelector
101
+ end
102
+
103
+ attribute? :wait_for_timeout, Types::Coercible::Integer.optional
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppeteerEntity
4
+ module Attributes
5
+ class Query < Dry::Struct
6
+ transform_keys(&:to_sym)
7
+
8
+ # Whether or nor to load ad-blocking extensions for the session. This currently uses uBlock Origin and may cause certain sites to not load properly.
9
+ attribute? :block_ads, Types::Bool.optional
10
+ # Launch options, which can be either an object of puppeteer.launch options or playwright.launchServer options, depending on the API.
11
+ attribute? :launch do
12
+ attribute? :args, Types::Array.of(Types::String).optional
13
+ attribute? :default_viewport do
14
+ attributes_from Attributes::Viewport
15
+ end
16
+ end
17
+ # Whether or nor to record the session. The browser will run in "head-full" mode, and recording is started and closed via the embedded browserless API. Please refer to the "Recording" section in the live documentation site for more details.
18
+ attribute? :record, Types::Bool.optional
19
+ # Override the system-level timeout for this request. Accepts a value in milliseconds.
20
+ attribute? :timeout, Types::Integer.optional
21
+ # The authorization token
22
+ attribute? :token, Types::String.optional
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppeteerEntity
4
+ module Attributes
5
+ class Screenshot < Dry::Struct
6
+ transform_keys(&:to_sym)
7
+
8
+ attribute? :add_script_tag, Types::Array do
9
+ attributes_from Attributes::ScriptTag
10
+ end
11
+
12
+ attribute? :add_style_tag, Types::Array do
13
+ attributes_from Attributes::StyleTag
14
+ end
15
+
16
+ attribute? :authenticate do
17
+ attributes_from Attributes::Authenticate
18
+ end
19
+
20
+ # When bestAttempt is set to true, browserless attempt to proceed when "awaited" events fail or timeout. This includes things like goto, waitForSelector, and more.
21
+ attribute? :best_attempt, Types::Bool.optional
22
+
23
+ attribute? :cookies, Types::Array do
24
+ attributes_from Attributes::Cookie
25
+ end
26
+
27
+ attribute? :emulate_media_type, Types::Coercible::String.optional
28
+
29
+ attribute? :goto_options do
30
+ attributes_from Attributes::GotoOptions
31
+ end
32
+
33
+ attribute? :html, Types::Coercible::String.optional
34
+
35
+ attribute? :options do
36
+ attribute? :optimize_for_speed, Types::Bool.optional
37
+ attribute :type, Types::Coercible::String.default("png").enum("jpeg", "png", "webp")
38
+ # Quality of the image, between 0-100. Not applicable to png images.
39
+ attribute? :quality, Types::Integer.optional.constrained(included_in: 0..100)
40
+ # Capture the screenshot from the surface, rather than the view.
41
+ attribute? :from_surface, Types::Bool.optional
42
+ # When true, takes a screenshot of the full page.
43
+ attribute? :full_page, Types::Bool.optional
44
+ # Hides default white background and allows capturing screenshots with transparency.
45
+ attribute? :omit_background, Types::Bool.optional
46
+ # The file path to save the image to. The screenshot type will be inferred from file extension. If path is a relative path, then it is resolved relative to current working directory. If no path is provided, the image won't be saved to the disk.
47
+ attribute? :path, Types::String.optional
48
+ attribute? :clip do
49
+ attribute? :scale, Types::Float.optional
50
+ # the width of the element in pixels.
51
+ attribute :width, Types::Integer
52
+ # the height of the element in pixels.
53
+ attribute :height, Types::Integer
54
+ attribute :x, Types::Integer
55
+ attribute :y, Types::Integer
56
+ end
57
+ # Encoding of the image.
58
+ attribute? :encoding, Types::Coercible::String.optional.enum("base64", "binary")
59
+ # Capture the screenshot beyond the viewport.
60
+ attribute? :capture_beyond_viewport, Types::Bool.optional
61
+ end
62
+
63
+ attribute? :reject_request_pattern, Types::Array.of(Types::String).optional
64
+ attribute? :reject_resource_types, Types::Array.of(Types::Coercible::String.enum("cspviolationreport", "document", "eventsource", "fetch", "font", "image", "manifest", "media", "other", "ping", "prefetch", "preflight", "script", "signedexchange", "stylesheet", "texttrack", "websocket", "xhr")).optional
65
+ attribute? :request_interceptors, Types::Array.optional
66
+
67
+ attribute? :scroll_page, Types::Bool.optional
68
+ attribute? :selector, Types::Coercible::String.optional
69
+
70
+ attribute? :set_extra_http_headers, Types::Array.optional
71
+ attribute? :set_java_script_enabled, Types::Bool.optional
72
+
73
+ attribute? :url, Types::URL.optional
74
+ attribute? :user_agent, Types::Coercible::String.optional
75
+
76
+ attribute? :viewport do
77
+ attributes_from Attributes::Viewport
78
+ end
79
+
80
+ attribute? :wait_for_event do
81
+ attributes_from Attributes::WaitForEvent
82
+ end
83
+
84
+ attribute? :wait_for_function do
85
+ attributes_from Attributes::WaitForFunction
86
+ end
87
+
88
+ attribute? :wait_for_selector do
89
+ attributes_from Attributes::WaitForSelector
90
+ end
91
+
92
+ attribute? :wait_for_timeout, Types::Coercible::Integer.optional
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppeteerEntity
4
+ module Attributes
5
+ class ScriptTag < Dry::Struct
6
+ transform_keys(&:to_sym)
7
+
8
+ # URL of the script to be added.
9
+ attribute? :url, Types::URL.optional
10
+ # Path to a JavaScript file to be injected into the frame.
11
+ attribute? :path, Types::String.optional
12
+ # JavaScript to be injected into the frame.
13
+ attribute? :content, Types::String.optional
14
+ # Sets the type of the script. Use module in order to load an ES2015 module.
15
+ attribute? :type, Types::Coercible::String.optional
16
+ # Sets the id of the script.
17
+ attribute? :id, Types::Coercible::String.optional
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppeteerEntity
4
+ module Attributes
5
+ class StyleTag < Dry::Struct
6
+ transform_keys(&:to_sym)
7
+
8
+ # the URL of the CSS file to be added.
9
+ attribute? :url, Types::URL.optional
10
+ # The path to a CSS file to be injected into the frame.
11
+ attribute? :path, Types::String.optional
12
+ # Raw CSS content to be injected into the frame.
13
+ attribute? :content, Types::String.optional
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppeteerEntity
4
+ module Attributes
5
+ class Viewport < Dry::Struct
6
+ transform_keys(&:to_sym)
7
+
8
+ # The page width in CSS pixels.
9
+ attribute :width, Types::Coercible::Integer
10
+ # The page height in CSS pixels.
11
+ attribute :height, Types::Coercible::Integer
12
+ # Specify device scale factor. See {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio devicePixelRatio} for more info.
13
+ attribute? :device_scale_factor, Types::Coercible::Float.optional
14
+ # Whether the meta viewport tag is taken into account.
15
+ attribute? :is_mobile, Types::Bool.optional
16
+ # Specifies if the viewport is in landscape mode.
17
+ attribute? :is_landscape, Types::Bool.optional
18
+ # Specify if the viewport supports touch events.
19
+ attribute? :has_touch, Types::Bool.optional
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppeteerEntity
4
+ module Attributes
5
+ class WaitForEvent < Dry::Struct
6
+ transform_keys(&:to_sym)
7
+
8
+ attribute :event, Types::Coercible::String
9
+ attribute? :timeout, Types::Coercible::Integer
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppeteerEntity
4
+ module Attributes
5
+ class WaitForFunction < Dry::Struct
6
+ transform_keys(&:to_sym)
7
+
8
+ # The function, or statement, to be evaluated in browser context
9
+ attribute :fn, Types::Coercible::String
10
+ # An interval at which the pageFunction is executed, defaults to raf. If polling is a number, then it is treated as an interval in milliseconds at which the function would be executed. If polling is a string, then it can be one of the following values: "raf" or "mutation"
11
+ attribute? :polling, Types::Coercible::String.optional
12
+ # Maximum time to wait for in milliseconds. Defaults to 30000 (30 seconds). Pass 0 to disable timeout.
13
+ attribute? :timeout, Types::Coercible::Integer.optional
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppeteerEntity
4
+ module Attributes
5
+ class WaitForSelector < Dry::Struct
6
+ transform_keys(&:to_sym)
7
+
8
+ attribute :selector, Types::Coercible::String
9
+ attribute? :hidden, Types::Bool.optional
10
+ attribute? :timeout, Types::Coercible::Integer.optional
11
+ attribute? :visible, Types::Bool.optional
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/struct"
4
+ require "http"
5
+ require "active_support/core_ext/hash"
6
+
7
+ module PuppeteerEntity
8
+ class Base < Dry::Struct
9
+ attribute? :browserless_url, Types::URL.default { ENV["BROWSERLESS_URL"] }
10
+ attribute? :retry_limit, Types::Integer.default { 3 }
11
+
12
+ attributes_from Attributes::Query
13
+
14
+ def response
15
+ url = url_with_query_params
16
+ json = as_body_json
17
+ retries = 0
18
+ begin
19
+ HTTP.headers(response_headers)
20
+ .post(url, json:)
21
+ .tap do |res|
22
+ raise RequestError.new(res.body.to_s) unless res.status.success?
23
+ rescue RequestError => e
24
+ retries = retries + 1
25
+ retries > retry_limit ? raise(e) : retry
26
+ end
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def response_headers
33
+ {
34
+ content_type: "application/json"
35
+ }
36
+ end
37
+
38
+ def browserless_uri
39
+ raise NotImplementedError
40
+ end
41
+
42
+ def url_with_query_params
43
+ browserless_uri.tap do |uri|
44
+ uri.query = URI.encode_www_form(as_query_params)
45
+ end.to_s
46
+ end
47
+
48
+ def as_query_params
49
+ to_h
50
+ .slice(*Attributes::Query.attribute_names)
51
+ .reject { |_, v| v.blank? }
52
+ .deep_stringify_keys
53
+ .deep_transform_keys { |k| k.camelcase(:lower) }
54
+ end
55
+
56
+ def as_body_json
57
+ raise NotImplementedError
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppeteerEntity
4
+ class Content < Base
5
+ attributes_from Attributes::Content
6
+
7
+ transform_keys(&:to_sym)
8
+
9
+ private
10
+
11
+ def as_body_json
12
+ to_h
13
+ .slice(*Attributes::Content.attribute_names)
14
+ .reject { |_, v| v.blank? }
15
+ .deep_stringify_keys
16
+ .deep_transform_keys do |k|
17
+ case k
18
+ when "set_extra_http_headers" then "setExtraHTTPHeaders"
19
+ else k.camelcase(:lower)
20
+ end
21
+ end
22
+ end
23
+
24
+ def browserless_uri
25
+ URI.parse(browserless_url).tap do |uri|
26
+ uri.path = "/content"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppeteerEntity
4
+ class Pdf < Base
5
+ attributes_from Attributes::Pdf
6
+
7
+ transform_keys(&:to_sym)
8
+
9
+ private
10
+
11
+ def response_headers
12
+ super.merge(
13
+ accept: "application/pdf"
14
+ )
15
+ end
16
+
17
+ def as_body_json
18
+ to_h
19
+ .slice(*Attributes::Pdf.attribute_names)
20
+ .reject { |_, v| v.blank? }
21
+ .deep_stringify_keys
22
+ .deep_transform_keys do |k|
23
+ case k
24
+ when "set_extra_http_headers" then "setExtraHTTPHeaders"
25
+ when "prefer_css_page_size" then "preferCSSPageSize"
26
+ else k.camelcase(:lower)
27
+ end
28
+ end
29
+ end
30
+
31
+ def browserless_uri
32
+ URI.parse(browserless_url).tap do |uri|
33
+ uri.path = "/pdf"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppeteerEntity
4
+ class Screenshot < Base
5
+ attributes_from Attributes::Screenshot
6
+
7
+ MIME_TYPES = {
8
+ "png" => "image/png",
9
+ "jpeg" => "image/jpeg",
10
+ "webp" => "image/webp"
11
+ }.freeze
12
+
13
+ transform_keys(&:to_sym)
14
+
15
+ private
16
+
17
+ def response_headers
18
+ type = to_h.dig(:options, :type) || "png"
19
+ super.merge(
20
+ accept: MIME_TYPES[type.downcase]
21
+ )
22
+ end
23
+
24
+ def as_body_json
25
+ to_h
26
+ .slice(*Attributes::Screenshot.attribute_names)
27
+ .reject { |_, v| v.blank? }
28
+ .deep_stringify_keys
29
+ .deep_transform_keys do |k|
30
+ case k
31
+ when "set_extra_http_headers" then "setExtraHTTPHeaders"
32
+ else k.camelcase(:lower)
33
+ end
34
+ end
35
+ end
36
+
37
+ def browserless_uri
38
+ URI.parse(browserless_url).tap do |uri|
39
+ uri.path = "/screenshot"
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/types"
4
+
5
+ module PuppeteerEntity
6
+ module Types
7
+ include Dry.Types
8
+
9
+ URL = Strict::String.constrained(format: URI::DEFAULT_PARSER.make_regexp)
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppeteerEntity
4
+ VERSION = "0.1.1"
5
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zeitwerk"
4
+ loader = Zeitwerk::Loader.for_gem
5
+ loader.setup
6
+
7
+ module PuppeteerEntity
8
+ class Error < StandardError; end
9
+
10
+ class RequestError < Error; end
11
+
12
+ class IdLengthError < Error; end
13
+ end
metadata ADDED
@@ -0,0 +1,187 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: puppeteer_entity
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Tomas Celizna
8
+ - Asger Behncke Jacobsen
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2024-10-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: dry-struct
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: http
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: zeitwerk
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: activesupport
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: minitest
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '5.0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '5.0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: faker
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: pdf-reader
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: rubocop-rails-omakase
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ description: PuppeteerEntity provides a simple and intuitive interface for interacting
127
+ with Puppeteer, a headless Chrome browser.
128
+ email:
129
+ - mail@tomascelizna.com
130
+ - a@asgerbehnckejacobsen.dk
131
+ executables: []
132
+ extensions: []
133
+ extra_rdoc_files: []
134
+ files:
135
+ - ".rubocop.yml"
136
+ - ".ruby-version"
137
+ - CHANGELOG.md
138
+ - LICENSE
139
+ - README.md
140
+ - Rakefile
141
+ - docker-compose.yml
142
+ - lib/puppeteer_entity.rb
143
+ - lib/puppeteer_entity/attributes/authenticate.rb
144
+ - lib/puppeteer_entity/attributes/content.rb
145
+ - lib/puppeteer_entity/attributes/cookie.rb
146
+ - lib/puppeteer_entity/attributes/goto_options.rb
147
+ - lib/puppeteer_entity/attributes/pdf.rb
148
+ - lib/puppeteer_entity/attributes/query.rb
149
+ - lib/puppeteer_entity/attributes/screenshot.rb
150
+ - lib/puppeteer_entity/attributes/script_tag.rb
151
+ - lib/puppeteer_entity/attributes/style_tag.rb
152
+ - lib/puppeteer_entity/attributes/viewport.rb
153
+ - lib/puppeteer_entity/attributes/wait_for_event.rb
154
+ - lib/puppeteer_entity/attributes/wait_for_function.rb
155
+ - lib/puppeteer_entity/attributes/wait_for_selector.rb
156
+ - lib/puppeteer_entity/base.rb
157
+ - lib/puppeteer_entity/content.rb
158
+ - lib/puppeteer_entity/pdf.rb
159
+ - lib/puppeteer_entity/screenshot.rb
160
+ - lib/puppeteer_entity/types.rb
161
+ - lib/puppeteer_entity/version.rb
162
+ homepage: https://github.com/tomasc/puppeteer_entity
163
+ licenses: []
164
+ metadata:
165
+ homepage_uri: https://github.com/tomasc/puppeteer_entity
166
+ source_code_uri: https://github.com/tomasc/puppeteer_entity
167
+ changelog_uri: https://github.com/tomasc/puppeteer_entity/blob/main/CHANGELOG.md
168
+ post_install_message:
169
+ rdoc_options: []
170
+ require_paths:
171
+ - lib
172
+ required_ruby_version: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: 3.1.0
177
+ required_rubygems_version: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ requirements: []
183
+ rubygems_version: 3.5.16
184
+ signing_key:
185
+ specification_version: 4
186
+ summary: A Ruby gem for interacting with Puppeteer
187
+ test_files: []