Ruby中哈希特定值的访问器方法
我正在用Ruby为API编写一个API包装器。API的一部分是创建一个表示Omeka站点上的项目的类。对API的GET请求返回一个JSON对象,我将其转换为哈希(见下文,因为它很长)。我使用gem来创建访问器方法,而不是让用户浏览长哈希。例如:Ruby中哈希特定值的访问器方法,ruby,class,metaprogramming,Ruby,Class,Metaprogramming,我正在用Ruby为API编写一个API包装器。API的一部分是创建一个表示Omeka站点上的项目的类。对API的GET请求返回一个JSON对象,我将其转换为哈希(见下文,因为它很长)。我使用gem来创建访问器方法,而不是让用户浏览长哈希。例如: class OmekaItem attr_accessor :data def initialize(hash) @data = RecursiveOpenStruct.new(hash, :recurse_over_arrays =
class OmekaItem
attr_accessor :data
def initialize(hash)
@data = RecursiveOpenStruct.new(hash, :recurse_over_arrays => true)
end
end
假设我创建了一个名为item
的类的实例,该类允许用户像这样访问数据:item.data.id
或item.data.modified
最重要的数据在元素文本数组中。有两种元素文本:“都柏林核心”和“项目类型元数据”。我想为这些字段中的数据创建一个单独的开放结构。我就是这样做的:
class OmekaItem
attr_accessor :data, :dublin_core, :item_type_metadata
def initialize(hash)
@data = RecursiveOpenStruct.new(hash, :recurse_over_arrays => true)
dublin_core = Hash.new
item_type_metadata = Hash.new
@data.element_texts.each do |element_text|
if element_text.element_set.name == "Dublin Core"
method_name = element_text.element.name.downcase.gsub(/\s/, '_')
value = element_text.text
dublin_core[method_name] = value
elsif element_text.element_set.name == "Item Type Metadata"
method_name = element_text.element.name.downcase.gsub(/\s/, '_')
value = element_text.text
item_type_metadata[method_name] = value
end
end
@dublin_core = RecursiveOpenStruct.new(dublin_core)
@item_type_metadata = RecursiveOpenStruct.new(item_type_metadata)
end
end
现在,用户可以通过如下方法调用访问都柏林核心元数据:item.Dublin\u Core.title
到目前为止还不错,但这正是我被困的地方。该类需要实现一个to_h
方法,以原始格式返回一个包含更改数据的散列,以便我可以将其传递给POST和PUT方法。如果有人通过调用item.data.element\u text[1].text=“My new data”
来更改数据,那么我可以很容易地调用open struct上返回哈希的方法。但如果用户更改item.dublin_core.title=“My new title”
,则该数据将与存储在@data
中的开放结构分离。我怎样才能把存储数据的两个地方排成一行呢
项目的典型哈希如下所示:
>> pp hash
{"id"=>1,
"url"=>"http://localhost/omeka-2.1-rc1/api/items/1",
"public"=>true,
"featured"=>false,
"added"=>"2013-07-13T04:47:08+00:00",
"modified"=>"2013-07-14T19:37:45+00:00",
"item_type"=>
{"id"=>10,
"url"=>"http://localhost/omeka-2.1-rc1/api/item_types/10",
"name"=>"Lesson Plan",
"resource"=>"item_types"},
"collection"=>
{"id"=>1,
"url"=>"http://localhost/omeka-2.1-rc1/api/collections/1",
"resource"=>"collections"},
"owner"=>
{"id"=>1,
"url"=>"http://localhost/omeka-2.1-rc1/api/users/1",
"resource"=>"users"},
"files"=>
{"count"=>0,
"url"=>"http://localhost/omeka-2.1-rc1/api/files?item=1",
"resource"=>"files"},
"tags"=>[],
"element_texts"=>
[{"html"=>false,
"text"=>"Item Title",
"element_set"=>
{"id"=>1,
"url"=>"http://localhost/omeka-2.1-rc1/api/element_sets/1",
"name"=>"Dublin Core",
"resource"=>"element_sets"},
"element"=>
{"id"=>50,
"url"=>"http://localhost/omeka-2.1-rc1/api/elements/50",
"name"=>"Title",
"resource"=>"elements"}},
{"html"=>false,
"text"=>"Item Subject",
"element_set"=>
{"id"=>1,
"url"=>"http://localhost/omeka-2.1-rc1/api/element_sets/1",
"name"=>"Dublin Core",
"resource"=>"element_sets"},
"element"=>
{"id"=>49,
"url"=>"http://localhost/omeka-2.1-rc1/api/elements/49",
"name"=>"Subject",
"resource"=>"elements"}},
{"html"=>false,
"text"=>"Item Contributor",
"element_set"=>
{"id"=>1,
"url"=>"http://localhost/omeka-2.1-rc1/api/element_sets/1",
"name"=>"Dublin Core",
"resource"=>"element_sets"},
"element"=>
{"id"=>37,
"url"=>"http://localhost/omeka-2.1-rc1/api/elements/37",
"name"=>"Contributor",
"resource"=>"elements"}},
{"html"=>true,
"text"=>"Item Description",
"element_set"=>
{"id"=>1,
"url"=>"http://localhost/omeka-2.1-rc1/api/element_sets/1",
"name"=>"Dublin Core",
"resource"=>"element_sets"},
"element"=>
{"id"=>41,
"url"=>"http://localhost/omeka-2.1-rc1/api/elements/41",
"name"=>"Description",
"resource"=>"elements"}},
{"html"=>true,
"text"=>"Item Creator",
"element_set"=>
{"id"=>1,
"url"=>"http://localhost/omeka-2.1-rc1/api/element_sets/1",
"name"=>"Dublin Core",
"resource"=>"element_sets"},
"element"=>
{"id"=>39,
"url"=>"http://localhost/omeka-2.1-rc1/api/elements/39",
"name"=>"Creator",
"resource"=>"elements"}},
{"html"=>false,
"text"=>"Item Source",
"element_set"=>
{"id"=>1,
"url"=>"http://localhost/omeka-2.1-rc1/api/element_sets/1",
"name"=>"Dublin Core",
"resource"=>"element_sets"},
"element"=>
{"id"=>48,
"url"=>"http://localhost/omeka-2.1-rc1/api/elements/48",
"name"=>"Source",
"resource"=>"elements"}},
{"html"=>false,
"text"=>"Item Publisher",
"element_set"=>
{"id"=>1,
"url"=>"http://localhost/omeka-2.1-rc1/api/element_sets/1",
"name"=>"Dublin Core",
"resource"=>"element_sets"},
"element"=>
{"id"=>45,
"url"=>"http://localhost/omeka-2.1-rc1/api/elements/45",
"name"=>"Publisher",
"resource"=>"elements"}},
{"html"=>false,
"text"=>"Item Date",
"element_set"=>
{"id"=>1,
"url"=>"http://localhost/omeka-2.1-rc1/api/element_sets/1",
"name"=>"Dublin Core",
"resource"=>"element_sets"},
"element"=>
{"id"=>40,
"url"=>"http://localhost/omeka-2.1-rc1/api/elements/40",
"name"=>"Date",
"resource"=>"elements"}},
{"html"=>false,
"text"=>"Item Rights",
"element_set"=>
{"id"=>1,
"url"=>"http://localhost/omeka-2.1-rc1/api/element_sets/1",
"name"=>"Dublin Core",
"resource"=>"element_sets"},
"element"=>
{"id"=>47,
"url"=>"http://localhost/omeka-2.1-rc1/api/elements/47",
"name"=>"Rights",
"resource"=>"elements"}},
{"html"=>false,
"text"=>"Item Relation",
"element_set"=>
{"id"=>1,
"url"=>"http://localhost/omeka-2.1-rc1/api/element_sets/1",
"name"=>"Dublin Core",
"resource"=>"element_sets"},
"element"=>
{"id"=>46,
"url"=>"http://localhost/omeka-2.1-rc1/api/elements/46",
"name"=>"Relation",
"resource"=>"elements"}},
{"html"=>false,
"text"=>"Item Format",
"element_set"=>
{"id"=>1,
"url"=>"http://localhost/omeka-2.1-rc1/api/element_sets/1",
"name"=>"Dublin Core",
"resource"=>"element_sets"},
"element"=>
{"id"=>42,
"url"=>"http://localhost/omeka-2.1-rc1/api/elements/42",
"name"=>"Format",
"resource"=>"elements"}},
{"html"=>false,
"text"=>"Item Language",
"element_set"=>
{"id"=>1,
"url"=>"http://localhost/omeka-2.1-rc1/api/element_sets/1",
"name"=>"Dublin Core",
"resource"=>"element_sets"},
"element"=>
{"id"=>44,
"url"=>"http://localhost/omeka-2.1-rc1/api/elements/44",
"name"=>"Language",
"resource"=>"elements"}},
{"html"=>false,
"text"=>"Item Type",
"element_set"=>
{"id"=>1,
"url"=>"http://localhost/omeka-2.1-rc1/api/element_sets/1",
"name"=>"Dublin Core",
"resource"=>"element_sets"},
"element"=>
{"id"=>51,
"url"=>"http://localhost/omeka-2.1-rc1/api/elements/51",
"name"=>"Type",
"resource"=>"elements"}},
{"html"=>false,
"text"=>"Item Identifier",
"element_set"=>
{"id"=>1,
"url"=>"http://localhost/omeka-2.1-rc1/api/element_sets/1",
"name"=>"Dublin Core",
"resource"=>"element_sets"},
"element"=>
{"id"=>43,
"url"=>"http://localhost/omeka-2.1-rc1/api/elements/43",
"name"=>"Identifier",
"resource"=>"elements"}},
{"html"=>false,
"text"=>"Item Coverage",
"element_set"=>
{"id"=>1,
"url"=>"http://localhost/omeka-2.1-rc1/api/element_sets/1",
"name"=>"Dublin Core",
"resource"=>"element_sets"},
"element"=>
{"id"=>38,
"url"=>"http://localhost/omeka-2.1-rc1/api/elements/38",
"name"=>"Coverage",
"resource"=>"elements"}},
{"html"=>false,
"text"=>"Item Type Duration",
"element_set"=>
{"id"=>3,
"url"=>"http://localhost/omeka-2.1-rc1/api/element_sets/3",
"name"=>"Item Type Metadata",
"resource"=>"element_sets"},
"element"=>
{"id"=>11,
"url"=>"http://localhost/omeka-2.1-rc1/api/elements/11",
"name"=>"Duration",
"resource"=>"elements"}},
{"html"=>false,
"text"=>"Item Type Standards",
"element_set"=>
{"id"=>3,
"url"=>"http://localhost/omeka-2.1-rc1/api/element_sets/3",
"name"=>"Item Type Metadata",
"resource"=>"element_sets"},
"element"=>
{"id"=>24,
"url"=>"http://localhost/omeka-2.1-rc1/api/elements/24",
"name"=>"Standards",
"resource"=>"elements"}},
{"html"=>false,
"text"=>"Item Type Objectives",
"element_set"=>
{"id"=>3,
"url"=>"http://localhost/omeka-2.1-rc1/api/element_sets/3",
"name"=>"Item Type Metadata",
"resource"=>"element_sets"},
"element"=>
{"id"=>25,
"url"=>"http://localhost/omeka-2.1-rc1/api/elements/25",
"name"=>"Objectives",
"resource"=>"elements"}},
{"html"=>false,
"text"=>"Item Type Materials",
"element_set"=>
{"id"=>3,
"url"=>"http://localhost/omeka-2.1-rc1/api/element_sets/3",
"name"=>"Item Type Metadata",
"resource"=>"element_sets"},
"element"=>
{"id"=>26,
"url"=>"http://localhost/omeka-2.1-rc1/api/elements/26",
"name"=>"Materials",
"resource"=>"elements"}},
{"html"=>false,
"text"=>"Item Type Lesson Plan Text",
"element_set"=>
{"id"=>3,
"url"=>"http://localhost/omeka-2.1-rc1/api/element_sets/3",
"name"=>"Item Type Metadata",
"resource"=>"element_sets"},
"element"=>
{"id"=>27,
"url"=>"http://localhost/omeka-2.1-rc1/api/elements/27",
"name"=>"Lesson Plan Text",
"resource"=>"elements"}}],
"extended_resources"=>[]}
这就是我最后做的。它使用Ruby的一些元编程方法循环遍历元素文本,并为每个元素文本创建方法。我不知道真正有帮助的两件事就是这些。首先,使用
循环数组中的每个\u和\u index
可以获得数组中项目的索引,这是创建访问器方法所必需的。其次,我学会了如何将值传递给进程;请参阅setter方法
class OmekaItem
attr_accessor :data
# Parse the data we got from the API into handy methods. All of the data
# from the JSON returned by the API is available as RecursiveOpenStructs
# through @data. The Dublin Core and Item Type Metadata fields are also
# available though special methods of the form dc_title and itm_field.
#
# @param hash [Hash] Uses the hash from OmekaClient::Client::get_hash
#
def initialize(hash)
@data = RecursiveOpenStruct.new(hash, :recurse_over_arrays => true)
# Step through the element texts separating them into Dublin Core and
# Item Type Metadata elements. e is the element text hash; i is the
# index of the element_text in the array of element texts.
@data.element_texts.each_with_index do |e, i|
if e.element_set.name == "Dublin Core"
# Define a reader method that retrieves the data from this element
# text in @data
self.class.send(:define_method,
# The name of the method will have the form "dc_title"
e.element.name.downcase.gsub(/^/, 'dc_').gsub(/\s/, '_'),
proc{ @data.element_texts[i].text }
)
# Define a setter method that sets the data for this element text in
# @ data
self.class.send(:define_method,
# The name of the method will have the form "dc_title="
e.element.name.downcase.gsub(/^/, 'dc_').gsub(/\s/, '_').gsub(/$/, '='),
proc{ |value| @data.element_texts[i].text = value }
)
elsif e.element_set.name == "Item Type Metadata"
# Define a reader method that retrieves the data from this element
# text in @data
self.class.send(:define_method,
# The name of the method will have the form "itm_field"
e.element.name.downcase.gsub(/^/, 'itm_').gsub(/\s/, '_'),
proc{ @data.element_texts[i].text }
)
# Define a setter method that sets the data for this element text in
# @ data
self.class.send(:define_method,
# The name of the method will have the form "itm_title="
e.element.name.downcase.gsub(/^/, 'itm_').gsub(/\s/, '_').gsub(/$/, '='),
proc{ |value| @data.element_texts[i].text = value }
)
end
end
end
end
请注意OpenStruct的实例化,如果可能的话,您可能应该避免它。你看了吗?谢谢你的信息,@ChrisHeald。我不知道方法缓存;你链接到的帖子非常有用。由于性能并没有被证明是一个问题,我想我会采用现在的解决方案,但我会记住这一点,以防我需要回去。