Given a simple custom generator:
# lib/generators/custom_model/custom_model_generator.rb
class CustomModelGenerator < Rails::Generators::NamedBase
def rails_generate_model
generate 'model', "#{file_name} #{args.join(' ')}"
end
end
Which is used like so:
$ rails generate custom_model ModelName
How can i define the destroy behavior for this custom generator?
$ rails destroy custom_model ModelName
Actually, my problem is that this generator use the generate method to use an existing rails generator. But i couldn't find any method to reverse what this generate did.
I used to use this for my own generators (which doesn't call any existing generator), and write my own "destroy" routines:
case self.behavior
when :invoke
# do that stuff
when :revoke
# undo it!
end
I red a lot about this accross the web, but nothing relevant or up-to-date. So any advices are more than welcome.
Thanks for reading.
You can use the following piece of code (of course you can replace :scaffold with any other generator):
case self.behavior
when :invoke
generate :scaffold, "#{file_name} #{attributes}"
# Or equally:
# Rails::Generators.invoke :scaffold, args, :behavior => :invoke
when :revoke
Rails::Generators.invoke :scaffold, [file_name], :behavior => :revoke
end
Related
I have a function which validates a model class where it has to check every member of the class to be non-null or non-empty in case of a String.
The logic for this is_complete function is something like this:
def is_complete(profile):
if profile.first_name in (None, ''):
return False
elif profile.last_name in (None, ''):
return False
elif profile.dob is None:
return False
.
.
.# and checks so on for all members of the profile instance
.
.
return True
My question since the number of possible paths the execution can take is quite large and increases in proportion to the number of member variables of profile to be checked, how does one reliably write tests for all the possible paths?
Right now, I have two simple test cases:
where only some of the members are set and checks assertFalse(is_complete(foo))
where all the members are set and checks assertTrue(is_complete(foo))
But I have a feeling that this may not be enough.
I'm not sure what you mean by having MxN paths. In the posted code you have as many paths as fields + 1.
Create a helper method that creates a profile that is complete and passes is_complete.
Add a test method to verify is_complete(profile) is True for the complete profile.
Add one test method for each field, with the following steps:
Call the helper method to create a complete profile that we know would pass is_complete
Break the field under test
Verify that is_complete returns False
You will have as many test methods as fields + 1.
Btw, instead of this:
if profile.first_name in (None, ''):
You can writer simpler:
if not profile.first_name:
You can test this with randomization:
Write a function that creates a random Profile object with all the fields filled (could be random, but valid).
Randomly change one or more fields to either None or empty
Pass the object to the is_complete() function.
I can show this only in Java with Qala Datagen. I assume you're interested in the negative paths, so it can look like this:
#Test void profileIsNotComplete_ifAtLeastOneFieldIsBlank() {
Profile p = Profile.random();
callOneOrMore(
() -> profile.setName(nullOrEmpty()),
() -> profile.setLastName(nullOrEmpty()),
...);
assertFalse(profile.isComplete();
}
Note, that this code actually tests more - it also checks the combination of fields set to null/empty. If in Python there are no such libs, you can write some utility methods for yourself.
NB: only one path is going to be tested per one execution. You can run it many times (a thousand?) to make sure that all the paths pass. Then in CI you can run it only once if this is not a mission-critical functionality and you're not afraid that it's going to break often.
Otherwise if you really want this to be covered 100% for every invocation, you can violate some of the good practices and just combine all these tests in one (otherwise there are going to be too many tests which complicate reading them):
#Test void profileIsNotComplete_ifOneFieldIsBlank() {
assertFalse(Profile.random().setName(null).isComplete());
assertFalse(Profile.random().setName("").isComplete());
assertFalse(Profile.random().setLastName(null).isComplete());
assertFalse(Profile.random().setLastName("").isComplete());
...
}
This doesn't test combinations though, but you can combine both approaches. The positive case is very easy in both approaches:
#Test void profileIsComplete_ifAllFieldsAreFilled() {
assertTrue(Profile.random());
}
More info on randomized testing is here.
i am using
parser = argparse.ArgumentParser()
subparser = parser.add_subparsers()
add_parser = subparsers.add_parser("add", help="Add parser")
add_parser.add_argument("-project", default="global")
edit_parser = subparsers.add_parser("edit", help="Edit parser")
I want to achieve smething like this:
python myprogram.py add
python myprogram.py edit
python myprogram.py "A random string"
Where in the first two usecases my program can asign the right subparser based on the keyword "add" or "edit". I am interested in the last case, where it maps to any random string that i provide.
There isn't a direct way of doing this. The use of subparsers is an extension of a positional argument with choices.
parser.add_argument('cmd', choices=['add', 'edit', ...])
It accepts a string if it is in choices. There isn't a builtin pattern matching mechanism.
I think your simplest solution would be to add one step:
custom = subparser.add_parser('custom', help='give a random string')
custom.add_argument('anystring', help='random quoted string')
python myprogram.py custom 'any random string'
I can imagine writing a custom choices class that could do both kinds of matching. It would require a custom __contains__ method (and maybe a new __iter__ to list choices in the help). But incorporating that into the _SubParsersAction class would require some serious coding.
A few more details. Your subparser object is an _SubParsersAction. Its choices attribute is an OrderedDict. When you add_parser, it creates a new ArgumentParser (the add_parser object), and enters it into the dict with the named key (plus any aliases). When parsing your input, argparse matches the strings against the keys of this dictionary. So, to implement your ideal, you'd have to change this dictionary lookup.
Another option is to look at sys.argv[1:] at the start. If one of your 'cmds' is present call the parser to get the full subparser action. Otherwise handle the inputs yourself, or call another parser that handles 'a random string' as an ordinary positional argument. Sometimes it just isn't worth the effort to twist argparse into a special shape.
I am using annotate in my app and all models are successfully annotated except for user.rb, which shows the following error when I annotate:
Unable to annotate user.rb: wrong number of arguments (0 for 1)
Outside of annotating, everything else works fine. User creation, updating, deletion, login, sign out, it all works properly. I have determined that the problem is with the Digest::SHA1, which I use to create session tokens, as demonstrated below in the snippet from user.rb.
def User.new_remember_token
SecureRandom.urlsafe_base64
end
def User.hash(token)
Digest::SHA1.hexdigest(token.to_s)
end
private
def create_remember_token
remember_token = User.hash(User.new_remember_token)
end
If I remove the second (def User.hash(token)) and instead do the following:
def User.new_remember_token
SecureRandom.urlsafe_base64
end
private
def create_remember_token
remember_token = Digest::SHA1.hexdigest(User.new_remember_token.to_s)
end
then annotate is happy and successfully annotates user.rb. However, this isn't really the ruby way as my session helper utilizes that User.hash(token) call several times. What am I not understanding about Digest::SHA1.hexdigest or the way that I am utilizing it?
Looks like you're working through The Rails Tutorial.
The likely reason you're seeing issues with your User.hash method is nothing to do with Digest::SHA1, but is because the method itself is inadvertently overriding Ruby's Object#hash method, which is giving you some cryptic errors. Link to Github issue about it.
So, like this commit to the Sample App repository, rename all your instances of User.hash to User.digest and hopefully that should fix your errors.
Yea, I know that this question is silly, newbee and simple, but I still can't figure it out.
I've created a class (in app/minions/ directory) to parse auth hashes from 3rd-party services (like google, twitter, etc.). It looks like this.
class AuthHash
def initialize(hash)
#hash = hash
#provider = hash[:provider]
#uid = hash[:uid]
create_user_hash
end
def create_user_hash
#user_hash = send("parse_hash_from_" << #hash[:provider], #hash)
end
def credentials
{provider: #provider, uid: #uid}
end
def user_hash
#user_hash
end
private
# parse_hash_from_* methods here
end
I've added that directory to the autoload path, so I can use it in my controllers. Now I want to write some tests for it.
I'm using RSpec with FactoryGirl for testing. So I started by adding a factory to spec/factories/ called auth_hashes.rb but I can't define a hash as a field in a factory.
So I moved the declaration to the spec/minions/auth_hash_spec.rb.
require 'spec_helper'
describe AuthHash do
before_each do
auth_hash = AuthHash.new({:provider=>"google_oauth2",:uid=>"123456789",:info=>{:name=>"JohnDoe",:email=>"john#company_name.com",:first_name=>"John",:last_name=>"Doe",:image=>"https://lh3.googleusercontent.com/url/photo.jpg"},:credentials=>{:token=>"token",:refresh_token=>"another_token",:expires_at=>1354920555,:expires=>true},:extra=>{:raw_info=>{:id=>"123456789",:email=>"user#domain.example.com",:verified_email=>true,:name=>"JohnDoe",:given_name=>"John",:family_name=>"Doe",:link=>"https://plus.google.com/123456789",:picture=>"https://lh3.googleusercontent.com/url/photo.jpg",:gender=>"male",:birthday=>"0000-06-25",:locale=>"en",:hd=>"company_name.com"}}})
end
end
But still it does not seem to work.
I know this should be alot simpler then I'm trying to do, but I can't figure it out.
Add something like this to that new spec (spec/minions/auth_hash_spec.rb) file at the top:
require Rails.root.to_s + '/app/minions/myhash.rb'
And then write your tests.
I am trying to use Minitest in a fresh Rails 4 install. My understanding is that if I have a class that doesn't inherit from ActiveRecord then I should be able to use Minitest itself, without Rails integration:
#test/models/blog.rb
require "minitest/autorun"
class Blog < Minitest::Unit::TestCase
def setup
#b = Blog.new
end
def test_entries
assert_empty "message", #b.entries
end
#app/models/blog.rb
class Blog
attr_reader :entries
def initialize
#entries = []
end
I run the test with ruby test/models/blog.rb.
My problem comes with the setup method. If I don't include an entry for my blog, the tests fails with the message that there are the wrong number of arguments in setup. If I include an entry in my setup message #b = Blog.new entries: "Twilight", my test fails in the test_entries method because entries is an undefined method.
You have a couple problems. First, you are not requiring "test_helper", which means that rails isn't getting loaded when you run this test, which means that the mechanism rails uses to resolve missing constants isn't loaded. You will either need to require the helper or require the blog file directly. Second, you are overwriting the constant you want to test with the test, which is why you are getting confusing messages. Name the test class BlogTest instead to avoid this.
This is what I think you are trying to do:
require "minitest/autorun"
require "models/blog" # Assuming "app" is in your load path when running the test
#require "test_helper" # Or require this instead if you need to use DB
class BlogTest < Minitest::Unit::TestCase
def setup
#b = Blog.new
end
def test_entries
assert_empty #b.entries, "Blog entries should be empty"
end
end