Форум программистов, компьютерный форум, киберфорум
Ruby
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.54/13: Рейтинг темы: голосов - 13, средняя оценка - 4.54
0 / 0 / 0
Регистрация: 21.09.2018
Сообщений: 4
1

Руби не видит метод

24.12.2019, 12:05. Показов 2538. Ответов 3

Author24 — интернет-сервис помощи студентам
Всем привет.
В данный момент занимаюсь написанием собственной DSL, суть проблемы в том, что deep_merge метод (который я скопировал из гема configus) работает только через self.deep_merge(при этом не правильно), в остальных случаях метод не видимый в методе self.config.
Мне нужно смержить два хэша, для этого стандартный merge не подойдёт, т.к он не работает на многоуровневых хэшах.

init.rb:
:staging - дочерний енв, : production - родительский енв
Вывод должен быть таким:
p config.key1 => "value1"
p config.key2 => "new value2" (Т.е забрать все ключи и значения из prod, добавить их к staging, но не переписывать ключи staging, т.е исключить key2 и key4 к примеру)

Ruby
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
require "./configus"
require "pry"
 
#Binding.pry
 
config = Configus.config :staging, :production do
  production do
    key1 "value1"
    key2 "value2"
    group1 do
      key3 "value3"
      key4 "value4"
    end
  end
 
  staging do
    key2 "new value2"
    group1 do
      key4 "new value4"
    end
  end
end
 
p config
Configus.rb:
Извиняюсь за кривую и костыльную реализацию дслки, получилось так как получилось

Ruby
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Configus
  class InHash
    attr_reader :inner_hash
 
    def initialize
      @inner_hash = {}
    end
 
    def method_missing(name, *args, &block)
      if block_given?
        context = InHash.new
        context.instance_eval &block
        @inner_hash[name] = context
      elsif args.empty?
        @inner_hash[name]
      else
        @inner_hash[name] = args
      end
    end
  end
 
  def self.deep_merge(target, source)
    source.each_pair do |k, v|
      tv = target[k]
      target[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? deep_merge(tv, v) : v
    end
    target
  end
 
  def self.config(environment, parent = nil, &block)
    in_hash = InHash.new
    in_hash.instance_eval &block
    keys = in_hash.inner_hash.keys
    index = keys.find_index(environment)
    if parent && environment
      parent_hash = in_hash.inner_hash[parent]
      adopted_hash = in_hash.inner_hash[environment]
      merged_hash = deep_merge(parent_hash, adopted_hash)
      in_hash
    elsif environment == keys[index]
      in_hash.inner_hash[environment]
    end
  end
end
Вывод значений по ключу БЕЗ наследования сред (в данном случае production):
Ruby
1
2
3
p config.key1             => ["value1"]
p config.group1.key3 => ["value3"]
p config                      => #<Configus::InHash:0x000055a0993f8a88 @inner_hash={:key1=>["value1"], :key2=>["value2"], :group1=>#<Configus::InHash:0x000055a0993f87b8 @inner_hash={:key3=>["value3"], :key4=>["value4"]}>}>
Вывод значений по ключу С наследованием сред (в данном случае дочерняя среда - staging, родительская - production):

Ruby
1
2
3
4
p config.key1             => nil
p config.group1.key3 => Traceback (most recent call last):
init.rb:24:in `<main>': undefined method `key3' for nil:NilClass (NoMethodErro
p config                      => #<Configus::InHash:0x000055cfe51410d0 @inner_hash={:production=>#<Configus::InHash:0x000055cfe5140f40 @inner_hash={:key1=>["value1"], :key2=>["value2"], :group1=>#<Configus::InHash:0x000055cfe5140bf8 @inner_hash={:key3=>["value3"], :key4=>["value4"]}>, :[]=>[#<Configus::InHash:0x000055cfe514bee0 @inner_hash={}>], :[]==>[#<Configus::InHash:0x000055cfe514bee0 @inner_hash={}>, nil]}>, :staging=>#<Configus::InHash:0x000055cfe5140748 @inner_hash={:key2=>["new value2"], :group1=>#<Configus::InHash:0x000055cfe51404c8 @inner_hash={:key4=>["new value4"]}>, :each_pair=>#<Configus::InHash:0x000055cfe514bee0 @inner_hash={}>}>}>
P.P.S
DSL - Domain Specific Language ( не Digital Subscriber Line )
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
24.12.2019, 12:05
Ответы с готовыми решениями:

Не видит метод класса
Есть несколько классов: основной и служебные. Во всех классах объявлены процедуры с модификатором...

Делегат не видит метод
Делегат не видит метод который я ему передаю. ниже в коде я отметил место где происходит передача....

Метод не видит коллекцию
Беда на картинке, почему не могу использовать theStudents??

ObjectDataSource не видит метод
Всем привет. Есть GridView. В данный момент осваиваю постраничное разбиение. С автоматической...

3
the hardway first
Эксперт JS
2461 / 1836 / 906
Регистрация: 05.06.2015
Сообщений: 3,603
24.12.2019, 15:23 2
Лучший ответ Сообщение было отмечено cybersuicide как решение

Решение

Не понял суть вопроса.

Но вы попались в ловушку с #method_missing:

дело в том, что в методе Configus::deep_merge вы вызываете Hash#each_pair на экземпляре InHash, где, т. к. такого метода нет, создается новый элемент внутреннего хэша с ключом :each_pair и новым InHash в качестве значения, а т. к. сюда передается блок, то совсем веселье получается.

Тоже самое в рекурсивном вызове - вы свой InHash проверяете на соответствие классу Hash.

Ваш метод Configus::config возвращает in_hash, а должен вернуть merged_hash. Да вы меняете значения хеша production значениями staging, но ожидаете получить не
Код
#<Configus::InHash:0x0000000003e49ca8
 @inner_hash=
  {:production=>
    #<Configus::InHash:0x0000000003e49be0
     @inner_hash=
      {:key1=>["value1"],
       :key2=>["new value2"],
       :group1=>
        #<Configus::InHash:0x0000000003e49a28
         @inner_hash={:key3=>["value3"], :key4=>["new value4"]}>}>,
   :staging=>
    #<Configus::InHash:0x0000000003e49870
     @inner_hash=
      {:key2=>["new value2"],
       :group1=>
        #<Configus::InHash:0x0000000003e49730
         @inner_hash={:key4=>["new value4"]}>}>}>
а, судя по тому как вы используете в init
Код
#<Configus::InHash:0x00000000041a2008
 @inner_hash=
  {:key1=>["value1"],
   :key2=>["new value2"],
   :group1=>
    #<Configus::InHash:0x00000000041a1e50
     @inner_hash={:key3=>["value3"], :key4=>["new value4"]}>}>
Ruby
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class Configus
  class InHash
    attr_reader :inner_hash
 
    def initialize
      @inner_hash = {}
    end
 
    def method_missing(name, *args, &block)
      if block_given?
        context = InHash.new
        context.instance_eval &block
 
        @inner_hash[name] = context
      elsif args.empty?
        @inner_hash[name]
      else
        @inner_hash[name] = args # NOTICE: args is array
      end
    end
  end
 
  def self.deep_merge(target, source)
    # source.each_pair do |k, v|
    source.inner_hash.each_pair do |k, v|
      # tv = target[k]
      tv = target.inner_hash[k]
 
      target.inner_hash[k] =
        if tv.is_a?(InHash) && v.is_a?(InHash)
          deep_merge(tv, v)
        else
          v
        end
    end
 
    target
  end
 
  def self.config(environment, parent = nil, &block)
    in_hash = InHash.new
    in_hash.instance_eval &block
 
    keys = in_hash.inner_hash.keys
    index = keys.find_index(environment)
 
    if parent && environment
      parent_hash = in_hash.inner_hash[parent]
      adopted_hash = in_hash.inner_hash[environment]
      # merged_hash = deep_merge(parent_hash, adopted_hash)
      # in_hash
      deep_merge(parent_hash, adopted_hash)
    elsif environment == keys[index]
      in_hash.inner_hash[environment]
    end
  end
end
1
0 / 0 / 0
Регистрация: 21.09.2018
Сообщений: 4
24.12.2019, 15:47  [ТС] 3
Благодарю вас от всей своей души, решили мою двухнедельную проблему, целую ваши пятки!
0
the hardway first
Эксперт JS
2461 / 1836 / 906
Регистрация: 05.06.2015
Сообщений: 3,603
25.12.2019, 08:57 4
Цитата Сообщение от cybersuicide Посмотреть сообщение
получилось так как получилось
ИМХО, странная идея. В вашей реализации т. н. "родительский" конфиг перезаписывается. Я ни в коем разе вас не осуждаю, но мне было бы удобнее использовать такую штуку
Ruby
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
require 'minitest/autorun'
 
require_relative "configus"
 
class TestInHash < MiniTest::Test
  def setup
    @inhash = Configus::InHash.new
  end
 
  def test_simple_value
    @inhash.foo 'bar'
    assert_equal 'bar', @inhash.foo
  end
 
  def test_block_with_simple_value
    @inhash.instance_eval do
      bar 'baz'
    end
 
    assert_equal 'baz', @inhash.bar
  end
 
  def test_nested_blocks
    @inhash.instance_eval do
      key1 'value1'
      key2 do
        key2_1 'value2_1'
      end
    end
 
    assert_equal 'value1', @inhash.key1
    assert_equal 'value2_1', @inhash.key2.key2_1
  end
 
  def test_deep_merge
    hash1 = Configus::InHash.new
    hash1.instance_eval do
      val1 'val1'
    end
 
    hash2 = Configus::InHash.new
    hash2.instance_eval do
      val2 'val2'
    end
 
    merged = hash1.deep_merge hash2
 
    assert_equal 'val1', merged.val1
    assert_equal 'val2', merged.val2
  end
 
  def test_deep_merge_w_nested_block
    hash1 = Configus::InHash.new
    hash1.instance_eval do
      val1 'val1'
    end
 
    hash2 = Configus::InHash.new
    hash2.instance_eval do
      val1 'new val1'
      val2 'val2'
      val3 do
        val4 'val4'
      end
    end
 
    merged = hash1.deep_merge hash2
 
    assert_equal 'val1', hash1.val1
    assert_equal 'new val1', merged.val1
    assert_equal 'val2', merged.val2
    assert_equal 'val4', merged.val3.val4
  end
end
 
class TestConfigus < MiniTest::Test
  def test_simple_config
    config = Configus.config :production do
      production do
        key1 "value1"
        key2 "value2"
        group1 do
          key3 "value3"
          key4 "value4"
        end
      end
    end
 
    assert_equal 'value1', config.production.key1
    assert_equal 'value3', config.production.group1.key3
  end
 
  def test_merged_config
    config = Configus.config :staging, :production do
      production do
        key1 "value1"
        key2 "value2"
        group1 do
          key3 "value3"
          key4 "value4"
        end
      end
 
      staging do
        key2 "new value2"
        new_key 'new key value'
        group1 do
          key4 "new value4"
          new_key 'new group1 key value'
        end
      end
    end
 
    assert_equal 'value1', config.staging.key1
    assert_equal 'value2', config.production.key2
    assert_equal 'new value2', config.staging.key2
    assert_equal 'value4', config.production.group1.key4
    assert_equal 'new value4', config.staging.group1.key4
    assert_equal 'new key value', config.staging.new_key
    assert_equal 'new group1 key value', config.staging.group1.new_key
  end
end
Ruby
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
module Configus
  class InHash
    attr_reader :inner_hash
 
    def initialize(init = {})
      @inner_hash = init
    end
 
    def deep_merge(other)
      h = InHash.new(inner_hash.merge(other.inner_hash) do |_, this_val, other_val|
        if this_val.is_a?(InHash) && other_val.is_a?(InHash)
          this_val.deep_merge other_val
        else
          other_val
        end
      end)
 
      h
    end
 
    def method_missing(name, *args, &block)
      if block_given?
        context = InHash.new
        context.instance_eval &block
 
        @inner_hash[name] = context
      elsif args.empty?
        @inner_hash.fetch(name)
      else
        @inner_hash[name] = args.first # NOTICE: args is array
      end
    end
  end
 
  def self.config(environment, parent = nil, &block)
    in_hash = InHash.new
    in_hash.instance_eval &block
 
    if parent
      parent_hash = in_hash.inner_hash[parent]
      adopted_hash = in_hash.inner_hash[environment]
      in_hash.inner_hash[environment] = parent_hash.deep_merge(adopted_hash)
    end
    
    in_hash
  end
end
0
25.12.2019, 08:57
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
25.12.2019, 08:57
Помогаю со студенческими работами здесь

Скрипт не видит метод
Привет! Есть класс, пытаюсь обратиться к его методу, мне выводится фатальная ошибка, что нет такой...

Метод не видит массив
В этом коде метод TheLargestVolume который принимает массив volumesArray не видит этот массив. Не...

Компилятор не видит расширяющий метод
Сделал расширяющий метод ForbiddenSymbols() для класса string но среда просто его не видит....

Не видит метод 'connect' QtRuby
Есть кнопка Qt::PushButton и есть сторонняя переменное к которой нужно прибавить еденицу при...

Eclipse не видит метод активити
@Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo...

В mvpAppCompatFragment не видит метод onCreateView
Собсно смотрю запись урока https://www.youtube.com/watch?v=1YLWWgTvoUE (примерно на 53:23 нужный...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
4
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru