sass-embedded 0.8.2 → 0.9.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: 36a036051c7abed208ad60c033464cc8ebba1c7b16e0aaf4d32e7e88e7480dbe
4
- data.tar.gz: d90c2b2f3c0a66c1de05cdb30fa66bb0b42b8d29ecaa84be53a36dc42fc886b2
3
+ metadata.gz: c5bcaee2299b4f0b9d4d1d23b25773fdad87d1a294a4a34eeec0028d1f89a25c
4
+ data.tar.gz: 75ce2615300b0d59e73f715ae43007baf09a9818ea56d80b8be4d54b1f561c69
5
5
  SHA512:
6
- metadata.gz: 9ea57f9e2d75135b81cc2dcdc7dd37cf1db7b185512f90b2e40a5c03d05994409d85023b61feccdc0a346443863fac2c4439c439d975adfd2404319747d1f8fc
7
- data.tar.gz: 90d91290a68af79837a17dd4cd73645524e4d9e377b5e30f232df2c2a2dde1dba4f4c2a7520b66fda1e225f409804abc3b2de0df8c8998be094827982114472c
6
+ metadata.gz: 93cf20b2df8d4c363797df90e459f52c6dedf5e540cb5b6eb1cd110dded76477adf82f6c0bc0652f1b5201f535265053f7838654952a4bcc7f338bcc69c9464a
7
+ data.tar.gz: c4190c8fc8e60a37fa834dfdac6fe1842e66d39ac8d0ad9bc1c15ea5c18c16766f19e194be746fc963a5604c740f438c6371aa59c10dd59d452a526fea6cbf0d
data/ext/sass/extconf.rb CHANGED
@@ -5,7 +5,8 @@ require 'fileutils'
5
5
  require 'json'
6
6
  require 'mkmf'
7
7
  require 'open-uri'
8
- require_relative '../../lib/sass/embedded/compiler'
8
+ require_relative '../../lib/sass/embedded/compiler/path'
9
+ require_relative '../../lib/sass/embedded/compiler/requirements'
9
10
  require_relative '../../lib/sass/embedded/platform'
10
11
 
11
12
  module Sass
@@ -210,7 +211,7 @@ module Sass
210
211
  def default_sass_embedded_protocol
211
212
  repo = 'sass/embedded-protocol'
212
213
 
213
- tag_name = IO.popen([Compiler::DART_SASS_EMBEDDED, '--version']) do |file|
214
+ tag_name = IO.popen([Compiler::PATH, '--version']) do |file|
214
215
  JSON.parse(file.read)['protocolVersion']
215
216
  end
216
217
 
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'compiler'
4
+
5
+ module Sass
6
+ class Embedded
7
+ # The {Channel} for {Compiler} calls. Each instance creates its own
8
+ # {Compiler}. A new {Compiler} is automatically created when the existing
9
+ # {Compiler} runs out of unique request id.
10
+ class Channel
11
+ def initialize
12
+ @mutex = Mutex.new
13
+ @compiler = Compiler.new
14
+ end
15
+
16
+ def close
17
+ @mutex.synchronize do
18
+ @compiler.close
19
+ end
20
+ end
21
+
22
+ def closed?
23
+ @mutex.synchronize do
24
+ @compiler.closed?
25
+ end
26
+ end
27
+
28
+ def subscribe(observer)
29
+ @mutex.synchronize do
30
+ begin
31
+ id = @compiler.add_observer(observer)
32
+ rescue IOError
33
+ @compiler = Compiler.new
34
+ id = @compiler.add_observer(observer)
35
+ end
36
+ Subscription.new @compiler, observer, id
37
+ end
38
+ end
39
+
40
+ # The {Subscription} between {Compiler} and {Observer}.
41
+ class Subscription
42
+ attr_reader :id
43
+
44
+ def initialize(compiler, observer, id)
45
+ @compiler = compiler
46
+ @observer = observer
47
+ @id = id
48
+ end
49
+
50
+ def unsubscribe
51
+ @compiler.delete_observer(@observer)
52
+ end
53
+
54
+ def send_message(*args)
55
+ @compiler.send_message(*args)
56
+ end
57
+ end
58
+
59
+ private_constant :Subscription
60
+ end
61
+ end
62
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../embedded_protocol'
3
4
  require_relative 'observer'
4
- require_relative 'protocol'
5
5
  require_relative 'util'
6
6
 
7
7
  module Sass
@@ -10,7 +10,7 @@ module Sass
10
10
  class CompileContext
11
11
  include Observer
12
12
 
13
- def initialize(transport, id,
13
+ def initialize(channel,
14
14
  data:,
15
15
  file:,
16
16
  indented_syntax:,
@@ -22,7 +22,6 @@ module Sass
22
22
  importer:)
23
23
  raise ArgumentError, 'either data or file must be set' if file.nil? && data.nil?
24
24
 
25
- @id = id
26
25
  @data = data
27
26
  @file = file
28
27
  @indented_syntax = indented_syntax
@@ -37,7 +36,7 @@ module Sass
37
36
  @importer = importer
38
37
  @import_responses = {}
39
38
 
40
- super(transport)
39
+ super(channel)
41
40
 
42
41
  send_message compile_request
43
42
  end
@@ -47,23 +46,23 @@ module Sass
47
46
 
48
47
  case message
49
48
  when EmbeddedProtocol::OutboundMessage::CompileResponse
50
- return unless message.id == @id
49
+ return unless message.id == id
51
50
 
52
51
  Thread.new do
53
52
  super(nil, message)
54
53
  end
55
54
  when EmbeddedProtocol::OutboundMessage::LogEvent
56
- return unless message.compilation_id == @id && $stderr.tty?
55
+ return unless message.compilation_id == id && $stderr.tty?
57
56
 
58
57
  warn message.formatted
59
58
  when EmbeddedProtocol::OutboundMessage::CanonicalizeRequest
60
- return unless message.compilation_id == @id
59
+ return unless message.compilation_id == id
61
60
 
62
61
  Thread.new do
63
62
  send_message canonicalize_response message
64
63
  end
65
64
  when EmbeddedProtocol::OutboundMessage::ImportRequest
66
- return unless message.compilation_id == @id
65
+ return unless message.compilation_id == id
67
66
 
68
67
  Thread.new do
69
68
  send_message import_response message
@@ -71,7 +70,7 @@ module Sass
71
70
  when EmbeddedProtocol::OutboundMessage::FileImportRequest
72
71
  raise NotImplementedError, 'FileImportRequest is not implemented'
73
72
  when EmbeddedProtocol::OutboundMessage::FunctionCallRequest
74
- return unless message.compilation_id == @id
73
+ return unless message.compilation_id == id
75
74
 
76
75
  Thread.new do
77
76
  send_message function_call_response message
@@ -87,7 +86,7 @@ module Sass
87
86
 
88
87
  def compile_request
89
88
  EmbeddedProtocol::InboundMessage::CompileRequest.new(
90
- id: @id,
89
+ id: id,
91
90
  string: string,
92
91
  path: path,
93
92
  style: style,
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../platform'
4
+
5
+ module Sass
6
+ class Embedded
7
+ class Compiler
8
+ PATH = File.absolute_path(
9
+ "../../../../ext/sass/sass_embedded/dart-sass-embedded#{Platform::OS == 'windows' ? '.bat' : ''}", __dir__
10
+ )
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sass
4
+ class Embedded
5
+ class Compiler
6
+ REQUIREMENTS = '~> 1.0.0-beta.11'
7
+ end
8
+ end
9
+ end
@@ -1,17 +1,156 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'platform'
3
+ require 'observer'
4
+ require 'open3'
5
+ require_relative '../embedded_protocol'
6
+ require_relative 'compiler/path'
7
+ require_relative 'error'
4
8
 
5
9
  module Sass
6
10
  class Embedded
7
- module Compiler
8
- DART_SASS_EMBEDDED = File.absolute_path(
9
- "../../../ext/sass/sass_embedded/dart-sass-embedded#{Platform::OS == 'windows' ? '.bat' : ''}", __dir__
10
- )
11
+ # The {::Observable} {Compiler} for low level communication with
12
+ # `dart-sass-embedded` using protocol buffers via stdio. Received messages
13
+ # can be observed by an {Observer}.
14
+ class Compiler
15
+ include Observable
11
16
 
12
- PROTOCOL_ERROR_ID = 4_294_967_295
17
+ ONEOF_MESSAGE = EmbeddedProtocol::InboundMessage
18
+ .descriptor
19
+ .lookup_oneof('message')
20
+ .collect do |field_descriptor|
21
+ [field_descriptor.subtype, field_descriptor.name]
22
+ end.to_h
13
23
 
14
- REQUIREMENTS = '~> 1.0.0-beta.11'
24
+ private_constant :ONEOF_MESSAGE
25
+
26
+ def initialize
27
+ @observerable_mutex = Mutex.new
28
+ @id = 0
29
+ @stdin_mutex = Mutex.new
30
+ @stdin, @stdout, @stderr, @wait_thread = Open3.popen3(PATH)
31
+
32
+ [@stdin, @stdout].each(&:binmode)
33
+
34
+ poll do
35
+ warn(@stderr.readline, uplevel: 1)
36
+ end
37
+ poll do
38
+ receive_proto read
39
+ end
40
+ end
41
+
42
+ def add_observer(*args)
43
+ @observerable_mutex.synchronize do
44
+ raise IOError, 'half-closed compiler' if half_closed?
45
+
46
+ super(*args)
47
+
48
+ id = @id
49
+ @id = @id.next
50
+ id
51
+ end
52
+ end
53
+
54
+ def delete_observer(*args)
55
+ @observerable_mutex.synchronize do
56
+ super(*args)
57
+
58
+ close if half_closed? && count_observers.zero?
59
+ end
60
+ end
61
+
62
+ def send_message(message)
63
+ write EmbeddedProtocol::InboundMessage.new(
64
+ ONEOF_MESSAGE[message.class.descriptor] => message
65
+ ).to_proto
66
+ end
67
+
68
+ def close
69
+ delete_observers
70
+
71
+ @stdin_mutex.synchronize do
72
+ @stdin.close unless @stdin.closed?
73
+ @stdout.close unless @stdout.closed?
74
+ @stderr.close unless @stderr.closed?
75
+ end
76
+
77
+ @wait_thread.value
78
+ end
79
+
80
+ def closed?
81
+ @stdin_mutex.synchronize do
82
+ @stdin.closed?
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ def half_closed?
89
+ @id == EmbeddedProtocol::PROTOCOL_ERROR_ID
90
+ end
91
+
92
+ def poll
93
+ Thread.new do
94
+ loop do
95
+ yield
96
+ rescue StandardError => e
97
+ notify_observers(e, nil)
98
+ close
99
+ break
100
+ end
101
+ end
102
+ end
103
+
104
+ def notify_observers(*args)
105
+ @observerable_mutex.synchronize do
106
+ changed
107
+ super(*args)
108
+ end
109
+ end
110
+
111
+ def receive_proto(proto)
112
+ payload = EmbeddedProtocol::OutboundMessage.decode(proto)
113
+ message = payload[payload.message.to_s]
114
+ case message
115
+ when EmbeddedProtocol::ProtocolError
116
+ raise ProtocolError, message.message
117
+ else
118
+ notify_observers(nil, message)
119
+ end
120
+ end
121
+
122
+ def read
123
+ length = read_varint(@stdout)
124
+ @stdout.read(length)
125
+ end
126
+
127
+ def write(payload)
128
+ @stdin_mutex.synchronize do
129
+ write_varint(@stdin, payload.length)
130
+ @stdin.write payload
131
+ end
132
+ end
133
+
134
+ def read_varint(readable)
135
+ value = bits = 0
136
+ loop do
137
+ byte = readable.readbyte
138
+ value |= (byte & 0x7f) << bits
139
+ bits += 7
140
+ break if byte < 0x80
141
+ end
142
+ value
143
+ end
144
+
145
+ def write_varint(writeable, value)
146
+ bytes = []
147
+ until value < 0x80
148
+ bytes << (0x80 | (value & 0x7f))
149
+ value >>= 7
150
+ end
151
+ bytes << value
152
+ writeable.write bytes.pack('C*')
153
+ end
15
154
  end
16
155
  end
17
156
  end
@@ -2,15 +2,15 @@
2
2
 
3
3
  module Sass
4
4
  class Embedded
5
- # The {Observer} module for receiving messages from {Transport}.
5
+ # The {Observer} module for communicating with {Compiler}.
6
6
  module Observer
7
- def initialize(transport)
8
- @transport = transport
7
+ def initialize(channel)
9
8
  @mutex = Mutex.new
10
9
  @condition_variable = ConditionVariable.new
11
10
  @error = nil
12
11
  @message = nil
13
- @transport.add_observer self
12
+
13
+ @subscription = channel.subscribe(self)
14
14
  end
15
15
 
16
16
  def receive_message
@@ -24,7 +24,7 @@ module Sass
24
24
  end
25
25
 
26
26
  def update(error, message)
27
- @transport.delete_observer self
27
+ @subscription.unsubscribe
28
28
  @mutex.synchronize do
29
29
  @error = error
30
30
  @message = message
@@ -34,8 +34,12 @@ module Sass
34
34
 
35
35
  private
36
36
 
37
- def send_message(message)
38
- @transport.send_message(message)
37
+ def id
38
+ @subscription.id
39
+ end
40
+
41
+ def send_message(*args)
42
+ @subscription.send_message(*args)
39
43
  end
40
44
  end
41
45
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sass
4
4
  class Embedded
5
- VERSION = '0.8.2'
5
+ VERSION = '0.9.0'
6
6
  end
7
7
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../embedded_protocol'
3
4
  require_relative 'observer'
4
- require_relative 'protocol'
5
5
 
6
6
  module Sass
7
7
  class Embedded
@@ -9,12 +9,10 @@ module Sass
9
9
  class VersionContext
10
10
  include Observer
11
11
 
12
- def initialize(transport, id)
13
- @id = id
12
+ def initialize(channel)
13
+ super(channel)
14
14
 
15
- super(transport)
16
-
17
- send_message EmbeddedProtocol::InboundMessage::VersionRequest.new(id: @id)
15
+ send_message EmbeddedProtocol::InboundMessage::VersionRequest.new(id: id)
18
16
  end
19
17
 
20
18
  def update(error, message)
@@ -22,7 +20,7 @@ module Sass
22
20
 
23
21
  case message
24
22
  when EmbeddedProtocol::OutboundMessage::VersionResponse
25
- return unless message.id == @id
23
+ return unless message.id == id
26
24
 
27
25
  Thread.new do
28
26
  super(nil, message)
data/lib/sass/embedded.rb CHANGED
@@ -2,17 +2,17 @@
2
2
 
3
3
  require 'base64'
4
4
  require 'json'
5
- require_relative 'embedded/compile'
5
+ require_relative 'embedded/channel'
6
+ require_relative 'embedded/compile_context'
6
7
  require_relative 'embedded/error'
7
- require_relative 'embedded/info'
8
8
  require_relative 'embedded/result'
9
- require_relative 'embedded/transport'
10
9
  require_relative 'embedded/util'
11
10
  require_relative 'embedded/version'
11
+ require_relative 'embedded/version_context'
12
12
 
13
13
  module Sass
14
14
  # The {Embedded} host for using dart-sass-embedded. Each instance creates
15
- # its own {Transport}.
15
+ # its own {Channel}.
16
16
  #
17
17
  # @example
18
18
  # embedded = Sass::Embedded.new
@@ -29,16 +29,14 @@ module Sass
29
29
  end
30
30
 
31
31
  def initialize
32
- @transport = Transport.new
33
- @id_semaphore = Mutex.new
34
- @id = 0
32
+ @channel = Channel.new
35
33
  end
36
34
 
37
35
  # The {Embedded#info} method.
38
36
  #
39
37
  # @raise [ProtocolError]
40
38
  def info
41
- @info ||= VersionContext.new(@transport, next_id).receive_message
39
+ @info ||= VersionContext.new(@channel).receive_message
42
40
  end
43
41
 
44
42
  # The {Embedded#render} method.
@@ -70,7 +68,7 @@ module Sass
70
68
  indent_width = parse_indent_width(indent_width)
71
69
  linefeed = parse_linefeed(linefeed)
72
70
 
73
- message = CompileContext.new(@transport, next_id,
71
+ message = CompileContext.new(@channel,
74
72
  data: data,
75
73
  file: file,
76
74
  indented_syntax: indented_syntax,
@@ -124,11 +122,11 @@ module Sass
124
122
  end
125
123
 
126
124
  def close
127
- @transport.close
125
+ @channel.close
128
126
  end
129
127
 
130
128
  def closed?
131
- @transport.closed?
129
+ @channel.closed?
132
130
  end
133
131
 
134
132
  private
@@ -254,13 +252,5 @@ module Sass
254
252
  raise ArgumentError, 'linefeed must be one of :lf, :lfcr, :cr, :crlf'
255
253
  end
256
254
  end
257
-
258
- def next_id
259
- @id_semaphore.synchronize do
260
- @id += 1
261
- @id = 0 if @id == Compiler::PROTOCOL_ERROR_ID
262
- @id
263
- end
264
- end
265
255
  end
266
256
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../ext/sass/embedded_sass_pb'
4
+
5
+ module Sass
6
+ module EmbeddedProtocol
7
+ PROTOCOL_ERROR_ID = 4_294_967_295
8
+ end
9
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sass-embedded
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - なつき
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-26 00:00:00.000000000 Z
11
+ date: 2021-09-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: google-protobuf
@@ -139,24 +139,26 @@ files:
139
139
  - lib/sass-embedded.rb
140
140
  - lib/sass.rb
141
141
  - lib/sass/embedded.rb
142
- - lib/sass/embedded/compile.rb
142
+ - lib/sass/embedded/channel.rb
143
+ - lib/sass/embedded/compile_context.rb
143
144
  - lib/sass/embedded/compiler.rb
145
+ - lib/sass/embedded/compiler/path.rb
146
+ - lib/sass/embedded/compiler/requirements.rb
144
147
  - lib/sass/embedded/error.rb
145
- - lib/sass/embedded/info.rb
146
148
  - lib/sass/embedded/observer.rb
147
149
  - lib/sass/embedded/platform.rb
148
- - lib/sass/embedded/protocol.rb
149
150
  - lib/sass/embedded/result.rb
150
151
  - lib/sass/embedded/struct.rb
151
- - lib/sass/embedded/transport.rb
152
152
  - lib/sass/embedded/util.rb
153
153
  - lib/sass/embedded/version.rb
154
+ - lib/sass/embedded/version_context.rb
155
+ - lib/sass/embedded_protocol.rb
154
156
  homepage: https://github.com/ntkme/sass-embedded-host-ruby
155
157
  licenses:
156
158
  - MIT
157
159
  metadata:
158
- documentation_uri: https://www.rubydoc.info/gems/sass-embedded/0.8.2
159
- source_code_uri: https://github.com/ntkme/sass-embedded-host-ruby/tree/v0.8.2
160
+ documentation_uri: https://www.rubydoc.info/gems/sass-embedded/0.9.0
161
+ source_code_uri: https://github.com/ntkme/sass-embedded-host-ruby/tree/v0.9.0
160
162
  funding_uri: https://github.com/sponsors/ntkme
161
163
  post_install_message:
162
164
  rdoc_options: []
@@ -1,3 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../../../ext/sass/embedded_sass_pb'
@@ -1,131 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'open3'
4
- require 'observer'
5
- require_relative 'compiler'
6
- require_relative 'error'
7
- require_relative 'protocol'
8
-
9
- module Sass
10
- class Embedded
11
- # The {::Observable} {Transport} for low level communication with
12
- # `dart-sass-embedded` using protocol buffers via stdio. Received messages
13
- # can be observed by an {Observer}.
14
- class Transport
15
- include Observable
16
-
17
- ONEOF_MESSAGE = EmbeddedProtocol::InboundMessage
18
- .descriptor
19
- .lookup_oneof('message')
20
- .collect do |field_descriptor|
21
- [field_descriptor.subtype, field_descriptor.name]
22
- end.to_h
23
-
24
- private_constant :ONEOF_MESSAGE
25
-
26
- def initialize
27
- @observerable_mutex = Mutex.new
28
- @stdin_mutex = Mutex.new
29
- @stdin, @stdout, @stderr, @wait_thread = Open3.popen3(Compiler::DART_SASS_EMBEDDED)
30
-
31
- [@stdin, @stdout].each(&:binmode)
32
-
33
- poll do
34
- warn(@stderr.readline, uplevel: 1)
35
- end
36
- poll do
37
- receive_proto read
38
- end
39
- end
40
-
41
- def add_observer(*args)
42
- @observerable_mutex.synchronize do
43
- super(*args)
44
- end
45
- end
46
-
47
- def send_message(message)
48
- write EmbeddedProtocol::InboundMessage.new(
49
- ONEOF_MESSAGE[message.class.descriptor] => message
50
- ).to_proto
51
- end
52
-
53
- def close
54
- delete_observers
55
- @stdin.close unless @stdin.closed?
56
- @stdout.close unless @stdout.closed?
57
- @stderr.close unless @stderr.closed?
58
- nil
59
- end
60
-
61
- def closed?
62
- @stdin.closed?
63
- end
64
-
65
- private
66
-
67
- def poll
68
- Thread.new do
69
- loop do
70
- yield
71
- rescue StandardError => e
72
- notify_observers(e, nil)
73
- close
74
- break
75
- end
76
- end
77
- end
78
-
79
- def notify_observers(*args)
80
- @observerable_mutex.synchronize do
81
- changed
82
- super(*args)
83
- end
84
- end
85
-
86
- def receive_proto(proto)
87
- payload = EmbeddedProtocol::OutboundMessage.decode(proto)
88
- message = payload[payload.message.to_s]
89
- case message
90
- when EmbeddedProtocol::ProtocolError
91
- raise ProtocolError, message.message
92
- else
93
- notify_observers(nil, message)
94
- end
95
- end
96
-
97
- def read
98
- length = read_varint(@stdout)
99
- @stdout.read(length)
100
- end
101
-
102
- def write(payload)
103
- @stdin_mutex.synchronize do
104
- write_varint(@stdin, payload.length)
105
- @stdin.write payload
106
- end
107
- end
108
-
109
- def read_varint(readable)
110
- value = bits = 0
111
- loop do
112
- byte = readable.readbyte
113
- value |= (byte & 0x7f) << bits
114
- bits += 7
115
- break if byte < 0x80
116
- end
117
- value
118
- end
119
-
120
- def write_varint(writeable, value)
121
- bytes = []
122
- until value < 0x80
123
- bytes << (0x80 | (value & 0x7f))
124
- value >>= 7
125
- end
126
- bytes << value
127
- writeable.write bytes.pack('C*')
128
- end
129
- end
130
- end
131
- end