Given:
class Thing
def initialize(object)
#object = object
end
end
items = [1,2,3]
I'd like to know of a more elegant way to convert each item to the Thing than this:
items.map{ |item| Thing.new item }
# => [<Thing #object=1>, <Thing #object=2>, <Thing #object=3>]
You can use the unary prefix & operator:
items.map(&Thing.method(:new))
I have suggested that Classes should behave as Factory Functions, which would allow you to write it like this:
items.map(&Thing)
However, there doesn't seem to be much interest in the proposal. You can monkey-patch it yourself, though, the implementation is trivial:
class Class
def to_proc
method(:new).to_proc
end
end
I would argue that your example is perfectly fine. But perhaps you like something like this:
# in item.rb
def to_thing
Thing.new(self)
end
That would allow you to write:
items.map(&:to_thing)
Related
My app passes to different methods a json_element for which the keys are different, and sometimes empty.
To handle it, I have been hard-coding the extraction with the following sample code:
def act_on_ruby_tag(json_element)
begin
# logger.progname = __method__
logger.debug json_element
code = json_element['CODE']['$'] unless json_element['CODE'].nil?
predicate = json_element['PREDICATE']['$'] unless json_element['PREDICATE'].nil?
replace = json_element['REPLACE-KEY']['$'] unless json_element['REPLACE-KEY'].nil?
hash = json_element['HASH']['$'] unless json_element['HASH'].nil?
I would like to eliminate hardcoding the values, and not quite sure how.
I started to think through it as follows:
keys = json_element.keys
keys.each do |k|
set_key = k.downcase
instance_variable_set("#" + set_key, json_element[k]['$']) unless json_element[k].nil?
end
And then use #code for example in the rest of the method.
I was going to try to turn into a method and then replace all this hardcoded code.
But I wasn't entirely sure if this is a good path.
It's almost always better to return a hash structure from a method where you have things like { code: ... } rather than setting arbitrary instance variables. If you return them in a consistent container, it's easier for callers to deal with delivering that to the right location, storing it for later, or picking out what they want and discarding the rest.
It's also a good idea to try and break up one big, clunky step with a series of smaller, lighter operations. This makes the code a lot easier to follow:
def extract(json)
json.reject do |k, v|
v.nil?
end.map do |k, v|
[ k.downcase, v['$'] ]
end.to_h
end
Then you get this:
extract(
'TEST' => { '$' => 'value' },
'CODE' => { '$' => 'code' },
'NULL' => nil
)
# => {"test"=>"value", "code"=>"code"}
If you want to persist this whole thing as an instance variable, that's a fairly typical pattern, but it will have a predictable name that's not at the mercy of whatever arbitrary JSON document you're consuming.
An alternative is to hard-code the keys in a constant like:
KEYS = %w[ CODE PREDICATE ... ]
Then use that instead, or one step further, define that in a YAML or JSON file you can read-in for configuration purposes. It really depends on how often these will change, and what sort of expectations you have about the irregularity of the input.
This is a slightly more terse way to do what your original code does.
code, predicate, replace, hash = json_element.values_at *%w{
CODE PREDICATE REPLACE-KEY HASH
}.map { |x| x.fetch("$", nil) if x }
I want to check that an array contains only objects of a specific class, let's say Float.
A working example at the moment:
it "tests array_to_test class of elements" do
expect(array_to_test.count).to eq(2)
expect(array_to_test[0]).to be_a(Float)
expect(array_to_test[1]).to be_a(Float)
end
Is there a way to validate if the array_to_test contains only Float instances?
Sample non-working pseudocode:
it "tests array_to_test class of elements" do
expect(array_to_test).to be_a(Array[Float])
end
Do not consider Ruby and Rspec version as a restriction.
Try all:
expect(array_to_test).to all(be_a(Float))
You could use ruby methods:
expect(array_to_test.map(&:class).uniq.length) to eq(1)
Or for better practice, implement a Helper using those methods:
RSpec::Matchers.define :all_be_same_type do
match do |thing|
thing.map(&:class).uniq.length == 1
end
end
and then use it this way:
expect(array_to_test) to all_be_same_type
I try to set an NSTabView delegate using MacRuby with XCode, but I can't figure how to write the delegate. I use:
def intialize
#tab_changed.delegate = self
end
def tabViewdidSelectTabViewItem(a_notification)
puts "tab has changed"
end
Then in the .xib, I hook the NSTab view element with the class, but nothing happen when I select some tabs.
Usually the delegate are very easy to use, but this one has a syntax like this :
tabView:didSelectTabViewItem:
and I don't know how to write this in MacRuby. Should I use tabViewdidSelectTabViewItem or tabView_didSelectTabViewItem (none of them works).
Thanks for your help.
Assuming this is being done in a ViewController, instead of using initialize, better to do things the Cocoa way and use a method like viewDidLoad.
def viewDidLoad
#tab_changed.delegate = self
end
The signature for the delegate method you want is -(void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem. In MacRuby, that would be represented like this:
def tabView(tabView, didSelectTabViewItem: tabViewItem)
puts "tab has changed"
end
In Google App Engine, I make lists of referenced properties much like this:
class Referenced(BaseModel):
name = db.StringProperty()
class Thing(BaseModel):
foo_keys = db.ListProperty(db.Key)
def __getattr__(self, attrname):
if attrname == 'foos':
return Referenced.get(self.foo_keys)
else:
return BaseModel.__getattr__(self, attrname)
This way, someone can have a Thing and say thing.foos and get something legitimate out of it. The problem comes when somebody says thing.foos.append(x). This will not save the added property because the underlying list of keys remains unchanged. So I quickly wrote this solution to make it easy to append keys to a list:
class KeyBackedList(list):
def __init__(self, key_class, key_list):
list.__init__(self, key_class.get(key_list))
self.key_class = key_class
self.key_list = key_list
def append(self, value):
self.key_list.append(value.key())
list.append(self, value)
class Thing(BaseModel):
foo_keys = db.ListProperty(db.Key)
def __getattr__(self, attrname):
if attrname == 'foos':
return KeyBackedList(Thing, self.foo_keys)
else:
return BaseModel.__getattr__(self, attrname)
This is great for proof-of-concept, in that it works exactly as expected when calling append. However, I would never give this to other people, since they might mutate the list in other ways (thing[1:9] = whatevs or thing.sort()). Sure, I could go define all the __setslice__ and whatnot, but that seems to leave me open for obnoxious bugs. However, that is the best solution I can come up with.
Is there a better way to do what I am trying to do (something in the Python library perhaps)? Or am I going about this the wrong way and trying to make things too smooth?
If you want to modify things like this, you shouldn't be changing __getattr__ on the model; instead, you should write a custom Property class.
As you've observed, though, creating a workable 'ReferenceListProperty' is difficult and involved, and there are many subtle edge cases. I would recommend sticking with the list of keys, and fetching the referenced entities in your code when needed.
How do I figure out if an array contains an element?
I thought there might be something like [1, 2, 3].includes(1) which would evaluate as true.
Some syntax sugar
1 in [1,2,3]
.contains() is the best method for lists, but for maps you will need to use .containsKey() or .containsValue()
[a:1,b:2,c:3].containsValue(3)
[a:1,b:2,c:3].containsKey('a')
For lists, use contains:
[1,2,3].contains(1) == true
If you really want your includes method on an ArrayList, just add it:
ArrayList.metaClass.includes = { i -> i in delegate }
You can use Membership operator:
def list = ['Grace','Rob','Emmy']
assert ('Emmy' in list)
Membership operator Groovy
IMPORTANT Gotcha for using .contains() on a Collection of Objects, such as Domains. If the Domain declaration contains a EqualsAndHashCode, or some other equals() implementation to determine if those Ojbects are equal, and you've set it like this...
import groovy.transform.EqualsAndHashCode
#EqualsAndHashCode(includes = "settingNameId, value")
then the .contains(myObjectToCompareTo) will evaluate the data in myObjectToCompareTo with the data for each Object instance in the Collection. So, if your equals method isn't up to snuff, as mine was not, you might see unexpected results.
def fruitBag = ["orange","banana","coconut"]
def fruit = fruitBag.collect{item -> item.contains('n')}
I did it like this so it works if someone is looking for it.
You can also use matches with regular expression like this:
boolean bool = List.matches("(?i).*SOME STRING HERE.*")