sass-embedded 0.8.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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