RubyOnRails (1.1) and Flex (2.0): Pt 2

May 6th, 2006

UPDATE: Continued from part 1

20 days!! That’s terrible of me, I didn’t mean to be this long in putting up the second part of ths tutorial but work has been madness and I just want to go home and sleep every day.

Still here we are, thanks for everyone who commented on the tutorial especially Mikey Jones for linking to it and Alex MacCaw for taking Up Flex and Rails so enthusiastically.

I think Ruby On Rails is a good choice for your hobbist Flex development, easier to learn for all the Flash Gurus out there and much quicker to learn and robust than PHP.

So in this part i’m going to look at a few new, more advanced things in Flex and some dead simple stuff in Rails. We’re going to do some Flex components, some parameter passing, some ActionScript coding, better Flex form code, some states and some transitions.

So stay awake at the back….

First some pre-amble.

So we are going to work with the same environment and Ruby on Rails application as the last tutorial part so i’m assuming you are up and ready with that.

First some small changes to the Reviews Controller


class ReviewsController < ApplicationController
    def list
      @reviews = Review.find :all
      render :xml => @reviews.to_xml
    end

    def create
      @review = Review.new(params[:review])
      @review.save
      render :xml => @review.to_xml
    end
end

Just making some the reviews controller actually sends something back on a request (Flex likes returns from its HTTPServices)

Now create a new Flex project in Flex Builder…

Creating the reviews component.

Flex allows you to break the UI down into reusable components. Which is nice. So we are going to create a reusable Reviews RIA component we can use in Flex applications.

Create a directory in the Flex project called frComponents.

Now select this directory, and go for File > New > MXML Component. You should now be able to create a new component called reviewsPanel. Enter “reviewsPanel” for the file name. Select Panel for the base component and select vertical for the layout.

You should now get a component file such as:


<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" 
   xmlns="*" layout="vertical">

</mx:Panel>

We are going to have a reusable Panel for the Reviews. Change this component file to read


<?xml version="1.0" encoding="utf-8"?>
<mx:Panel title="Reviews" xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" 
                 creationComplete="reviewRequest.send()">
   <mx:HTTPService id="reviewRequest" 
                              url="http://localhost:3000/reviews/list" useProxy="false"/>
     <mx:DataGrid id="dgReviews"  width="800" 
                           dataProvider="{reviewRequest.lastResult.reviews.review}" 
                           editable="false" >
        <mx:columns>
      <mx:DataGridColumn headerText="Title" dataField="title" width="480"/>
          <mx:DataGridColumn headerText="Author" dataField="author" width="240"/>
          <mx:DataGridColumn headerText="Score" dataField="score" width="80"/>
    </mx:columns>
    </mx:DataGrid>

     <mx:TextArea id="taReviews" width="800" htmlText="{dgReviews.selectedItem.text}"/>

     <mx:ControlBar id="cbReviewControlBar">
       <mx:Button label="Create New" id="btnCreateNew"/>
     </mx:ControlBar> 

</mx:Panel>

Like the first tutorial part this creates a DataGrid showing all the entries in the reviews table pulled from the ReviewController method list.

We now need to put this component into the main project application to try it out. Open the main Flex application file for the project (should be called projectname.mxml)

Change it to read:


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
                         xmlns="*" layout="absolute"  xmlns:fr="frComponents.*">
    <fr:reviewsPanel x="0" y="0">
    </fr:reviewsPanel> 
</mx:Application>

The xmlns:fr=”frComponents.*” namespace declaration tells Flex to load all the component files in the frComponents directory. These can the be added to the application using the XML tag namespace:componentname

Make sure your Rails application is running and Run the Flex project. You should see something like:

Creating a ‘Add Review’ state

Now unlike the last part we are going to add an Add Review form as a new view state in Flex. Flex uses states to define different user reviews which can be transitioned between based on user input. You can have different state models per components which are handled locally from the application state model.

Lets add the create HTTPService to the component. Add the following under the existing HTTPService.


   <mx:HTTPService contentType="application/xml" id="reviewCreateRequest" 
                              result="reviewRequest.send();" 
                              url="http://localhost:3000/reviews/create" useProxy="false" 
                              method="POST">
      <mx:request xmlns="">
          <review>
             <title>{fTitle.text}</title>
             <author>{fAuthor.text}</author>
             <score>{fScore.value}</score>
             <text>{fText.text}</text>
          </review>
       </mx:request>
   </mx:HTTPService>

The _request=”reviewRequest.send();” _ parameter refreshes the DataGrid from the database after the create is completed.

And add code for the new Form state. Add the following as the last child of the component


<mx:states>
    <mx:State name="Create">
    <mx:AddChild position="lastChild">
             <mx:Form width="800" id="frmCreate">
            <mx:FormHeading label="Add a new Review"/>
             <mx:FormItem label="Title" required="true">
                <mx:TextInput width="260" id="fTitle"/>
            </mx:FormItem>
               <mx:FormItem label="Author" required="true">
                <mx:TextInput width="140" id="fAuthor"/>
            </mx:FormItem>
            <mx:FormItem label="Score">
                <mx:NumericStepper id="fScore"/>
            </mx:FormItem>
            <mx:FormItem label="Text">
                <mx:TextArea width="600" height="200" id="fText"/>
            </mx:FormItem>
            <mx:FormItem direction="horizontal">
                <mx:Button label="Submit" 
                                  click="reviewCreateRequest.send();currentState=''"/>
                <mx:Button label="Cancel" click="currentState=''"/>
            </mx:FormItem>
            </mx:Form>
          </mx:AddChild>
    </mx:State>
</mx:states>

This adds a new state called ‘Create’. This state uses mx:AddChild to add a new child to the end of the component. When this state is current. This form will be added to the Panel, when this state is no longer active the form will no longer be part of the Flex model.

The form is created a bit better than the last part. Using mx:FormItem is the best way to layout forms in Flex.

Now all we need to do is be able to change to this state. So lets change the control bar create button to:


<mx:Button label="Create New" id="btnCreateNew" 
                  click="currentState='Create'"/>

The currentState= allows us to switch states. The currentState=’‘ switches back to the default state.

So run the project and click on the Create New button. You should get a little something like:

Transitions

Ok so thats a little boring. This is why Flex allows you to add effects to state transitions so lets add a simple WideDown transition effect.

Add the following after the mx:states end tag.


<mx:transitions>
      <mx:Transition id="createTransition" fromState="*" 
                             toState="Create">
          <mx:Parallel target="{frmCreate}">
            <mx:WipeDown duration="1000"/>
        </mx:Parallel>
      </mx:Transition>
    </mx:transitions>

A simple WipeDown effect for 1000ms when going from any state to the Create state.

Parameter passing to Rails

Now its time to delete the rows we have created. The simple method is to select a row in the grid and hit a delete button which sends a request to the rails controller. We need to add a method to our ReviewController


    def delete
      @review = Review.find(params[:id])
      @review.destroy
      render :xml => @review.to_xml
    end
This method finds the review with the Id sent in the HTTP parameter id and then calls the destroy method which will delete the row from the database. So lets add a Flex HTTPService to our component:

<mx:HTTPService id="reviewDeleteRequest" 
                           result="reviewRequest.send();" 
                           url="http://localhost:3000/reviews/delete" 
                           useProxy="false"/>

And a button to our control bar:


  <mx:Button label="Delete" 
       click="reviewDeleteRequest.send({id: dgReviews.selectedItem.id});

The HTTPService send() methods allows you to specify HTTP parameters by sending an ActionScript Object with properties defined. We do this with the {id: dgReviews.selectedItem.id} syntax (later on we will do it with a different syntax.

This will now delete rows from the database. All well and good but a little too easy to delete something, we need to add a confirmation selection. To do this we need some ActionScript code in our Flex component file.

Add this to the file


<mx:Script>
<![CDATA[
    import mx.controls.Alert;
    import mx.events.CloseEvent;
    private function deleteHandler(event:Event) : void
    {
      Alert.show("Are you sure you want to delete this item?", "Delete Item", 3, this,
      function(event:CloseEvent):void
      {
        if (event.detail==Alert.YES)
          reviewDeleteRequest.send({id: dgReviews.selectedItem.id});
       });
    }
]]>
</mx:Script>

And change the delete button to call this new function.


  <mx:Button label="Delete" click="deleteHandler(event);"/>

Now clicking on delete should give a little something like this:

And finally some editing.

Now I guess all we need to create a CRUD is to allow the users to update the data. Well we good just do another Form like the create form and edit the data that way, but that would be a little too easy and we wouldn’t really learn much. I’ll leave that to you at home. Instead what we are going to use is the editable DataGrid. This is a bit harder than what has come before.

First an updating action for our Rails controller:


def update
  @review = Review.find(params[:id])
  @review.update_attributes(params[:review])
  render :xml => @review.to_xml
end

This method finds the review specified by the HTTP Parameter id and then updates any attributes which are given are interrupted as a Map by Rails under the review parameter. Any HTTP parameters given as “review[attname] ” are changed by Rails to be a Map as params:review so passing the update_attributes method params[:review] will give it name value pairs defied in the HTTP parameters using “review[]”. Complex huh.

We need a Flex HTTPService to call this Rails method:


  <mx:HTTPService id="reviewUpdateRequest" 
                  result="reviewRequest.send();" 
                  url="http://localhost:3000/reviews/update" 
                  useProxy="false" 
                  method="POST"/>    

No we need our code to make the data grid editable:


<mx:Script>
<![CDATA[
    import mx.controls.Alert;
    import mx.events.DataGridEvent;
    import mx.events.DataGridEventReason;
     private function updateHandler(event:Event) : void
    {
        dgReviews.editable=true
        btnUpdateData.label = "End Updates" 
        btnUpdateData.removeEventListener('click', updateHandler)
        btnUpdateData.addEventListener('click', endUpdateHandler)
    }

    private function endUpdateHandler(event:Event) : void
    {
        dgReviews.editable=false
        btnUpdateData.label = "Update Data" 
        btnUpdateData.removeEventListener('click', endUpdateHandler)
        btnUpdateData.addEventListener('click', updateHandler)
    }

     public function checkUpdate(event:DataGridEvent) : void            
         {  
             if (event != null)
             {                
                if (event.reason == DataGridEventReason.CANCELLED ||
                    event.reason == DataGridEventReason.OTHER )
                    return;

                var params:Object = new Object();
                params['id'] = event.currentTarget.editedItemRenderer.data['id']
                params['review[' + event.dataField +']'] 
                 = TextInput(event.currentTarget.itemEditorInstance).text
                reviewUpdateRequest.send(params);
               }

        }                       
]]>
</mx:Script>

And a new button to our control bar:


  <mx:Button label="Update Data" click="updateHandler(event)" 
             id="btnUpdateData"/>

Pressing the button will make the DataGrid editable and change the button name to “End Updates”. Pressing it again will make the grid un-editable again. We just need to call the checkUpdate method when a cell in the grid has been edited.


  <mx:DataGrid id="dgReviews"  width="800" 
               dataProvider="{reviewRequest.lastResult.reviews.review}" 
               editable="false"  itemEditEnd="checkUpdate(event);">

Try running this and pressing the Update Data button. Then any grid cell can be edited by clicking on it:

Summary

And that’s it. Hope you enjoyed it. At somepoint in the future we will look at integrating Flex with Rails sessions. Lots of fun i hope.

47 Responses to “RubyOnRails (1.1) and Flex (2.0): Pt 2”

  1. Enrico Says:

    Thanks for the second part. The tutorila is great, only thing I have a problem with creating the first HTTPService, the one for creating a new review: Flex Builder complains about ‘request’ as an unknown attribute. I am able to create a new review, but it cannot be displayed immadiately in the grid, only a relod will show it. Enrico

  2. Stuart Eccles Says:

    Yes, that’s a typo. Should be result

    I’ve edited the article but good spot!!

  3. Alex MacCaw Says:

    Great tutorial. Just one thing – the delete button should have a closing tag. Also perhaps you should announce it on the Ruby on Rails mailing list/forum and also the Flex one. I’m sure there are lots of other interested people out there.

  4. Christopher Maloney Says:

    Thanks Stuart, Your tute has been most informative. Part 2 has been eagerly awaited.

    I wonder… if you could find the time, whether you could please take us one step further and provide a clue as to how such a Flex(ible) unit may be incorporated into an existing Rails (desktop/browser) App.

    I have your sample working in a “closed shop” in an isolated (Windows xp) webrick example, but am desperate to get this functionality into my “job dasboard” (ultimatemately using a flex tabbar + list + exploded form, on datagrid item click) . My (experimental) app is sitting on a Linux box served via Apache 2 (xampp + RoRox) and I just cannot figure out how to get such an example served from my App.

    Does one take the whole [/bin] directory ( of the generated “flexiblerails” sample) and somehow make a “partial” call?

    Any guidance would be GREATLY appreciated. Cheers, Chris

  5. JesterXL Says:

    Nice tutorials mangxt! Thanks a lot.

    You really outta get yourself aggregated on MXNA with content like this:

    http://weblogs.macromedia.com/mxna/

  6. larry Says:

    this is very cool stuff. Thanks so much for posting it.

    I’m a bit stuck however on how to incorporate flex in a real rails environment. The hardcoded urls that reference ‘localhost” obviously have to go and I have other questions on where it would live on the server, etc. Any chance of a third tutorial with a ‘life-like’ deployment example?

  7. Stuart Eccles Says:

    Ok life like deployment example it is. I’ll post it up soon.

  8. Borut Says:

    You can use relative URLs: ”...the relative URL is computed automatically based on the location of the SWF running this application.” You can use url=”/reviews/list” instead of url=”http://localhost:3000/reviews/list”.

  9. Ras Says:

    I wanted to create a project that uses rails and flex with sessions but I have no idea where to start. Any ETA on your “integrating Flex with Rails sessions.” turtorial. Thanks

  10. Austin Says:

    Nice Job Again

  11. Andrey Says:

    Then I create a component with Panel, the application shows nothing. Then I change Panel to, for example, VBox – it’s ok. that’s happended?

  12. Steve Nelson Says:

    Stuart; Thank you SO MUCH for this tutorial and the previous. It fills me with renewed energy to pick up RoR again which I had been infatuated with last fall but had to walk away from temporarily when I became clear I wasn’t making any headway with it. But the combination of MySQL, RoR and Flex 2 is AWESOME!

    Here’s my situation. I don’t have access to a PC and thus to Builder. I am compiling in Terminal on a Mac with mxmlc. Tutorial 1 went fine. In this one I hit a serious snag. First the thing wouldn’t compile until I changed “result” to “lastResult” in the datagrid data provider. Then it compiled but wouldn’t initialize when I embedded the app in an html page. When I opened the app directly in Flash Player 9 I found out why: it gave a 1009 error, stating that it couldn’t run methods on a null object. I’m guessing that Builder does some smoothing out of stuff that the command line compiler doesn’t – apparently the main app isn’t recognizing the Panel in the mxml component you demonstrate.

    Any suggestions on how I can make the main app accept a custom mxml component? When I incorporated the code directly in the main app it runs (placed wrong but functional).

    Thanks again for the GREAT material.

  13. Steve Nelson Says:

    Retraction…. So sorry. I was compiling using the SDK from beta 3. I hadn’t heard or read that anything had changed with the SKD subsequent to the official release of Flex 2, but I just downloaded it again and something MUST have changed because now it compiles and finds the custom component okay. Sorry. Still had to change the dataprovider line to lastResult though.

  14. Chris Says:

    Hi, Just wondering if there has been any revision of this tute? Has anyone got it working?

    I am getting the following error on the DataGridEventReason test.

    1120: Access of undefined property DataGridEventReason Due tho this error, the Update button does not appear after compile/run.

    I’m just not a good enough coder to understand – the help pages seem to agree ok with Stuart’s code.

  15. David Says:

    FYI for Flex 2 final you must change:

    {reviewRequest.result.reviews.review}

    to:

    {reviewRequest.lastResult.reviews.review}

    otherwise you will get a:

    1119: Access of possibly undefined property result through a reference with static type mx.rpc.http.mxml:HTTPService

  16. David Says:

    Hey Chris, I ran into the same problem as you with DataGridEventReason. It seems that there is a bug:

    http://livedocs.macromedia.com/flex/2/docs/00000871.html (read the comments at the bottom).

    I simply removed those Event lines so it works now.

  17. David Says:

    Also I noticed this, though it doesnt fix the problem:

    import mx.events.DataGridEvent

    Should be:

    import mx.events.DataGridEvent;

  18. Chris Says:

    Thanks for the feedback David, Yeah cheers, I picked up the {reviewRequest.lastResult.reviews.review} from Steve Nelson’s heads-up earlier…

    I also noticed the syntax problem with the semi-colon; but thanks.

    I have not looked at your notice of the bug yet… just thought I’d report this first on the way thru.

    I now have the Update button showing up… I moved the HTTPService for: - reviewDeleteRequest and… - reviewUpdateRequest to up above the <mx:script> tag.

    However, after trying an update/end update, I get this:

    URL: http://localhost:3000/reviews/update"]

    ... plus a few more lines of rather cryptic error reporting.

    I will go now to investigate the bug report you mentioned…

    Thanks again for the input… I will return…

  19. Chris Says:

    oops, sorry. I did not realize my error notice was going to generate a url to my localhost.

    Thanks for the feedback David, Yeah cheers, I picked up the {reviewRequest.lastResult.reviews.review} from Steve Nelson’s heads-up earlier…

    I also noticed the syntax problem with the semi-colon; but thanks.

    I have not looked at your notice of the bug yet… just thought I’d report this first on the way thru.

    I now have the Update button showing up… I moved the HTTPService for: - reviewDeleteRequest and… - reviewUpdateRequest to up above the <mx:script> tag.

    However, after trying an update/end update, I get this:

    ....RPC Fault faultString=”HTTP request error” faultCode=”Server.Error.Request” faultDetail=”Error: [IOErrorEvent type=”ioError” bubbles=false cancelable=false eventPhase=2 text=”Error #2032: Stream Error. URL: http://localhost:3000/reviews/update”]. URL: http://localhost:3000/reviews/update”....

    ... plus a few more lines of rather cryptic error reporting.

    I will go now to investigate the bug report you mentioned…

    Thanks again for the input… I will return…

  20. Chris Says:

    oops, sorry AGAIN

    My ”- reviewUpdateRequest to up above the tag.” Should have read: - reviewUpdateRequest to up above SCRIPT the tag.

  21. David Says:

    No problem. I am happy with the functionality after removing the datagrid event pieces. It updates, the button is there, but it could be a little better. I’m sure this will be fixed in flex soon )fingers crossed).

    Now I’m trying to learn more about filtering results from rails. Today was my first touch of rails. Havent found a good online forum for tech discussion on rails yet.

  22. Chris Says:

    Cheers David, Yes, ok; with the DataGridEventReason commented out, I seem to be updating the record… as you you say, not ideal… we will wait and see for next version update… Although with the first try – before htting the “dismiss” button, I did generate an error ( as outlined above). Subsequent to that first edit/update, it worked – without the error!

    Still have yet to embrace the challenge of getting the Flex-Bin stuff to be called from my Rails App… have seen the other bits & pieces on this, but yet to get to a point where my Flex RIA is at one with my Rails back end.

    I agree “filtering results from rails” is a MUST for a real app. I have seen something on this filtering from a mate of mine who has had a good go at rails… I will come back here if I have news that may be of interest.

  23. Sanjay Says:

    Stuart, great tutorial, just what I have been looking for I think :) one question though, the last section to edit the datagrid is not working for me, I am able to change the data in the grid, but it does not get updated on the db – any ideas ANYONE !!! I have made the changes so that the error due to the ‘undefined property’ above is removed and the ‘delete button’ code has been added. I presume that the data should be updated on the db ?? thanks Sanjay

  24. Chris Says:

    Re the DataGridEventReason issue and commenting out the if (event.reason DataGridEventReason.CANCELLED || event.reason DataGridEventReason.OTHER ) { return; }

    if you put this line import mx.events.DataGridEventReason; just above the public function checkUpdate(event:DataGridEvent) : void line, the above event.reason test is OK.

    I am still not able to update the DB however… same as Sanjay above. Cheers, Chris

  25. Chris Says:

    I will try that again so it might be readable… Re the DataGridEventReason issue and commenting out the

    if (event.reason DataGridEventReason.CANCELLED || event.reason DataGridEventReason.OTHER ) { return; }

    if you put this line

    import mx.events.DataGridEventReason;

    just above the

    public function checkUpdate(event:DataGridEvent) : void

    line, the above event.reason test is OK.

    I am still not able to update the DB however… same as Sanjay above. I know I am connected to mysql because the datagrid populates OK. Cheers, Chris

  26. Roberto Says:

    Amazing !!! Thank you very much. This is my first mini app using Ruby.. and I definately will use it with Flex now that I know it really speeds up the development time. Hope to hear more tutorials like this.

  27. chtrinh Says:

    Can someone clarify this? So, if i want to do some AJAX style effects inplace edit and such, I would have to use actionscripts? When would i use actionscripts and when would i use mxml? They seem to blend nicely but whats the seperations?

  28. Antyki Says:

    Thank’s for that but what do you mean “hobbyst” ?

    Cheers.

  29. Alan Buses Says:

    Nice tutorials! I presume that the data should be updated on the db and I have made the changes so that the error due to the undefined property above is removed and the delete button code has been added- I hope that this is going to walk! Thanks a lot.

  30. doowhsalf@mac.com Says:

    Nice tutorial ! I managed almost to get everything working, but I got a problem with the if (event.reason DataGridEventReason.CANCELLED || event.reason DataGridEventReason.OTHER ) return; condition. The event.reason alwasys gets the value “other” and consquently I never reach the update method in Ruby.

    I’m working on my Mac Pro with the Beta flex builder and ruby 1.8.x Thank’s for any tip. Cheers !

  31. Jonny Says:

    Hey Good Job,

    My my issue is different, I am looking for Simple example to demonstrate the TabBar control with submit button using PHP. The data has to be entered in MYSQL DB. Can anyone help me. Just imagine you are using this for a Multiple tax for on one page using different Tabs (Navigator).

    regards John

  32. Mark S Says:

    What about RESTful Rails + Flex?

  33. Greg P Says:

    Hi, I found that I needed the import mx.controls.TextInput; or it didnt recognize the TextInput directives. This may be a flex 2.0.1 thing .. dont know for sure.

  34. Anil Nelli Says:

    A nice simple and easy tutorial to integrate Flex with Rails.

    Thank you.

  35. a Says:

    could any one tell which files of flex is needed to stored on public folder in rails , and is it required to give only controller name/def name instead of full path like http://localhost:300/controller name/def name in HttpService tag mail me: abhishekchess1@gmail.com

  36. Opony Says:

    I enjoyed reading your posts. It’s interesting to read ideas, and observations from someone else’s point of view. So please keep up the great work.

  37. Sonnensegel Says:

    Hi Chris, thank you so much for your helpful tutorials, I have made the changes and now it works and i dont have anymore problems :-) Hugs from Sue – Stuttgart – Germany

  38. Charles Says:

    Interesting article, I have added it to my bookmarks folder

  39. nixon2000 Says:

    Thank’s for that

  40. WN Says:

    What to do when you have a master/detail model and view ? Let say you have an account and account details. The xml for the account info is simple just as you use <reviews>. I am using a detail form where the user can add/delete/update detail items dynamically so I think I have to construct the details XML using ActionScript and use HTTPService there to send the data or do you know of something simpler ?

  41. Christoph Says:

    There are many useful informations in this great article really enjoy reading the whole blog that you write. Thanks!

  42. InsenceTync Says:

    billboard top country [url=http://soundmp3s.com/artist12598/clay-aiken/]Clay Aiken[/url] mother son wedding dance music http://allofmusic.co.cc/all_music-dj-zeph-117936-1/ julie scott folk music singer [url=http://mymusicpro.org/artist614ip/the-69-eyes-audio/]The 69 Eyes[/url] free music aerobic http://www.indianpad.com/user/overseas-pharmacy who in the beatles still survive. give it to me michael jackson youngbloodz mp3 [url=http://mp3sstore.org/blueray-zemine-and-t.half-6587-1/]Zemine and T.Half[/url] soil and rock left behind by a glacier [url=http://moviesman.livejournal.com/625.html]basic dance steps chareston[/url] d7984t audio drivers. ice dance vivien keirle world champion [url=http://mp3panda.info/artist366748/natural-born-grooves/]Natural Born Grooves[/url] deutsch audio http://mp3sstore.org/blueray-fiorello-73401-1/ j rock granite orlando fl http://moviesman.livejournal.com/796.html the influence of background music on academic performance [url=http://soundmp3s.com/artist15716/drift/]audio pitch changer software[/url] apartment rentals in prince rupert.

  43. Baiblenia Says:

    А как на блоге можно заработать? У меня есть блог о игре на гитаре. Правда там народа в день не много ходит...человек 20. Можно с него что то заработать?

  44. Baiblenia Says:

    Хорошо написал. Так держать!!! :)

  45. Baiblenia Says:

    А мне блог понравился

  46. Baiblenia Says:

    одобрямс статью :)

  47. Baiblenia Says:

    оцените мой сайтец пожалуйста :) www.qwabi.ru

Leave a Reply