I have the following Automata class:
class DFA
attr_accessor :start, :end, :transition
def initialize(options = {})
#start = options[:start]
#end = options[:end]
#transition = options[:transition]
end
def run(input)
current_state = #start
for i in input
current_state = #transition[current_state][i]
end
return true if #end.include? current_state
return false
end
end
if $PROGRAM_NAME == __FILE__
d = DFA.new(:start => :q1, :end => [:q2],
:transition =>
{ :q1 => {'0' => :q1, '1' => :q2},
:q2 => {'0' => :q3, '1' => :q2},
:q3 => {'0' => :q2, '1' => :q3}
})
puts d.run(['1','1','0','0'])
puts d.run(['1','1','0','0','0'])
puts d.run(['0','0','0'])
end
Output:
True
False
False
The problem is that i want this Automata to be able to 'eat' 'ANY OTHER' symbol. It would be like 'default' or 'else' action. For example:
{ :q1 => {'0' => :q1, 'ANY OTHER SYMBOL' => :q2},
So, it will be possible to write:
puts d.run(['1','1','0','a'])
and get 'True'. What are possible solutions? Thank you!
P.S. I have to write an Automata, which will be able to parse .INI files. May be it would be better not to write a class, but just to form my automata in 'case...when...when...else...' statement? What do you think?
You could define a special symbol:
class DFA
ANY = :any
And calculate the state based on it:
state = #transitions[state][step] || #transitions[state][DFA::ANY]
And use it defining DFA:
{'0' => :q1, '1' => :q2, DFA::ANY => :q2}
Complete example after refactoring:
class DFA
ANY = :any
attr_accessor :start, :end, :transitions
def initialize(options = {})
#start = options[:start]
#end = options[:end]
#transitions = options[:transitions]
end
def run(steps)
state = #start
steps.each do |step|
state = #transitions[state][step] || #transitions[state][DFA::ANY]
raise 'Unexpected Symbol' if state.nil?
end
#end.include? state
end
end
dfa = DFA.new start: :q1,
end: [:q2],
transitions: {
q1: {'0' => :q1, '1' => :q2, DFA::ANY => :q2},
q2: {'0' => :q3, '1' => :q2},
q3: {'0' => :q2, '1' => :q3}
}
puts dfa.run(['Q', '0', '0']) #=> true
Related
Is there a way to DRY up these two Ruby functions by moving the three lines that both functions share to another function?
def format_currency(number)
number_to_currency(number,
:unit => current_user.currency_unit,
:delimiter => current_user.currency_delimiter,
:separator => current_user.currency_separator,
:format => current_user.currency_format
)
end
def format_currency_for_pdf(number, invoice)
number_to_currency(number / invoice.exchange_rate,
:unit => CURRENCIES[invoice.currency]
:delimiter => current_user.currency_delimiter,
:separator => current_user.currency_separator,
:format => current_user.currency_format
)
end
Thanks for any help?
As taro suggests, it will be:
def format_currency(number)
number_to_currency(number,
currency_hash(current_user.currency_unit)
)
end
def format_currency_for_pdf(number, invoice)
number_to_currency(number / invoice.exchange_rate,
currency_hash(CURRENCIES[invoice.currency])
)
end
def currency_hash unit
{
:unit => unit,
:delimiter => current_user.currency_delimiter,
:separator => current_user.currency_separator,
:format => current_user.currency_format
}
end
def format_currency(number)
number_to_currency(number,
currency_hash(current_user.currency_unit)
)
end
def format_currency_for_pdf(number, invoice)
number_to_currency(number / invoice.exchange_rate,
currency_hash(CURRENCIES[invoice.currency])
)
end
def currency_hash(unit)
{
:unit => unit,
:delimiter => current_user.currency_delimiter,
:separator => current_user.currency_separator,
:format => current_user.currency_format
}
end
When the script wants to increment progressbar value it gives me:
C:/Ruby193/lib/ruby/gems/1.9.1/gems/ruby-progressbar-1.4.0/lib/ruby-progressbar/components
/progressable.rb:93:in `<=': comparison of Fixnum with Array failed (ArgumentError)
The Script is shown belove. I ftryed to find solution but no luck. Can Anyone help me with this?
korpusu_id = []
container.divs(:class => "resItem")
.find_all { |div| div.span(:class => "theText", :text => /TestAuto/).exists? }
.each do |korpuss|
id = korpuss.span.parent.parent.attribute_value("id")
id = id[-38..-1]
korpusu_id.push(id)
end
puts ""
puts "Tagad notiek nepieciešamo korpusu dzēšana..."
progress = ProgressBar.create(:title => "Failu dzēšana:", :progress_mark => "|", :format => "%t [%B] %p%%", :total => korpusu_id)
korpusu_id.each do |korp_id|
#b.goto("#{#env}/CorpusMetadataEditor.aspx?id=#{korp_id}")
#b.execute_script("window.confirm = function() {return true}")
delete_poga = #b.link(:id, "ctl00_ContentPlaceHolder1_lnkDeleteCorpora")
if delete_poga.exists?
delete_poga.click
else
puts "Korpuss\n#{#env}/CorpusMetadataEditor.aspx?id=#{korp_id}\nlietotājam #{lietotajs} nav pieejams rediģēšanai.\nTurpinu ar nākošo korpusu!\n---------------------------------------------"
next
end
container.div(:class => "resItem").wait_until_present
progress.increment
end
I found the final solution for my problem. I changed:
progress = ProgressBar.create(:title => "Failu dzēšana:", :progress_mark => "|", :format => "%t [%B] %p%%", :total => korpusu_id.length)
and this worked for me.
Sorry for spam...
I have a collection of OpenStruct elements using which I need to build an XML with help of Nokogiri.
collection = [
OpenStruct.new(:catalogStoreNumber => '657758',
:catalogStoreId => 'CTH6536',
:catalogStoreLocation => 'UnitedStates',
:catalogOwnerId => 'TYCT11190',
:catalogOwner => 'McGrawHill Pub.',
:catalogList => OpenStruct.new(
:catalogProductInfo => OpenStruct.new(
:productType => 'Book',
:productName => 'The Client',
:productAuthorized => 'Y',
:productId => 'BKSUS113246A',
:productVerificationCode => '4546747',
:productPurcTransactionTime => '2012-05-21T13:36:38+05:30',
:productAuditDetails => OpenStruct.new(
:productAuditNo => '1',
:prodHandledByUser => 'StoreUserS14',
:productAuditTime => '2012-05-21T13:36:38+05:30',
:productAuditAdminId => 'McGr1132',
:productPurchaseRate => '50.14 Prcnt',
:productSystemLoggerId => 'UNX-NETW4536'
),
:productAuditDetails => OpenStruct.new(
:productAuditNo => '2',
:prodHandledByUser => 'OnlineUserOn008',
:productAuditTime => '2012-05-23T16:16:08+05:30',
:productAuditAdminId => 'McGr1132',
:productPurchaseRate => '84.86 Prcnt',
:productSystemLoggerId => 'UNX-NETW4536'
)
),
:catalogProductInfo => OpenStruct.new(
:productType => 'Pen',
:productName => 'Reynolds'
:productAuthorized => 'N',
:productId => 'PNSUS228886B',
:productVerificationCode => '2330076',
:productPurcTransactionTime => '2012-04-22T15:06:18+04:30',
:productAuditDetails => OpenStruct.new(
:productAuditNo => '1',
:prodHandledByUser => 'CCUserA14',
:productAuditTime => '2012-04-26T13:36:38+05:30',
:productAuditAdminId => 'ReyGr1132',
:productPurchaseRate => '20.19 Prcnt',
:productSystemLoggerId => 'WIN-NETW4536'
)
)
)
)]
I tried with below code .. as per your answer (handpick of elements)
builder = Nokogiri::XML::Builder.new do |xml|
xml.CatalogOrder do
collection.each do |ctlg|
xml.CatalogStoreNumber ctlg.catalogStoreNumber
xml CatalogStoreId ctlg.catalogStoreId
xml.CatalogOwnerId ctlg.catalogOwnerid
xml.CatalogOwner ctlg.catalogOwner
xml.CatalogList do
prod_count = 0
aud_list_count = 0
collection.each do |prod|
info = prod.catalogList[0].catalogProductInfo
xml.ProductInfo do
xml.ProductType info.productType
xml.ProductName info.productName
xml.ProductId info.productId
xml.ProductVerificationCode info.ProductVerificationCode
xml.ProductPurcTransactionTime info.productPurcTransactionTime
xml.ProductAuditDetails do
collection.each do |aud_dtl|
aud_info = aud_dtl.catalogList[0].catalogProductinfo[0].productAuditDetails
xml.ProductAuditNo aud_info.productAuditNo
xml.ProdHandledByUser aud_info.prodHandledByUser
xml.ProductAuditTime aud_info.productAuditTime
xml.ProductAuditAdminId aud_info.productAuditAdminId
xml.ProductPurchaseRate aud_info.productPurchaseRate
xml.ProductSystemLoggerId aud_info.productSystemLoggerId
# Do whatever you must above to concoct your ProductId
end
aud_list_count = aud_list_count + 1
end
prod_count = prod_count + 1
end
end
end
puts builder.to_xml
I need the Output as below...
<CatalogOrder>
<CatalogStoreNumber>657758</CatalogStoreNumber>
<CatalogStoreId>CTH6536</CatalogStoreId>
<CatalogStoreLocation>UnitedStates</CatalogStoreLocation>
<CatalogOwnerId>TYCT11190</CatalogOwnerId>
<CatalogOwner>McGrawHill Pub.</CatalogOwner>
<CatalogList>
<CatalogProductInfo>
<ProductType>Book</ProductType>
<ProductName>The Client</ProductName>
<ProductAuthorized>Y</ProductAuthorized>
<ProductId>BKSUS113246A</ProductId>
<ProductVerificationCode>4546747</ProductVerificationCode>
<ProductPurcTransactionTime>2012-05-21T13:36:38+05:30</ProductPurcTransactionTime>
<ProductAuditDetails>
<ProductAuditNo>1</ProductAuditNo>
<ProdHandledByUser>StoreUserS14</ProdHandledByUser>
<ProductAuditTime>2012-05-21T13:36:38+05:30</ProductAuditTime>
<ProductAuditAdminId>McGr1132</ProductAuditAdminId>
<ProductPurchaseRate>50.14 Prcnt</ProductPurchaseRate>
<ProductSystemLoggerId>WIN-NETW4536</ProductSystemLoggerId>
</ProductAuditDetails>
<ProductAuditDetails>
<ProductAuditNo>2</ProductAuditNo>
<ProdHandledByUser>OnlineUserOn008</ProdHandledByUser>
<ProductAuditTime>2012-05-23T16:16:08+05:30</ProductAuditTime>
<ProductAuditAdminId>McGr1132</ProductAuditAdminId>
<ProductPurchaseRate>84.86 Prcnt</ProductPurchaseRate>
<ProductSystemLoggerId>UNX-NETW4536</ProductSystemLoggerId>
</ProductAuditDetails>
</CatalogProductInfo>
<CatalogProductInfo>
<ProductType>Pen</ProductType>
<ProductName>Reynolds</ProductName>
<ProductAuthorized>N</ProductAuthorized>
<ProductId>PNSUS228886B</ProductId>
<ProductVerificationCode>2330076</ProductVerificationCode>
<ProductPurcTransactionTime>2012-04-22T15:06:18+04:30</ProductPurcTransactionTime>
<ProductAuditDetails>
<ProductAuditNo>1</ProductAuditNo>
<ProdHandledByUser>CCUserA14</ProdHandledByUser>
<ProductAuditTime>2012-04-26T13:36:38+05:30</ProductAuditTime>
<ProductAuditAdminId>ReyGr1132</ProductAuditAdminId>
<ProductPurchaseRate>20.19 Prcnt</ProductPurchaseRate>
<ProductSystemLoggerId>WIN-NETW4536</ProductSystemLoggerId>
</ProductAuditDetails>
</CatalogProductInfo>
</CatalogList>
</CatalogOrder>
I tried to loop in the nested Array of OpenStruct of elements, but couldn't land on right logic for that ..
Ref.. How to add child nodes in NodeSet using Nokogiri
Setup Code
require 'ostruct'
require 'nokogiri'
collection = [
OpenStruct.new(
:catalogStoreNumber => '657758',
:catalogStoreId => 'CTH6536',
:catalogStoreLocation => 'UnitedStates',
:catalogOwnerId => 'TYCT11190',
:catalogOwner => 'McGrawHill Pub.',
:catalogList => OpenStruct.new(
:catalogProductInfo => OpenStruct.new(
:productType => 'Book',
:productName => 'The Client'
)
)
)
]
If You Want To Hand-Pick Your Elements and Data
builder = Nokogiri::XML::Builder.new do |xml|
xml.CatalogOrder do
xml.CatalogList do
collection.each do |prod|
info = prod.catalogList.catalogProductInfo
xml.ProductInfo do
xml.ProductType info.productType
xml.ProductName info.productName
xml.ProductId "#{prod.catalogOwnerId}-#{prod.catalogStoreNumber}"
# Do whatever you must above to concoct your ProductId
end
end
end
end
end
puts builder.to_xml
Output
<?xml version="1.0"?>
<CatalogOrder>
<CatalogList>
<ProductInfo>
<ProductType>Book</ProductType>
<ProductName>The Client</ProductName>
<ProductId>TYCT11190-657758</ProductId>
</ProductInfo>
</CatalogList>
</CatalogOrder>
If you want a more generic conversion (an XML representation of your OpenStruct hierarchy) see either of the two solutions below:
One Way to Perform Generic Conversion
# Add all entries of an OpenStruct to an XML builder
# Recursively creates sub-nodes for OpenStruct instances
def ostruct_each(ostruct,xml)
ostruct.instance_variable_get(:#table).each do |field,value|
if value.is_a?(OpenStruct)
xml.send(field) do
ostruct_each(value,xml)
end
else
xml.send(field,value)
end
end
end
builder = Nokogiri::XML::Builder.new do |xml|
xml.CatalogOrder do
xml.CatalogList do
collection.each do |prod_info|
xml.ProductInfo do
ostruct_each(prod_info,xml)
end
end
end
end
end
puts builder.to_xml
Output
<?xml version="1.0"?>
<CatalogOrder>
<CatalogList>
<ProductInfo>
<catalogStoreNumber>657758</catalogStoreNumber>
<catalogStoreId>CTH6536</catalogStoreId>
<catalogStoreLocation>UnitedStates</catalogStoreLocation>
<catalogOwnerId>TYCT11190</catalogOwnerId>
<catalogOwner>McGrawHill Pub.</catalogOwner>
<catalogList>
<catalogProductInfo>
<productType>Book</productType>
<productName>The Client</productName>
</catalogProductInfo>
</catalogList>
</ProductInfo>
</CatalogList>
</CatalogOrder>
A Different Way to Generically Convert It
# Create a NodeSet of elements for all attributes in an OpenStruct
# Recursively creates child elements for any value that is an OpenStruct
def ostruct_to_elements(xml_doc,ostruct)
Nokogiri::XML::NodeSet.new(
xml_doc,
ostruct.instance_variable_get(:#table).map do |name,val|
xml_doc.create_element(name.to_s).tap do |el|
el << (val.is_a?(OpenStruct) ? ostruct_to_elements(xml_doc,val) : val)
end
end
)
end
builder = Nokogiri::XML::Builder.new do |xml|
xml.CatalogOrder do
xml.CatalogList do
collection.each do |prod_info|
xml.ProductInfo do
xml.parent << ostruct_to_elements(xml.doc,prod_info)
end
end
end
end
end
puts builder.to_xml
Answering your changed question and data.
Here's your source data, summarized:
collection = [
OpenStruct.new(
:foo => 'bar',
:list => OpenStruct.new(
:catalogProductInfo => OpenStruct.new(...)
:catalogProductInfo => OpenStruct.new(...)
)
)
]
The first thing we notice is that collection is an array, but it only has one item. That doesn't seem very useful.
The second—far more important—thing to notice is that you are attempting to use an OpenStruct (the inner one) as an array. This fails quite fully, as the second value completely overwrites the first:
require 'ostruct'
p OpenStruct.new( a:1, a:2 )
#=> #<OpenStruct a=2>
You can't have two values for the same key in a struct. Instead, you probably wanted an array value inside your object. For example:
root = OpenStruct.new(
:foo => 'bar',
:list => [ # this is an array of two distinct objects
OpenStruct.new( :catalogProductInfo => OpenStruct.new(...) ),
OpenStruct.new( :catalogProductInfo => OpenStruct.new(...) )
]
)
Thirdly, as I noted earlier, I see no good reason for you to be using an OpenStruct here. Instead, just use Hash literals. Combined with the code I provided in my other answer, this is what your data (simplified) and a working solution looks like:
Source Data
MyOrder = {
catalogStoreNumber: '657758', # Ruby 1.9 Hash syntax;
catalogStoreId: 'CTH6536', # same as :catalogStoreId => 'CTH536'
catalogList: {
catalogProductInfo: [
{
productType: 'Book',
productName: 'The Client',
productAuditDetails: [
{ productAuditNo: '1', prodHandledByUser: 'StoreUserS14' },
{ productAuditNo: '2', prodHandledByUser: 'OnlineUserOn008' }
]
},
{
productType: 'Pen',
productName: 'Reynolds',
productAuditDetails: [
{ productAuditNo: '1', prodHandledByUser: 'CCUserA14' }
]
}
]
}
}
Generic Conversion
# Adds key/value pairs from a Hash to a Nokogiri::XML::Builder
def hash2xml(hash,xml)
hash.each do |field,value|
name = field.to_s.sub(/^./,&:upcase) # convert "fooBar" to "FooBar"
case value
when Hash then xml.send(name){ hash2xml(value,xml) }
when Array then value.each{ |o| xml.send(name){ hash2xml(o,xml) } }
else xml.send(name,value)
end
end
end
Working Code
builder = Nokogiri::XML::Builder.new do |xml|
xml.CatalogOrder do
hash2xml(MyOrder,xml)
end
end
puts builder.to_xml
Output
<?xml version="1.0"?>
<CatalogOrder>
<CatalogStoreNumber>657758</CatalogStoreNumber>
<CatalogStoreId>CTH6536</CatalogStoreId>
<CatalogList>
<CatalogProductInfo>
<ProductType>Book</ProductType>
<ProductName>The Client</ProductName>
<ProductAuditDetails>
<ProductAuditNo>1</ProductAuditNo>
<ProdHandledByUser>StoreUserS14</ProdHandledByUser>
</ProductAuditDetails>
<ProductAuditDetails>
<ProductAuditNo>2</ProductAuditNo>
<ProdHandledByUser>OnlineUserOn008</ProdHandledByUser>
</ProductAuditDetails>
</CatalogProductInfo>
<CatalogProductInfo>
<ProductType>Pen</ProductType>
<ProductName>Reynolds</ProductName>
<ProductAuditDetails>
<ProductAuditNo>1</ProductAuditNo>
<ProdHandledByUser>CCUserA14</ProdHandledByUser>
</ProductAuditDetails>
</CatalogProductInfo>
</CatalogList>
</CatalogOrder>
I'm looking for a solution how to write the format function which will take a string or nested hash as an argument and return the flatten version of it with the path as a key.
arg = "foo"
format(arg) # => { "hash[keys]" => "foo" }
arg = {:a => "foo", :b => { :c => "bar", :d => "baz" }}
format(arg) # => { "hash[keys][a]" => "foo", "hash[keys][b][c]" => "bar", "hash[keys][b][d]" => "baz" }
def hash_flatten h
h.inject({}) do |a,(k,v)|
if v.is_a?(Hash)
hash_flatten(v).each do |sk, sv|
a[[k]+sk] = sv
end
else
k = k ? [k] : []
a[k] = v
end
a
end
end
def format h
if h.is_a?(Hash)
a = hash_flatten(h).map do |k,v|
key = k.map{|e| "[#{e}]"}.join
"\"event[actor]#{key}\" => \"#{v}\""
end.join(', ')
else
format({nil => h})
end
end
arg = "sth"
puts format(arg)
# => "event[actor]" => "sth"
arg = {:a => "sth", :b => { :c => "sth else", :d => "trololo" }}
puts format(arg)
# => "event[actor][a]" => "sth", "event[actor][b][c]" => "sth else", "event[actor][b][d]" => "trololo"
Let's say I have an arbitrarily deep nested Hash h:
h = {
:foo => { :bar => 1 },
:baz => 10,
:quux => { :swozz => {:muux => 1000}, :grimel => 200 }
# ...
}
And let's say I have a class C defined as:
class C
attr_accessor :dict
end
How do I replace all nested values in h so that they are now C instances with the dict attribute set to that value? For instance, in the above example, I'd expect to have something like:
h = {
:foo => <C #dict={:bar => 1}>,
:baz => 10,
:quux => <C #dict={:swozz => <C #dict={:muux => 1000}>, :grimel => 200}>
# ...
}
where <C #dict = ...> represents a C instance with #dict = .... (Note that as soon as you reach a value which isn't nested, you stop wrapping it in C instances.)
def convert_hash(h)
h.keys.each do |k|
if h[k].is_a? Hash
c = C.new
c.dict = convert_hash(h[k])
h[k] = c
end
end
h
end
If we override inspect in C to give a more friendly output like so:
def inspect
"<C #dict=#{dict.inspect}>"
end
and then run with your example h this gives:
puts convert_hash(h).inspect
{:baz=>10, :quux=><C #dict={:grimel=>200,
:swozz=><C #dict={:muux=>1000}>}>, :foo=><C #dict={:bar=>1}>}
Also, if you add an initialize method to C for setting dict:
def initialize(d=nil)
self.dict = d
end
then you can reduce the 3 lines in the middle of convert_hash to just h[k] = C.new(convert_hash_h[k])
class C
attr_accessor :dict
def initialize(dict)
self.dict = dict
end
end
class Object
def convert_to_dict
C.new(self)
end
end
class Hash
def convert_to_dict
Hash[map {|k, v| [k, v.convert_to_dict] }]
end
end
p h.convert_to_dict
# => {
# => :foo => {
# => :bar => #<C:0x13adc18 #dict=1>
# => },
# => :baz => #<C:0x13adba0 #dict=10>,
# => :quux => {
# => :swozz => {
# => :muux => #<C:0x13adac8 #dict=1000>
# => },
# => :grimel => #<C:0x13ada50 #dict=200>
# => }
# => }