s41c 0.0.2 → 0.0.4

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.
data/README.md CHANGED
@@ -16,21 +16,29 @@ TCP-socket-сервер и клиент для платформы "1С:Пред
16
16
 
17
17
  ### Пример использования
18
18
 
19
- Запустить сервер для 1C:Предприятия 7.7 (сетевая версия) на localhost:1421
19
+ #### Сервер
20
+
21
+ Запустить сервер для 1C:Предприятия 7.7 (сетевая версия) на localhost:1421
22
+ и использовать базу "C:\Base\"
20
23
 
21
24
  require 's41c'
22
25
 
23
26
  server = S41C::Server.new
27
+ server.db('C:\Base\\')
24
28
  server.start
25
29
 
26
30
  Или чуть сложнее: запустить сервер для 1С:Предприятия (локальная версия) на
27
- 127.0.0.1:2000, писать лог в c:\server.log и записать время остановки
28
- сервера в файл
31
+ 127.0.0.1:2000, использовать базу "C:\Base\", при подключении требовать логин
32
+ "username" и пароль "password", писать лог в c:\server.log и записать время
33
+ остановки сервера в файл
29
34
 
30
35
  require 's41c'
31
36
 
32
37
  server = S41C::Server.new('127.0.0.1', 2000, 'c:\server.log')
33
- server.ole_object = 'V77L.Application'
38
+ server.ole_name = 'V77L.Application'
39
+ server.db('C:\Base\\')
40
+
41
+ server.login('username', 'password')
34
42
 
35
43
  server.at_exit do
36
44
  File.open('c:\server_stoped_at.txt', 'w') { |f| f.puts Time.now }
@@ -38,38 +46,56 @@ TCP-socket-сервер и клиент для платформы "1С:Пред
38
46
 
39
47
  server.start
40
48
 
41
- После запуска с сервером можно общаться по telnet. Комманды, содержащие не-ASCII
42
- символы, должны быть utf8-строками переведенными в бинарный формат. В руби это
43
- можно сделать с помощью метода [String#force_encoding](http://ruby-doc.org/core-1.9.3/String.html#method-i-force_encoding):
49
+ #### Клиент
50
+
51
+ При подключении можно указать хост и порт сервера (если не указаны, будут
52
+ использоваться localhost:1421), а также данные для авторизации. После
53
+ подключения на сервер передаётся руби-код в виде строки, который и выполняет все,
54
+ что нам нужно
55
+
56
+ require 's41c'
57
+
58
+ client = S41C::Client.new('127.0.0.1', 2000)
59
+ client.login('username', 'password')
60
+
61
+ response = client.eval %{
44
62
 
45
- "utf8-строка".force_encoding("BINARY")
46
- => "utf8-\xD1\x81\xD1\x82\xD1\x80\xD0\xBE\xD0\xBA\xD0\xB0"
63
+ spr = @ole.CreateObject('Справочник.Товары')
47
64
 
48
- Отвечает сервер в том же формате, т.е. на стороне клиента ответ нужно
49
- преобразовать в utf-строку:
65
+ if spr.invoke('НайтиПоНаименованию', 'Название товара') == 1.0
66
+ item = spr.invoke('ТекущийЭлемент')
67
+ item_id = get_1c_id(item)
68
+ expr = 'КакаяНибудьФункция(ЗначениеИзСтрокиВнутр(' + item_id + '))'
69
+ result = @ole.EvalExpr(expr)
70
+ end
50
71
 
51
- "utf8-\xD1\x81\xD1\x82\xD1\x80\xD0\xBE\xD0\xBA\xD0\xB0".force_encoding("UTF-8")
52
- => "utf8-строка"
72
+ to_utf8(result)
53
73
 
54
- Список команд:
74
+ }
55
75
 
56
- * 'connect|параметры_подключения' - подключиться к базе, например:
57
-
58
- 'connect|/d c:\1c\\'
76
+ puts response
77
+ client.disconnect
59
78
 
60
- * 'eval_expr|команда' - выполнить команду
61
-
62
- 'eval_expr|ОсновнойЯзык()'
79
+ Внутри кода доступен объект @ole, который и является инстансом 1С. У него есть
80
+ 3 метода
63
81
 
64
- * 'create|Название.Объекта' - создать объект
82
+ * CreateObject - создает объект агрегатного типа данных 1С:Предприятия и
83
+ возвращает ссылку на него.
65
84
 
66
- 'create|Справочник.Товары'
85
+ * EvalExpr - вычислияет выражение системы 1С:Предприятие и возвращает результат
86
+ вычисления
67
87
 
68
- * 'invoke|НазваниеМетода|параметры|метода' - выполнить процедуру/функцию для объекта
88
+ * ExecuteBatch - выполняет последовательность операторов системы
89
+ 1С:Предприятие и возвращает статус выполнения (true/false)
69
90
 
70
- 'invoke|НайтиПоНаименованию|Шляпа с полями'
91
+ Вызов функций и процедур созданных объектов осуществляется с помощью метода
92
+ 'invoke', которому в качестве первого аргумента передаётся название
93
+ функции/процедуры, а последующих - параметры.
71
94
 
72
- * 'disconnect' - отключиться от 1С
73
- * 'shutdown' - остановить сервер
95
+ Также достпен ряд вспомогательных функций
74
96
 
97
+ * to_bin - переводит utf-строку в бинарный формат
98
+ * to_utf8 - переводит бинарную или строку из 1С в utf-8
99
+ * get_1c_id - обертка над 1С-функцией 'ЗначениеВСтрокуВнутр', возращает
100
+ строковый идентификатор объекта.
75
101
 
data/lib/s41c/client.rb CHANGED
@@ -4,6 +4,8 @@ module S41C
4
4
 
5
5
  class Client
6
6
 
7
+ include S41C::Utils
8
+
7
9
  def initialize(host='localhost', port=1421)
8
10
  require 'net/telnet'
9
11
 
@@ -16,6 +18,8 @@ module S41C
16
18
  def login(username, password = nil)
17
19
  @login = username.nil? || username.empty? ? nil : username
18
20
  @password = password
21
+
22
+ self
19
23
  end # login
20
24
 
21
25
  def errors
@@ -23,25 +27,13 @@ module S41C
23
27
  end # errors
24
28
 
25
29
  def connect(options)
26
- cmd "connect|#{options}"
30
+ cmd "connect\0#{options}"
27
31
  end # connect
28
32
 
29
33
  def ping
30
34
  cmd "ping"
31
35
  end # ping
32
36
 
33
- def eval_expr(expr)
34
- cmd "eval_expr|#{expr}"
35
- end # eval_expr
36
-
37
- def create(obj_name)
38
- cmd "create|#{obj_name}"
39
- end # create
40
-
41
- def invoke(method_name, *args)
42
- cmd "invoke|#{method_name}|#{args.join('|')}"
43
- end # invoke
44
-
45
37
  def disconnect
46
38
  cmd "disconnect"
47
39
  end # disconnect
@@ -50,6 +42,10 @@ module S41C
50
42
  cmd "shutdown"
51
43
  end # shutdown
52
44
 
45
+ def eval(code)
46
+ cmd "eval\0\n#{code}\nend_of_code"
47
+ end
48
+
53
49
  private
54
50
 
55
51
  def conn
@@ -77,21 +73,14 @@ module S41C
77
73
 
78
74
  def cmd(str)
79
75
  return @errors unless conn
80
- parse @client.cmd(S41C::Utils.to_bin(str))
76
+ parse @client.cmd(to_bin(str))
81
77
  end # cmd
82
78
 
83
79
  def parse(response)
84
- resp = S41C::Utils.to_utf8(response)
85
- res = []
86
-
87
- resp.each_line do |line|
88
- res << line.chomp unless line[@prompt]
89
- end # each_line
90
-
91
- res.count > 1 ? res : res.first
80
+ resp = to_utf8(response)
81
+ resp.lines.first.chomp
92
82
  end # parse
93
83
 
94
84
  end # Client
95
85
 
96
86
  end # S41C
97
-
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+
3
+ module S41C
4
+
5
+ class Sandbox
6
+
7
+ include S41C::Utils
8
+
9
+ def initialize(ole, code)
10
+ @ole = ole
11
+ code.untaint
12
+ @code = proc {
13
+ $SAFE = 3
14
+ instance_eval code, __FILE__, __LINE__
15
+ }
16
+ end
17
+
18
+ def eval_code
19
+ begin
20
+ @code.call
21
+ rescue WIN32OLERuntimeError => e
22
+ "Error: #{to_utf8(e.message)}"
23
+ rescue Exception => e
24
+ "Error: #{e.message}"
25
+ rescue => e
26
+ "Error: #{e.message} from #{__FILE__}:#{__LINE__}"
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ end # S41C
data/lib/s41c/server.rb CHANGED
@@ -4,27 +4,45 @@ module S41C
4
4
 
5
5
  class Server
6
6
 
7
+ include S41C::Utils
8
+
7
9
  def initialize(host='localhost', port=1421, log_file=nil)
10
+ require 'socket'
8
11
  require 'win32ole'
9
12
 
10
13
  @host, @port = host, port
11
14
  @logger = log_file ? ::STDOUT.reopen(log_file, 'a') : ::STDOUT
12
- @ole_object = 'V77.Application'
15
+ @ole_name = 'V77.Application'
16
+
13
17
  end # initialize
14
18
 
15
- def set_login(username, password)
19
+ def login(username, password)
16
20
  @login = username
17
21
  @password = password
18
- end # login_info
19
22
 
20
- def ole_object=(name)
21
- @ole_object = name
22
- end # ole_object
23
+ self
24
+ end # login
25
+
26
+ def db(database, user=nil, password=nil)
27
+ @conn_options = "/d #{database}"
28
+ @conn_options << " /n #{user}" if user
29
+ @conn_options << " /p #{password}" if password
30
+
31
+ self
32
+ end # db
33
+
34
+ def ole_name=(name)
35
+ @ole_name = name
36
+
37
+ self
38
+ end # ole_name=
23
39
 
24
40
  def at_exit(&block)
25
41
  ::Kernel::at_exit do
26
42
  yield
27
43
  end
44
+
45
+ self
28
46
  end # at_exit
29
47
 
30
48
  def start
@@ -36,6 +54,8 @@ module S41C
36
54
  }
37
55
  end
38
56
 
57
+ connect_to_1c
58
+
39
59
  server = TCPServer.open(@host, @port)
40
60
 
41
61
  log "*** Server has been started on #{@host}:#{@port}"
@@ -53,7 +73,7 @@ module S41C
53
73
  private
54
74
 
55
75
  def read_response(session)
56
- S41C::Utils.to_utf8(session.gets || '').chomp
76
+ to_utf8(session.gets || '').chomp
57
77
  end # read_response
58
78
 
59
79
  def log(msg)
@@ -61,52 +81,27 @@ module S41C
61
81
  @logger.flush
62
82
  end # log
63
83
 
64
- def connect_to_1c(args)
65
- @conn.ole_free unless @conn.nil?
66
- options = args.shift || ''
84
+ def connect_to_1c
67
85
  begin
68
- @conn = ::WIN32OLE.new('V77.Application')
69
- res = @conn.Initialize(
70
- @conn.RMTrade,
71
- options,
86
+ @ole = ::WIN32OLE.new(@ole_name)
87
+ @conn = @ole.Initialize(
88
+ @ole.RMTrade,
89
+ (@conn_options),
72
90
  ''
73
91
  )
74
- "Connected"
92
+ rescue WIN32OLERuntimeError => e
93
+ @conn = nil
94
+ log "*** Error: #{to_utf8(e.message)}"
75
95
  rescue => e
76
- @conn.ole_free
77
- "Error: #{e.message}"
96
+ log "*** Error: #{e.message} from #{__FILE__}:#{__LINE__}"
78
97
  end
79
98
  end
80
99
 
81
- def create(args)
100
+ def eval_code(code)
82
101
  return "Error: not connected" unless @conn
83
- obj_name = args.shift || ''
84
- begin
85
- @obj = @conn.CreateObject(obj_name)
86
- "Created"
87
- rescue => e
88
- "Error: #{e.message}"
89
- end
90
- end
91
102
 
92
- def eval_expr(args)
93
- return "Error: not connected" unless @conn
94
- expr = args.shift || ''
95
- begin
96
- @conn.invoke("EvalExpr", expr).to_s
97
- rescue => e
98
- "Error: #{e.message}"
99
- end
100
- end
101
-
102
- def invoke(args)
103
- return "Error: working object not found. You must create it before" unless @conn
104
- begin
105
- @obj.invoke(*args).to_s.encode("UTF-8", "IBM866", :invalid => :replace, :replace => "?")
106
- rescue => e
107
- "Error: #{e.message}"
108
- end
109
- end
103
+ S41C::Sandbox.new(@ole, code).eval_code
104
+ end # eval_code
110
105
 
111
106
  def main_loop(server)
112
107
  loop do
@@ -138,20 +133,15 @@ module S41C
138
133
  end
139
134
 
140
135
  loop {
141
- args = S41C::Utils.to_utf8(session.gets || '').chomp.split('|')
136
+ args = read_response(session).split("\0")
142
137
  cmd = args.shift
143
138
  case cmd
144
- when "connect"
145
- session.puts S41C::Utils.to_bin(connect_to_1c(args))
146
- session.puts "+OK"
147
- when "create"
148
- session.puts S41C::Utils.to_bin(create(args))
149
- session.puts "+OK"
150
- when "eval_expr"
151
- session.puts S41C::Utils.to_bin(eval_expr(args))
152
- session.puts "+OK"
153
- when "invoke"
154
- session.puts S41C::Utils.to_bin(invoke(args))
139
+ when "eval"
140
+ code = ""
141
+ while !(part = session.gets)['end_of_code']
142
+ code << to_utf8(part)
143
+ end
144
+ session.puts to_bin(eval_code(code))
155
145
  session.puts "+OK"
156
146
  when "ping"
157
147
  session.puts "pong"
@@ -165,7 +155,7 @@ module S41C
165
155
  session.close
166
156
  exit
167
157
  else
168
- session.puts "Bad command"
158
+ session.puts "Bad command: `#{cmd}`"
169
159
  session.puts "+OK"
170
160
  end
171
161
  }
data/lib/s41c/utils.rb CHANGED
@@ -1,15 +1,30 @@
1
+ # encoding: utf-8
2
+
1
3
  module S41C
2
4
 
3
5
  module Utils
4
6
 
5
- def Utils.to_bin(str)
6
- str.force_encoding("BINARY")
7
+ def to_bin(str)
8
+ str.to_s.force_encoding("BINARY")
7
9
  end
8
10
 
9
- def Utils.to_utf8(str)
10
- str.force_encoding("UTF-8")
11
+ def to_utf8(str)
12
+ return unless str.is_a?(String)
13
+ if str.encoding.to_s == "IBM866"
14
+ str.to_s.encode("UTF-8", "IBM866", :invalid => :replace, :replace => "?")
15
+ else
16
+ str.to_s.force_encoding("UTF-8")
17
+ end
11
18
  end
12
19
 
20
+ def get_1c_id(obj)
21
+ return false unless @ole
22
+
23
+ str_id = @ole.invoke('ЗначениеВСтрокуВнутр', obj)
24
+ escaped_str_id = "\"#{str_id.gsub('"', '""')}\""
25
+
26
+ end # get_1c_id
27
+
13
28
  end # Utils
14
29
 
15
30
  end # S41C
data/lib/s41c/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module S41C
2
2
 
3
- VERSION = '0.0.2'
3
+ VERSION = '0.0.4'
4
4
 
5
5
  end # S41C
data/lib/s41c.rb CHANGED
@@ -1,5 +1,5 @@
1
- require 'socket'
2
1
  require 's41c/version'
3
2
  require 's41c/utils'
3
+ require 's41c/sandbox'
4
4
  require 's41c/server'
5
5
  require 's41c/client'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: s41c
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-19 00:00:00.000000000 Z
12
+ date: 2012-03-24 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: TCP-socket сервер и клиент для платформы "1С:Предприятие"
15
15
  email:
@@ -20,8 +20,8 @@ extra_rdoc_files: []
20
20
  files:
21
21
  - LICENSE.BSD
22
22
  - s41c.gemspec
23
- - s41c-0.0.1.gem
24
23
  - README.md
24
+ - lib/s41c/sandbox.rb
25
25
  - lib/s41c/version.rb
26
26
  - lib/s41c/utils.rb
27
27
  - lib/s41c/server.rb
data/s41c-0.0.1.gem DELETED
Binary file