shanti555890 1.1.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.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/lib/shanti555890/version.rb +5 -0
  3. data/lib/shanti555890.rb +10 -0
  4. data/lib/source/add_controller.rb +53 -0
  5. data/lib/source/all_data/data_list.rb +76 -0
  6. data/lib/source/all_data/data_list_student_short.rb +16 -0
  7. data/lib/source/all_data/data_table.rb +38 -0
  8. data/lib/source/controller.rb +103 -0
  9. data/lib/source/database/add_controller.rb +53 -0
  10. data/lib/source/database/controller.rb +103 -0
  11. data/lib/source/database/create.sql +10 -0
  12. data/lib/source/database/db_university.rb +31 -0
  13. data/lib/source/database/dialog_create_student.rb +97 -0
  14. data/lib/source/database/insert.sql +3 -0
  15. data/lib/source/database/observer.rb +53 -0
  16. data/lib/source/database/student_file_adapter.rb +37 -0
  17. data/lib/source/database/student_list.rb +34 -0
  18. data/lib/source/database/student_list_db.rb +77 -0
  19. data/lib/source/database/update_contact_controller.rb +9 -0
  20. data/lib/source/database/update_controller.rb +29 -0
  21. data/lib/source/database/update_git_controller.rb +9 -0
  22. data/lib/source/database/update_name_controller.rb +9 -0
  23. data/lib/source/database/window.rb +59 -0
  24. data/lib/source/strategy/student_list_base.rb +80 -0
  25. data/lib/source/strategy/student_list_json.rb +16 -0
  26. data/lib/source/strategy/student_list_strat.rb +12 -0
  27. data/lib/source/strategy/student_list_txt.rb +42 -0
  28. data/lib/source/strategy/student_list_yaml.rb +18 -0
  29. data/lib/source/student/student.rb +156 -0
  30. data/lib/source/student/student_short.rb +50 -0
  31. data/lib/source/student/student_test.rb +86 -0
  32. data/lib/source/update_contact_controller.rb +9 -0
  33. data/lib/source/update_controller.rb +29 -0
  34. data/lib/source/update_git_controller.rb +9 -0
  35. data/lib/source/update_name_controller.rb +9 -0
  36. data/shanti555890.gemspec +17 -0
  37. data/sig/shanti555890.rbs +4 -0
  38. metadata +93 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f46caa37cfa400f1ba3b8e7b1b413480c0ea4461f8ae973c427de8e48d1fe6e1
4
+ data.tar.gz: 4d9f0741be3f817093b3f36325f513d978dda403c383fe365cf68ff3c1dc0816
5
+ SHA512:
6
+ metadata.gz: db0a4fa5b05210cb613e3939cc54879204e4242196f4eb99a74fd4138447eb6f6828f6877710771734c921bb90f6c0b5353e049a16942f58df8713111af5bf76
7
+ data.tar.gz: 9784bf80e8789988c4e56d86311bcd2f0eab3bdaab7555b38c95d4b94b276938e1f2e11121027afe9c971cb253176d77f3fa05d6926d7d33d36075571b96079d
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shanti555890
4
+ VERSION = "1.1.4"
5
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "shanti555890/version"
4
+
5
+ module Shanti555890
6
+ Dir[File.dirname(__FILE__) + '/source/**/*.rb'].each { |file|
7
+ puts file
8
+ require file
9
+ }
10
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+ require_relative '../student/student'
3
+ require 'logger'
4
+ class AddStudentController
5
+
6
+ def initialize(student_list)
7
+ @student_list = student_list
8
+ @logger = Logger.new('controller_add.log') # Указывает путь и имя файла для логов
9
+ end
10
+
11
+ #привязка view
12
+ def add_view(view)
13
+ @view = view
14
+ end
15
+
16
+ def execute
17
+ @view.execute
18
+ @logger.info('Executing add student operation')
19
+ end
20
+
21
+ def save_student(student)
22
+ @logger.info('Saving student')
23
+ @student_list.add_student(student)
24
+ end
25
+
26
+
27
+ def validate_fields(fields)
28
+ @logger.info('Validating fields')
29
+ required_fields = [:first_name, :middle_name, :surname] # список обязательных полей
30
+ if required_fields.all? { |field| fields.key?(field) }
31
+ student = Student.new(
32
+ first_name: fields[:first_name],
33
+ middle_name: fields[:middle_name],
34
+ surname: fields[:surname],
35
+ id: fields[:id] || nil,
36
+ git: fields[:git] || nil,
37
+ phone_number: fields[:phone_number] || nil,
38
+ mail: fields[:mail] || nil,
39
+ telegram: fields[:telegram] || nil
40
+ )
41
+ puts(student)
42
+ return student
43
+ else
44
+ return nil
45
+ end
46
+ rescue ArgumentError => e
47
+ @logger.error("Error occurred while validating fields: #{e.message}")
48
+ return nil
49
+ end
50
+
51
+
52
+
53
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DataList
4
+ attr_writer :list
5
+ def initialize(objects)
6
+ self.sel_objects = []
7
+ self.list = objects
8
+ @observers = []
9
+ end
10
+
11
+ def add_observer(observer)
12
+ @observers.append(observer)
13
+ end
14
+
15
+ def notify
16
+ @observers.each { |observer| observer.on_datalist_changed(get_data) }
17
+ end
18
+
19
+ def select(number)
20
+ raise ArgumentError, "arg 'number' not Integer" if number.class != Integer
21
+ sel_objects.append(number)
22
+ end
23
+
24
+ def clear_select
25
+ self.sel_objects = []
26
+ end
27
+
28
+ def get_select
29
+ sel_objects.inject([]) {|res, index| res<<list[index].id}
30
+ end
31
+
32
+ def clear_selected
33
+ self.sel_objects = []
34
+ end
35
+
36
+ #def get_selected
37
+ # list_id=[]
38
+ # sel_objects.each do |i|
39
+ # list_id.append(list[i].id)
40
+ # end
41
+ # list_id
42
+ #end
43
+
44
+ def get_data
45
+ result = []
46
+ id = 0
47
+ list.each do |o|
48
+ elem = []
49
+ elem << id
50
+ # * необходима так как, если нам передадут [1,2,3], то передастся row.push(1,2,3)
51
+ elem.push(*table_fields(o))
52
+ result << elem
53
+ id += 1
54
+ end
55
+ DataTable.new(result)
56
+ end
57
+
58
+ def replace_objects(objects_list)
59
+ self.list=objects_list.dup
60
+ notify
61
+ end
62
+
63
+ def get_names
64
+
65
+ end
66
+
67
+ def table_fields(object)
68
+ []
69
+ end
70
+
71
+
72
+
73
+ private
74
+ attr_accessor :sel_objects
75
+ attr_reader :list
76
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'data_list'
4
+
5
+ class DataListStudentShort < DataList
6
+
7
+
8
+
9
+ def get_names
10
+ ["short_name_initials", "git", "contact"]
11
+ end
12
+
13
+ protected def table_fields(object)
14
+ [object.short_name_initials, object.git, object.contact]
15
+ end
16
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DataTable
4
+
5
+ attr_reader :str_count, :st_count
6
+
7
+ def initialize(table)
8
+ self.str_count = table.length
9
+
10
+ max_c=0
11
+ table.each do |el|
12
+ max_c = el.length if el.size > max_c
13
+ end
14
+ self.st_count = max_c
15
+ self.table = table
16
+ end
17
+
18
+
19
+ def get_elem(str, st)
20
+ return nil if str>=str_count
21
+ return nil if st>=st_count
22
+ table[str][st]
23
+ end
24
+
25
+ def each(&block)
26
+ table.each do |row|
27
+ block.call(row)
28
+ end
29
+ end
30
+
31
+ def to_s
32
+ table.map { |row| "[#{row.join(', ')}]" }.join("\n")
33
+ end
34
+ private
35
+ attr_accessor :table
36
+ attr_writer :str_count, :st_count
37
+
38
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+ require_relative 'window'
3
+ require_relative 'student_list'
4
+ require_relative 'student_list_db'
5
+ require 'win32api'
6
+ require_relative '../student/student'
7
+ require_relative 'student_file_adapter'
8
+ require_relative 'add_controller'
9
+ require_relative '../strategy/student_list_json'
10
+ require_relative '../strategy/student_list_txt'
11
+ require_relative '../strategy/student_list_yaml'
12
+ require_relative '../all_data/data_list_student_short'
13
+ require_relative 'update_controller'
14
+ require 'fox16'
15
+ include Fox
16
+ require_relative 'update_name_controller'
17
+ require_relative 'update_git_controller'
18
+ require_relative 'update_contact_controller'
19
+ require 'logger'
20
+ class StudentListController
21
+ def initialize(view)
22
+ @view = view
23
+ @data_list = DataListStudentShort.new([])
24
+ @data_list.add_observer(@view)
25
+ @student_list = StudentList.new(StudentListDBAdapter.new)
26
+ adapter_path='C:/Users/Lenovo/ruby_labs_new/lab2/tests/students.yaml'
27
+ @logger = Logger.new('controller.log')
28
+ end
29
+
30
+
31
+
32
+ def refresh_data(k_page, number_students)
33
+ #begin
34
+ @logger.info("Refreshing data with k_page=#{k_page} and number_students=#{number_students}")
35
+ @data_list = @student_list.get_k_n_student_short_list(k_page, number_students, @data_list)
36
+ #rescue
37
+ # on_db_conn_error
38
+ #end
39
+ @view.update_count_students(@student_list.count_student)
40
+ @logger.info("Data refreshed successfully with k_page=#{k_page} and number_students=#{number_students}")
41
+ end
42
+
43
+ def on_db_conn_error
44
+ api = Win32API.new('user32', 'MessageBox', ['L', 'P', 'P', 'L'], 'I')
45
+ api.call(0, "No connection to DB", "Error", 0)
46
+ exit(false)
47
+ end
48
+
49
+ def student_add
50
+ controller = AddStudentController.new(@student_list)
51
+ show_dialog(controller)
52
+ end
53
+
54
+ def get_student_id(index)
55
+ @data_list.select(index)
56
+ id = @data_list.get_select
57
+ @data_list.clear_selected
58
+ id
59
+ end
60
+ public
61
+ def student_change_name(index)
62
+ @logger.info('Changing student name')
63
+ puts 'update name'
64
+ id = get_student_id(index)
65
+ controller = ChangeStudentNameController.new(@student_list, id)
66
+ show_dialog(controller)
67
+ end
68
+
69
+ def student_change_git(index)
70
+ @logger.info('Changing student Git')
71
+ puts 'update git'
72
+ id = get_student_id(index)
73
+ controller = ChangeStudentGitController.new(@student_list, id)
74
+ show_dialog(controller)
75
+ end
76
+
77
+ def student_change_contact(index)
78
+ @logger.info('Changing student contact')
79
+ puts 'update name'
80
+ id = get_student_id(index)
81
+ controller = ChangeStudentContactController.new(@student_list, id)
82
+ show_dialog(controller)
83
+ end
84
+ def student_delete(indexes)
85
+ @data_list.select(*indexes)
86
+ id_list = @data_list.get_select
87
+ @data_list.clear_selected
88
+
89
+ id_list.each{|student_id| @student_list.delete_student(student_id)}
90
+ @view.refresh
91
+ @logger.info("Deleted students with IDs: #{id_list.join(', ')}")
92
+ end
93
+
94
+ private
95
+ def show_dialog(controller)
96
+ # controller = AddStudentController.new(@student_list)
97
+ view = CreateStudentDialog.new(@view, controller)
98
+ controller.add_view(view)
99
+ controller.execute
100
+ @view.refresh
101
+ end
102
+
103
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+ require_relative '../student/student'
3
+ require 'logger'
4
+ class AddStudentController
5
+
6
+ def initialize(student_list)
7
+ @student_list = student_list
8
+ @logger = Logger.new('controller_add.log') # Указывает путь и имя файла для логов
9
+ end
10
+
11
+ #привязка view
12
+ def add_view(view)
13
+ @view = view
14
+ end
15
+
16
+ def execute
17
+ @view.execute
18
+ @logger.info('Executing add student operation')
19
+ end
20
+
21
+ def save_student(student)
22
+ @logger.info('Saving student')
23
+ @student_list.add_student(student)
24
+ end
25
+
26
+
27
+ def validate_fields(fields)
28
+ @logger.info('Validating fields')
29
+ required_fields = [:first_name, :middle_name, :surname] # список обязательных полей
30
+ if required_fields.all? { |field| fields.key?(field) }
31
+ student = Student.new(
32
+ first_name: fields[:first_name],
33
+ middle_name: fields[:middle_name],
34
+ surname: fields[:surname],
35
+ id: fields[:id] || nil,
36
+ git: fields[:git] || nil,
37
+ phone_number: fields[:phone_number] || nil,
38
+ mail: fields[:mail] || nil,
39
+ telegram: fields[:telegram] || nil
40
+ )
41
+ puts(student)
42
+ return student
43
+ else
44
+ return nil
45
+ end
46
+ rescue ArgumentError => e
47
+ @logger.error("Error occurred while validating fields: #{e.message}")
48
+ return nil
49
+ end
50
+
51
+
52
+
53
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+ require_relative 'window'
3
+ require_relative 'student_list'
4
+ require_relative 'student_list_db'
5
+ require 'win32api'
6
+ require_relative '../student/student'
7
+ require_relative 'student_file_adapter'
8
+ require_relative 'add_controller'
9
+ require_relative '../strategy/student_list_json'
10
+ require_relative '../strategy/student_list_txt'
11
+ require_relative '../strategy/student_list_yaml'
12
+ require_relative '../all_data/data_list_student_short'
13
+ require_relative 'update_controller'
14
+ require 'fox16'
15
+ include Fox
16
+ require_relative 'update_name_controller'
17
+ require_relative 'update_git_controller'
18
+ require_relative 'update_contact_controller'
19
+ require 'logger'
20
+ class StudentListController
21
+ def initialize(view)
22
+ @view = view
23
+ @data_list = DataListStudentShort.new([])
24
+ @data_list.add_observer(@view)
25
+ @student_list = StudentList.new(StudentListDBAdapter.new)
26
+ adapter_path='C:/Users/Lenovo/ruby_labs_new/lab2/tests/students.yaml'
27
+ @logger = Logger.new('controller.log')
28
+ end
29
+
30
+
31
+
32
+ def refresh_data(k_page, number_students)
33
+ #begin
34
+ @logger.info("Refreshing data with k_page=#{k_page} and number_students=#{number_students}")
35
+ @data_list = @student_list.get_k_n_student_short_list(k_page, number_students, @data_list)
36
+ #rescue
37
+ # on_db_conn_error
38
+ #end
39
+ @view.update_count_students(@student_list.count_student)
40
+ @logger.info("Data refreshed successfully with k_page=#{k_page} and number_students=#{number_students}")
41
+ end
42
+
43
+ def on_db_conn_error
44
+ api = Win32API.new('user32', 'MessageBox', ['L', 'P', 'P', 'L'], 'I')
45
+ api.call(0, "No connection to DB", "Error", 0)
46
+ exit(false)
47
+ end
48
+
49
+ def student_add
50
+ controller = AddStudentController.new(@student_list)
51
+ show_dialog(controller)
52
+ end
53
+
54
+ def get_student_id(index)
55
+ @data_list.select(index)
56
+ id = @data_list.get_select
57
+ @data_list.clear_selected
58
+ id
59
+ end
60
+ public
61
+ def student_change_name(index)
62
+ @logger.info('Changing student name')
63
+ puts 'update name'
64
+ id = get_student_id(index)
65
+ controller = ChangeStudentNameController.new(@student_list, id)
66
+ show_dialog(controller)
67
+ end
68
+
69
+ def student_change_git(index)
70
+ @logger.info('Changing student Git')
71
+ puts 'update git'
72
+ id = get_student_id(index)
73
+ controller = ChangeStudentGitController.new(@student_list, id)
74
+ show_dialog(controller)
75
+ end
76
+
77
+ def student_change_contact(index)
78
+ @logger.info('Changing student contact')
79
+ puts 'update name'
80
+ id = get_student_id(index)
81
+ controller = ChangeStudentContactController.new(@student_list, id)
82
+ show_dialog(controller)
83
+ end
84
+ def student_delete(indexes)
85
+ @data_list.select(*indexes)
86
+ id_list = @data_list.get_select
87
+ @data_list.clear_selected
88
+
89
+ id_list.each{|student_id| @student_list.delete_student(student_id)}
90
+ @view.refresh
91
+ @logger.info("Deleted students with IDs: #{id_list.join(', ')}")
92
+ end
93
+
94
+ private
95
+ def show_dialog(controller)
96
+ # controller = AddStudentController.new(@student_list)
97
+ view = CreateStudentDialog.new(@view, controller)
98
+ controller.add_view(view)
99
+ controller.execute
100
+ @view.refresh
101
+ end
102
+
103
+ end
@@ -0,0 +1,10 @@
1
+ create table students (
2
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
3
+ first_name varchar(32) not null,
4
+ middle_name varchar(32) not null,
5
+ surname varchar(32) not null,
6
+ phone_number varchar(32) null,
7
+ telegram varchar(32) null,
8
+ mail varchar(32) null,
9
+ git varchar(32) null
10
+ );
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ class DBUniversity
3
+ private_class_method :new
4
+ @instance_mutex = Mutex.new
5
+
6
+ #организует подключение, чтобы не делать много подключений
7
+ def initialize
8
+ @client = SQLite3::Database.open '/Users/Lenovo/untitled/university.sql'
9
+ @client.results_as_hash = true
10
+ end
11
+
12
+ #обеспечивает принцип одиночки
13
+ def self.instance
14
+ return @instance if @instance
15
+
16
+ @instance_mutex.synchronize do
17
+ @instance ||= new
18
+ end
19
+ @instance
20
+ end
21
+
22
+ def prepare_exec(statement, *params)
23
+ @client.prepare(statement).execute(*params)
24
+ end
25
+
26
+ def query(statement)
27
+ @client.query(statement)
28
+ end
29
+
30
+
31
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+ require 'fox16'
3
+
4
+ include Fox
5
+ class CreateStudentDialog<FXDialogBox
6
+ def initialize(parent, controller)
7
+ # Создаем родительское модальное окно
8
+ super(parent, "Добавление студента", DECOR_TITLE | DECOR_BORDER | DECOR_RESIZE)
9
+
10
+ @controller = controller
11
+ @student = nil
12
+
13
+ # Устанавливаем размер окна и делаем его модальным
14
+ setWidth(500)
15
+ setHeight(400)
16
+ add_fields
17
+ # setModal(true)
18
+ end
19
+
20
+ def set_student(student, editable_fields)
21
+ @student=student
22
+ enter_student(editable_fields)
23
+ end
24
+
25
+ private
26
+ def add_fields
27
+ #кнопка отмены
28
+
29
+ frame_data = FXVerticalFrame.new(self, :opts=> LAYOUT_FILL_X|LAYOUT_FILL_Y )
30
+
31
+ field_name =[[:first_name,'Имя'], [:surname, 'Фамилия'], [:middle_name, 'Отчество'], [:git, 'Гит'], [:mail, 'Почта'], [:phone_number, 'Телефон'], [:telegram, 'Телеграм']]
32
+ @field_text = {}
33
+ field_name.each do |field|
34
+ frame_field = FXHorizontalFrame.new(frame_data )
35
+ field_label = FXLabel.new(frame_field, field[1], :opts => LAYOUT_FIX_WIDTH)
36
+ field_label.setWidth(100)
37
+ text = FXTextField.new(frame_field, 40, :opts=>TEXTFIELD_NORMAL)
38
+ @field_text[field[0]] = text
39
+ end
40
+
41
+ btn_frame = FXHorizontalFrame.new(frame_data, LAYOUT_CENTER_X)
42
+ btn_add=FXButton.new(btn_frame, "Сохранить")
43
+ btn_add.textColor = Fox.FXRGB(0,23,175)
44
+ btn_add.disable
45
+
46
+ btn_back=FXButton.new(btn_frame, "Отмена")
47
+ btn_back.textColor = Fox.FXRGB(0,23,175)
48
+
49
+ btn_add.connect(SEL_COMMAND) do
50
+ @controller.save_student(@student)
51
+ self.handle(btn_add, FXSEL(Fox::SEL_COMMAND,
52
+ Fox::FXDialogBox::ID_ACCEPT), nil)
53
+ # @controller.on_add_click(@student)
54
+ end
55
+
56
+ btn_back.connect(SEL_COMMAND) do
57
+ self.handle(btn_back, FXSEL(Fox::SEL_COMMAND,
58
+ Fox::FXDialogBox::ID_CANCEL), nil)
59
+ # @controller.on_add_click(@student)
60
+ end
61
+
62
+ @field_text.each_key do |name_field|
63
+ @field_text[name_field].connect(SEL_CHANGED) do |text_field|
64
+ res = {}
65
+ @field_text.each do |k,v|
66
+ text = v.text.empty? ? nil : v.text
67
+ res[k] = text
68
+ end
69
+
70
+
71
+ result = @controller.validate_fields(res)
72
+ unless result.nil?
73
+ @student = result
74
+ btn_add.enable
75
+ else
76
+ btn_add.disable
77
+ end
78
+
79
+ end
80
+ end
81
+
82
+ end
83
+
84
+ def enter_student(editable_fields)
85
+ unless @student.nil?
86
+ puts @student.class
87
+ student_hash = @student.to_hash
88
+ @field_text.each_key do |name_field|
89
+ puts "fields #{name_field}"
90
+ unless editable_fields.include?(name_field)
91
+ @field_text[name_field].editable = false
92
+ end
93
+ @field_text[name_field].text = student_hash[name_field]
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,3 @@
1
+
2
+ insert into students(first_name, middle_name, surname, phone_number, telegram, mail, git) values
3
+ ('Петр', 'Петрович', 'Петров', '79180085000', '@petrrr', 'petya@mail.ru', 'github.com/petya007');
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+ # Паттерн Наблюдатель (Observer) - это поведенческий паттерн проектирования,
3
+ # который позволяет объектам следить и реагировать на изменения состояния других объектов.
4
+
5
+ # Наблюдаемый объект
6
+ class Observable
7
+ attr_reader :observers
8
+
9
+ def initialize
10
+ @observers = []
11
+ end
12
+
13
+ def add_observer(observer)
14
+ @observers << observer
15
+ end
16
+
17
+ def remove_observer(observer)
18
+ @observers.delete(observer)
19
+ end
20
+
21
+ def notify_observers
22
+ @observers.each { |observer| observer.update(self) }
23
+ end
24
+
25
+ def do_something
26
+ puts "Наблюдаемый объект: сделал что-то!"
27
+ notify_observers
28
+ end
29
+ end
30
+
31
+ # Наблюдатель
32
+ class Observer
33
+ def update(observable)
34
+ puts "Наблюдатель: получил оповещение от #{observable.class.name}"
35
+ end
36
+ end
37
+
38
+ # Пример использования
39
+ observable = Observable.new
40
+ observer = Observer.new
41
+
42
+ observable.add_observer(observer)
43
+
44
+ observable.do_something
45
+ # Вывод:
46
+ # Наблюдаемый объект: сделал что-то!
47
+ # Наблюдатель: получил оповещение от Observable
48
+
49
+ observable.remove_observer(observer)
50
+
51
+ observable.do_something
52
+ # Вывод:
53
+ # Наблюдаемый объект: сделал что-то!