grails plugins lister

import urllib2
import threading
from sgmllib import SGMLParser

class URLLister(SGMLParser):
    def reset(self):
        SGMLParser.reset(self)
        self.urls = []
    def start_a(self, attrs):
        if attrs[0][0] == 'href': self.urls.append(attrs[0][1])

class GrailsPluginsLister(URLLister):
    def getFileDetail(self, url):
        print 'starting get', url
        try:
            stream = urllib2.urlopen(url)
            data = stream.read()
            parser = URLLister()
            parser.feed(data)
            self.fileDetails.extend([url+i for i in parser.urls if i.endswith('.zip')])
            print url, 'acquired'
        except IOError:
            self.failed.append(url)            
    def getUrls(self):
        data = ['http://plugins.grails.org/'+i+'trunk/' for i in self.urls[1:-1]]
        handlers = []
        self.fileDetails = []
        self.failed = []
        print 'wait.....'
        for url in data:
            handlers.append(threading.Thread(target=self.getFileDetail, args=(url,)))
        print 'loading...'
        for i in handlers:
            i.start()
        for i in handlers:
            i.join()
        print 'done.....'
    def getUrls1(self):
        data = ['http://plugins.grails.org/'+i+'trunk/' for i in self.urls[1:-1]]
        self.fileDetails = []
        self.failed = []
        for url in data:
            self.getFileDetail(url)

if __name__ == '__main__':
    print 'hello'
    stream = urllib2.urlopen('http://plugins.grails.org/')
    print 'stream lister'
    data = stream.read()
    parser = GrailsPluginsLister()
    parser.feed(data)
    parser.getUrls()
    print parser.fileDetails
    print 'failed:', parser.failed

my searchable on grails

Within grails, Book class-domain is very common used as sample. And here is my simple implementation of searchable plugin on Book class-domain:

Book.groovy

class Book {

String bookName
Date release
String description

static Searchable = true

}

BookController.groovy

def search = {

   def bookInstanceList
    def searchResult
    if (params.query) {
        searchResult = Book.search('*'+params.query+'*')
        bookInstanceList = searchResult.results
    }
    else
        bookInstanceList = Book.list()
    render(view:'list', model:[bookInstanceList:bookInstanceList, searchResult:searchResult, query:params.query])
}

List view

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <meta name="layout" content="main" />
        <title>Book List</title>
    </head>
    <body>
        <div class="nav">
            <span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
            <span class="menuButton"><g:link class="create" action="create">New Book</g:link></span>
        </div>
        <div class="body">
            <h1>Book List</h1>
            <g:if test="${flash.message}">
                <div class="message">${flash.message}</div>
            </g:if>
            <div class="list">
                <div class="nav">
                    <g:form name="search" action="search">
                        search: <input type="text" name="query" value="${query}"/>
                        <input type="submit" value="search">
                    </g:form>
                </div>
                <table>
                    <thead>
                        <tr>
                            <g:sortableColumn property="id" title="Id" />
                            <g:sortableColumn property="bookName" title="Book Name" />
                            <g:sortableColumn property="description" title="Description" />
                            <g:sortableColumn property="release" title="Release" />
                        </tr>
                    </thead>
                    <tbody>
                        <g:each in="${bookInstanceList}" status="i" var="bookInstance">
                            <tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
                                <td><g:link action="show" id="${bookInstance.id}">${fieldValue(bean:bookInstance, field:'id')}</g:link></td>
                                <td>${fieldValue(bean:bookInstance, field:'bookName')}</td>
                                <td>${fieldValue(bean:bookInstance, field:'description')}</td>
                                <td>${fieldValue(bean:bookInstance, field:'release')}</td>
                            </tr>
                        </g:each>
                    </tbody>
                </table>
            </div>
            <div class="paginateButtons">
                <g:if test="${searchResult}">
                    <g:paginate total="${searchResult.total}" params="[    query:query]"/>
                </g:if>
                <g:else>
                    <g:paginate total="${Book.count()}" />
                </g:else>
            </div>
        </div>
    </body>
</html>

and here is the shoot:

searchable plugin on book domain-class

searchable plugin on book domain-class

The search criteria above is based on word match, for more details about more search criteria or all features on grails searchable please see the documentation on grails plugins site