问:
WebRTC Connectivity Issue
lately I have been experimenting with WebRTC. I was trying to really wrap my head around it, while at the same time see if I could test it.

So I wrote a test that checks for two things

Is the connection consistent?

Will the number of peers slow down the connection, and if so what is the threshold before it becomes noticeable?

What I have noticed in my test, is that (1) was not true. What I found was that this tests results averages to about 10 to 20 passes out of a 100?

What I ask is for someone to take a look and let me know if I wrote my test wrong, or perhaps identify another reason for the failure.

Please note that I ran this only on Chrome 62, and the ice-server list is taken from https://github.com/DamonOehlman/freeice.

So below is a test that I wrote:

```<code>const localStream = await navigator.mediaDevices.getUserMedia({video:true});
const iceServers = [{
    urls: [
        "stun:stun.l.google.com:19302",
        "stun:stun1.l.google.com:19302",
        "stun:stun2.l.google.com:19302",
        "stun:stun3.l.google.com:19302",
        "stun:stun4.l.google.com:19302",
        "stun:stun.ekiga.net",
        "stun:stun.ideasip.com",
        "stun:stun.rixtelecom.se",
        "stun:stun.schlund.de",
        "stun:stun.stunprotocol.org:3478",
        "stun:stun.voiparound.com",
        "stun:stun.voipbuster.com",
        "stun:stun.voipstunt.com",
        "stun:stun.voxgratia.org"
    ]
}]

async function initExample(){
    try{
        // Init Local Connections
        var localConnection = new RTCPeerConnection({iceServers});
        var remoteConnection = new RTCPeerConnection({iceServers});

        // Create Data Channels
        var remoteDataChannel;// this is added later
        var localDataChannel = localConnection.createDataChannel('DataChannel');

        // Configure Stream
        var remoteStream;// this is added later
        localConnection.addStream(localStream)

        // Catch DataChannel when it is sent from local to remote
        remoteConnection.ondatachannel = dataChannelEvent => {
            remoteDataChannel = dataChannelEvent.channel
        }

        // Catch MediaStream when it is sent from local to remote
        remoteConnection.onaddstream = mediaStreamEvent => {
            remoteStream = mediaStreamEvent.stream;
        }

        // Create Offer
        var offer = await localConnection.createOffer();
        await localConnection.setLocalDescription(offer);
        await remoteConnection.setRemoteDescription(localConnection.localDescription);

        // Create Answer
        var answer = await remoteConnection.createAnswer();
        await remoteConnection.setLocalDescription(answer);
        await localConnection.setRemoteDescription(remoteConnection.localDescription);

        // Return Results
        return {
            localConnection, remoteConnection,
            localDataChannel, localStream,
            get remoteDataChannel(){
                return remoteDataChannel
            },
            get remoteStream(){
                return remoteStream;
            },
        }
    }catch(e){
        console.error(e)
    }
}

async function test(N=100){
    const stats = {pass:0, fail: 0, N};
    const results = [];
    for (var i = 0; i < N; i++) {
        console.log({...stats, i})
        let conn = await initExample();
        if(conn.remoteDataChannel){
            stats.pass++;
        }else{
            stats.fail++;
        }
        results.push(conn);
    }
    return results;
}

var r = await test();
</code>```

答:
So after @JTejedor comments and suggestion, I realized that my connection test was setup incorrectly. For anyone who is having this issue, please note, you need make sure that the peers are sending their registered ice-candidates to one another.

In my offline version this is done as follows:

```<code>// Map Ice Candidate
remoteConnection.onicecandidate = evt => {
    if(evt.candidate) {
        localConnection.addIceCandidate(evt.candidate)
    }
}
localConnection.onicecandidate = evt => {
    if(evt.candidate) {
        remoteConnection.addIceCandidate(evt.candidate)
    }
}
</code>```

If you have this working with sockets (or some other server-side solution), make sure to send these over the socket to other peer.

Here is the working test (which always passes):

```<code>// Ice Servers
const iceServers = [{
    urls: [
        "stun:stun.l.google.com:19302",
        "stun:stun1.l.google.com:19302",
        "stun:stun2.l.google.com:19302",
        "stun:stun3.l.google.com:19302",
        "stun:stun4.l.google.com:19302",
        "stun:stun.ekiga.net",
        "stun:stun.ideasip.com",
        "stun:stun.rixtelecom.se",
        "stun:stun.schlund.de",
        "stun:stun.stunprotocol.org:3478",
        "stun:stun.voiparound.com",
        "stun:stun.voipbuster.com",
        "stun:stun.voipstunt.com",
        "stun:stun.voxgratia.org"
    ]
}]

// Run the test once
async function initExample(){
    // Init Local Connections
    var localConnection = new RTCPeerConnection({iceServers});
    var remoteConnection = new RTCPeerConnection({iceServers});//this is really "remote" its simulated

    // Map Ice Candidate
    remoteConnection.onicecandidate = evt => {
        if(evt.candidate) {
            localConnection.addIceCandidate(evt.candidate)
        }
    }
    localConnection.onicecandidate = evt => {
        if(evt.candidate) {
            remoteConnection.addIceCandidate(evt.candidate)
        }
    }

    // Create Data Channels
    var remoteDataChannel;// this is added later
    var localDataChannel = localConnection.createDataChannel('DataChannel');

    // Tie end of promise to getting the datachannel
    return new Promise( async res => {
        // Catch DataChannel when it is sent from local to remote
        remoteConnection.ondatachannel = dataChannelEvent => {
            console.log('GOT CHANNEL', dataChannelEvent)
            remoteDataChannel = dataChannelEvent.channel
            res(remoteDataChannel);
        }

        // Create Offer
        var offer = await localConnection.createOffer();
        await localConnection.setLocalDescription(offer);
        await remoteConnection.setRemoteDescription(localConnection.localDescription);

        // Create Answer
        var answer = await remoteConnection.createAnswer();
        await remoteConnection.setLocalDescription(answer);
        await localConnection.setRemoteDescription(remoteConnection.localDescription);

    })
}

// A Promisified Timeout
const promisedTimeout = (timeout = 10000) => new Promise((resolve, reject) => {
  let wait = window.setTimeout(() => {
    window.clearTimeout(wait);
    resolve(false);
  }, timeout)
})

// Run the test N times
async function test(N=100){
    // Init variables
    const stats = {pass:0, fail: 0, N};
    const results = [];

    // Loop
    for (var i = 0; i <= N; i++) {
        // log
        console.log({...stats, i});

        // This will each example against a timeout,
        // if the timeout finishes first, then this test will be marked as fail
        let pass = await Promise.race([
            initExample(),
            promisedTimeout()
        ]);
        if(pass) {
            stats.pass ++;
        }else{
            stats.fail ++;
        }
        results.push(pass);
    }
    return {results,stats};
}

// Run the test
test()
</code>```

Title: WebRTC Connectivity Issue

Body:
lately I have been experimenting with WebRTC. I was trying to really wrap my head around it, while at the same time see if I could test it.

So I wrote a test that checks for two things

Is the connection consistent?

Will the number of peers slow down the connection, and if so what is the threshold before it becomes noticeable?

What I have noticed in my test, is that (1) was not true. What I found was that this tests results averages to about 10 to 20 passes out of a 100?

What I ask is for someone to take a look and let me know if I wrote my test wrong, or perhaps identify another reason for the failure.

Please note that I ran this only on Chrome 62, and the ice-server list is taken from https://github.com/DamonOehlman/freeice.

So below is a test that I wrote:

const localStream = await navigator.mediaDevices.getUserMedia({video:true});
const iceServers = [{
    urls: [
        "stun:stun.l.google.com:19302",
        "stun:stun1.l.google.com:19302",
        "stun:stun2.l.google.com:19302",
        "stun:stun3.l.google.com:19302",
        "stun:stun4.l.google.com:19302",
        "stun:stun.ekiga.net",
        "stun:stun.ideasip.com",
        "stun:stun.rixtelecom.se",
        "stun:stun.schlund.de",
        "stun:stun.stunprotocol.org:3478",
        "stun:stun.voiparound.com",
        "stun:stun.voipbuster.com",
        "stun:stun.voipstunt.com",
        "stun:stun.voxgratia.org"
    ]
}]

async function initExample(){
    try{
        // Init Local Connections
        var localConnection = new RTCPeerConnection({iceServers});
        var remoteConnection = new RTCPeerConnection({iceServers});

        // Create Data Channels
        var remoteDataChannel;// this is added later
        var localDataChannel = localConnection.createDataChannel('DataChannel');

        // Configure Stream
        var remoteStream;// this is added later
        localConnection.addStream(localStream)

        // Catch DataChannel when it is sent from local to remote
        remoteConnection.ondatachannel = dataChannelEvent => {
            remoteDataChannel = dataChannelEvent.channel
        }

        // Catch MediaStream when it is sent from local to remote
        remoteConnection.onaddstream = mediaStreamEvent => {
            remoteStream = mediaStreamEvent.stream;
        }

        // Create Offer
        var offer = await localConnection.createOffer();
        await localConnection.setLocalDescription(offer);
        await remoteConnection.setRemoteDescription(localConnection.localDescription);

        // Create Answer
        var answer = await remoteConnection.createAnswer();
        await remoteConnection.setLocalDescription(answer);
        await localConnection.setRemoteDescription(remoteConnection.localDescription);

        // Return Results
        return {
            localConnection, remoteConnection,
            localDataChannel, localStream,
            get remoteDataChannel(){
                return remoteDataChannel
            },
            get remoteStream(){
                return remoteStream;
            },
        }
    }catch(e){
        console.error(e)
    }
}

async function test(N=100){
    const stats = {pass:0, fail: 0, N};
    const results = [];
    for (var i = 0; i < N; i++) {
        console.log({...stats, i})
        let conn = await initExample();
        if(conn.remoteDataChannel){
            stats.pass++;
        }else{
            stats.fail++;
        }
        results.push(conn);
    }
    return results;
}

var r = await test();

Body:
So after @JTejedor comments and suggestion, I realized that my connection test was setup incorrectly. For anyone who is having this issue, please note, you need make sure that the peers are sending their registered ice-candidates to one another.

In my offline version this is done as follows:

// Map Ice Candidate
remoteConnection.onicecandidate = evt => {
    if(evt.candidate) {
        localConnection.addIceCandidate(evt.candidate)
    }
}
localConnection.onicecandidate = evt => {
    if(evt.candidate) {
        remoteConnection.addIceCandidate(evt.candidate)
    }
}

If you have this working with sockets (or some other server-side solution), make sure to send these over the socket to other peer.

Here is the working test (which always passes):

// Ice Servers
const iceServers = [{
    urls: [
        "stun:stun.l.google.com:19302",
        "stun:stun1.l.google.com:19302",
        "stun:stun2.l.google.com:19302",
        "stun:stun3.l.google.com:19302",
        "stun:stun4.l.google.com:19302",
        "stun:stun.ekiga.net",
        "stun:stun.ideasip.com",
        "stun:stun.rixtelecom.se",
        "stun:stun.schlund.de",
        "stun:stun.stunprotocol.org:3478",
        "stun:stun.voiparound.com",
        "stun:stun.voipbuster.com",
        "stun:stun.voipstunt.com",
        "stun:stun.voxgratia.org"
    ]
}]

// Run the test once
async function initExample(){
    // Init Local Connections
    var localConnection = new RTCPeerConnection({iceServers});
    var remoteConnection = new RTCPeerConnection({iceServers});//this is really "remote" its simulated

    // Map Ice Candidate
    remoteConnection.onicecandidate = evt => {
        if(evt.candidate) {
            localConnection.addIceCandidate(evt.candidate)
        }
    }
    localConnection.onicecandidate = evt => {
        if(evt.candidate) {
            remoteConnection.addIceCandidate(evt.candidate)
        }
    }

    // Create Data Channels
    var remoteDataChannel;// this is added later
    var localDataChannel = localConnection.createDataChannel('DataChannel');

    // Tie end of promise to getting the datachannel
    return new Promise( async res => {
        // Catch DataChannel when it is sent from local to remote
        remoteConnection.ondatachannel = dataChannelEvent => {
            console.log('GOT CHANNEL', dataChannelEvent)
            remoteDataChannel = dataChannelEvent.channel
            res(remoteDataChannel);
        }

        // Create Offer
        var offer = await localConnection.createOffer();
        await localConnection.setLocalDescription(offer);
        await remoteConnection.setRemoteDescription(localConnection.localDescription);

        // Create Answer
        var answer = await remoteConnection.createAnswer();
        await remoteConnection.setLocalDescription(answer);
        await localConnection.setRemoteDescription(remoteConnection.localDescription);

    })
}

// A Promisified Timeout
const promisedTimeout = (timeout = 10000) => new Promise((resolve, reject) => {
  let wait = window.setTimeout(() => {
    window.clearTimeout(wait);
    resolve(false);
  }, timeout)
})

// Run the test N times
async function test(N=100){
    // Init variables
    const stats = {pass:0, fail: 0, N};
    const results = [];

    // Loop
    for (var i = 0; i <= N; i++) {
        // log
        console.log({...stats, i});

        // This will each example against a timeout,
        // if the timeout finishes first, then this test will be marked as fail
        let pass = await Promise.race([
            initExample(),
            promisedTimeout()
        ]);
        if(pass) {
            stats.pass ++;
        }else{
            stats.fail ++;
        }
        results.push(pass);
    }
    return {results,stats};
}

// Run the test
test()


来源: stackexchange

问:
Mouse "buffering" clicks on disabled buttons
my program is a text game that emulates a Console output with a TextBox on a WindowsForm.
One feature I'm trying to implement is by clicking a button it will output to the TextBox with a certain speed, which is achieved with this method atm:

```<code>public static void Write(String text, Color color, TextBox textArea, UI ui)
    {
        ui.Enabled = false;
        foreach (Control b in ui.Controls)//Disable controls
        {
            if (b.GetType() == typeof(Button))
                b.Enabled= false;
        }
        textArea.ForeColor = color;
        foreach (char c in text) //Write the text
        {
            textArea.AppendText(c.ToString());
            if (c == '\n')
            {
                textArea.AppendText("\n");
                Thread.Sleep(100);
            }
            Thread.Sleep(30);
        }
        textArea.AppendText("\n");
        foreach (Control b in ui.Controls)//Enable controls
        {
            if (b.GetType() == typeof(Button))
                b.Enabled= true;
        }
        ui.Enabled = true;
    }
</code>```

The button events are called like so:

```<code>    private void btn1_Click(object sender, EventArgs e)
    {
        Core.Write("this is button 1", Color.Red, txtDialog, this);
    }
    private void btn2_Click(object sender, EventArgs e)
    {
        Core.Write("this is button 2", Color.Green, txtDialog, this);
    }
    private void btn3_Click(object sender, EventArgs e)
    {
        Core.Write("this is button 3", Color.Blue, txtDialog,this);
    }
    private void btn4_Click(object sender, EventArgs e)
    {
        Core.Write("this is button 4", Color.White, txtDialog,this);
    }
</code>```

What I've noticed is that, even with the buttons and the form disabled while it does not complete the writing logic, by clicking multiple times on any given button it will "save" that click and output it right after the writing of the previous is done.

What should I do to eradicate this bug? Many thanks

答:
as far as I see, this code should not work as you've expected, but it should work as you've described as bug. The situation is not a bug, this is how Windows works. Windows creates a message queue for each application and forward every event to this message queue and main thread (GUI thread for WinForms) of this application, and messages are processed sequentially.

What your code does is, it disables the container (UI?) and hides the buttons and tries to write some text by suspending for a few milliseconds, and then it enabled everything. All are happening in UI thread where it should be processing messages.

Now, when you click, app receives a click event, and runs your code and it takes some time. While your code is running (remember Thread.Sleep), user clicks the place where your button is, windows posts message to your app queue to be processed next. When does this messages get processed? After your code completes, since it is the same thread that processes messages and runs your own code. When your code completes (buttons and UI is available again), and then the next message is processed.

Try using a BackgroundWorker which runs in another thread.

```<code>public static void Write(String text, Color color, TextBox textArea, Form ui)
{
    textArea.ForeColor = color;

    var handler = new Action<string>(textArea.AppendText);

    foreach (char c in text) //Write the text
    {
        textArea.Invoke(handler, c.ToString());
        if (c == '\n')
        {
            textArea.Invoke(handler, "\n");
            Thread.Sleep(100);
        }
        Thread.Sleep(30);
    }
    textArea.Invoke(handler, "\n");
}

private void button1_Click(object sender, EventArgs e)
{
    button1.Enabled = false;
    backgroundWorker1.RunWorkerAsync();
}

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    button1.Enabled = true;

}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    Write("this is button 1", Color.Red, textBox1, this);
}
</code>```

ContentLicense: CC BY-SA 3.0

Body:
my program is a text game that emulates a Console output with a TextBox on a WindowsForm.
One feature I'm trying to implement is by clicking a button it will output to the TextBox with a certain speed, which is achieved with this method atm:

public static void Write(String text, Color color, TextBox textArea, UI ui)
    {
        ui.Enabled = false;
        foreach (Control b in ui.Controls)//Disable controls
        {
            if (b.GetType() == typeof(Button))
                b.Enabled= false;
        }
        textArea.ForeColor = color;
        foreach (char c in text) //Write the text
        {
            textArea.AppendText(c.ToString());
            if (c == '\n')
            {
                textArea.AppendText("\n");
                Thread.Sleep(100);
            }
            Thread.Sleep(30);
        }
        textArea.AppendText("\n");
        foreach (Control b in ui.Controls)//Enable controls
        {
            if (b.GetType() == typeof(Button))
                b.Enabled= true;
        }
        ui.Enabled = true;
    }

The button events are called like so:

    private void btn1_Click(object sender, EventArgs e)
    {
        Core.Write("this is button 1", Color.Red, txtDialog, this);
    }
    private void btn2_Click(object sender, EventArgs e)
    {
        Core.Write("this is button 2", Color.Green, txtDialog, this);
    }
    private void btn3_Click(object sender, EventArgs e)
    {
        Core.Write("this is button 3", Color.Blue, txtDialog,this);
    }
    private void btn4_Click(object sender, EventArgs e)
    {
        Core.Write("this is button 4", Color.White, txtDialog,this);
    }

What I've noticed is that, even with the buttons and the form disabled while it does not complete the writing logic, by clicking multiple times on any given button it will "save" that click and output it right after the writing of the previous is done.

What should I do to eradicate this bug? Many thanks

Body:
as far as I see, this code should not work as you've expected, but it should work as you've described as bug. The situation is not a bug, this is how Windows works. Windows creates a message queue for each application and forward every event to this message queue and main thread (GUI thread for WinForms) of this application, and messages are processed sequentially.

What your code does is, it disables the container (UI?) and hides the buttons and tries to write some text by suspending for a few milliseconds, and then it enabled everything. All are happening in UI thread where it should be processing messages.

Now, when you click, app receives a click event, and runs your code and it takes some time. While your code is running (remember Thread.Sleep), user clicks the place where your button is, windows posts message to your app queue to be processed next. When does this messages get processed? After your code completes, since it is the same thread that processes messages and runs your own code. When your code completes (buttons and UI is available again), and then the next message is processed.

Try using a BackgroundWorker which runs in another thread.

public static void Write(String text, Color color, TextBox textArea, Form ui)
{
    textArea.ForeColor = color;

    var handler = new Action<string>(textArea.AppendText);

    foreach (char c in text) //Write the text
    {
        textArea.Invoke(handler, c.ToString());
        if (c == '\n')
        {
            textArea.Invoke(handler, "\n");
            Thread.Sleep(100);
        }
        Thread.Sleep(30);
    }
    textArea.Invoke(handler, "\n");
}

private void button1_Click(object sender, EventArgs e)
{
    button1.Enabled = false;
    backgroundWorker1.RunWorkerAsync();
}

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    button1.Enabled = true;

}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    Write("this is button 1", Color.Red, textBox1, this);
}


问:
Call to MongoRepository in an ExecutorService fails to complete
I am running a Spring Boot application and have multiple threads calling a MongoRepository. This, however, leads to weird timeout behavior.

This is my MongoRepository:

```<code>public interface EquipmentRepository extends MongoRepository<Equipment, String> {

    Optional<Equipment> findByEquipmentSerialNumber(String equipmentSerialNumber);

}
</code>```

This is a reduced version of my code highlighting the problem

```<code>ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
taskExecutor.execute(() -> {
    LOG.info("Executing query...");
    Optional<Equipment> equipment = equipmentRepository.findByEquipmentSerialNumber("21133"); // guaranteed to be found
    LOG.info("Query done: {}", equipment.get().getEquipmentSerialNumber());
});
taskExecutor.shutdown();
LOG.info("taskExecutor shut down");
try {
    taskExecutor.awaitTermination(30, TimeUnit.SECONDS);
    LOG.info("taskExecutor done");
} catch (InterruptedException e) {
    System.out.println("Error");
}
</code>```

That produced output looks like this

```<code>taskExecutor shut down
Executing query...
<30 second pause>
taskExecutor done
Query done: 21133
</code>```

If I increase the timeout of awaitTermination() the pause increases accordingly. So somehow my code inside the execute() lambda is "paused" and only continues after the timeout is reached.

If I remove the call to equipmentRepository, everything works as expected and there is no 30-second pause.

What is keeping my code from completing without reaching the timeout?

答:
Looks like mongo repository waits for 'main' thread to perform query (very strange though)

Not answer for your specific question, but probably solution for your problem: Spring Data can do async requests doc

Body:
I am running a Spring Boot application and have multiple threads calling a MongoRepository. This, however, leads to weird timeout behavior.

This is my MongoRepository:

public interface EquipmentRepository extends MongoRepository<Equipment, String> {

    Optional<Equipment> findByEquipmentSerialNumber(String equipmentSerialNumber);

}

This is a reduced version of my code highlighting the problem

ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
taskExecutor.execute(() -> {
    LOG.info("Executing query...");
    Optional<Equipment> equipment = equipmentRepository.findByEquipmentSerialNumber("21133"); // guaranteed to be found
    LOG.info("Query done: {}", equipment.get().getEquipmentSerialNumber());
});
taskExecutor.shutdown();
LOG.info("taskExecutor shut down");
try {
    taskExecutor.awaitTermination(30, TimeUnit.SECONDS);
    LOG.info("taskExecutor done");
} catch (InterruptedException e) {
    System.out.println("Error");
}

That produced output looks like this

taskExecutor shut down
Executing query...
<30 second pause>
taskExecutor done
Query done: 21133

If I increase the timeout of awaitTermination() the pause increases accordingly. So somehow my code inside the execute() lambda is "paused" and only continues after the timeout is reached.

If I remove the call to equipmentRepository, everything works as expected and there is no 30-second pause.

What is keeping my code from completing without reaching the timeout?


问:
"Requires extended permission: read_mailbox" when I get all the conversations
I need to get all messages addressed to the user who identifies the access token. That is to get all the comments that other users have made in their posts, comments that have made in their albums. And also the messages they have received in the conversations.

I have it done as follows:

```<code>@Override
    public List<CommentEntity> getCommentsLaterThan(Date startDate, String accessToken) {

        List<CommentEntity> comments = new ArrayList<CommentEntity>();
        try {
            logger.debug("Call Facebook API for accessToken : " + accessToken + " on thread: " + Thread.currentThread().getName());
            FacebookClient facebookClient = new DefaultFacebookClient(accessToken, Version.VERSION_2_8);
            // Get Information about access token owner
            User user = facebookClient.fetchObject("me", User.class);

            StreamUtils.concat(
                StreamUtils.concat(
                    getAllCommentsFromPostsAfterThan(facebookClient, startDate, user),
                    getAllCommentsFromAlbumsAfterThan(facebookClient, startDate, user)
                )
                .map(comment -> facebookCommentMapper.facebookCommentToCommentEntity(comment)),
                getAllCommentsFromConversationsAfterThan(facebookClient, startDate, user)
            )
            .collect(Collectors.toList());

            logger.debug("Total Facebook comments : " + comments.size());
        } catch (FacebookOAuthException e) {
            logger.error(e.getErrorMessage());
            throw new InvalidAccessTokenException(SocialMediaTypeEnum.FACEBOOK, accessToken);
        } catch (Exception e) {
            throw new GetCommentsProcessException(e);
        }
        return comments;
    }
</code>```

The getAllCommentsFromConversationsAfterThan method is explained below:

```<code>private Stream<CommentEntity> getAllCommentsFromConversationsAfterThan(FacebookClient facebookClient, Date startDate, User user) {
           Connection<Conversation> conversations = facebookClient.fetchConnection("me/conversations", Conversation.class);
        return StreamUtils.asStream(conversations.iterator())
                .flatMap(List::stream)
                .flatMap(conversation -> {
                    Connection<Message> messages = facebookClient.fetchConnection(
                            conversation.getId() + "/messages", Message.class, Parameter.with("fields", "message,created_time,from,id"));
                    return StreamUtils.asStream(messages.iterator());
                })
                .flatMap(List::stream)
                .filter(message
                        -> !message.getFrom().getId().equals(user.getId())
                && (startDate != null ? message.getCreatedTime().after(startDate) : true))
                .map(message -> facebookMessageMapper.facebookMessageToCommentEntity(message));
    }
</code>```

I have obtained an access token through the Graph API Explorer Tool. Granting all permissions. And an exception occurs with this error message.

```<code>  (# 298) Requires extended permission: read_mailbox "
</code>```

The documentation states that the read_mailbox permission is obsolete.

I'm using version 2.8. Is it possible to get the messages from the conversations?

Thanks in advance

答: First you have to obtain a user access token with read_page_mailboxes permission. Then, you can generate the page access token.</p>

Body:
I need to get all messages addressed to the user who identifies the access token. That is to get all the comments that other users have made in their posts, comments that have made in their albums. And also the messages they have received in the conversations.

I have it done as follows:

@Override
    public List<CommentEntity> getCommentsLaterThan(Date startDate, String accessToken) {

        List<CommentEntity> comments = new ArrayList<CommentEntity>();
        try {
            logger.debug("Call Facebook API for accessToken : " + accessToken + " on thread: " + Thread.currentThread().getName());
            FacebookClient facebookClient = new DefaultFacebookClient(accessToken, Version.VERSION_2_8);
            // Get Information about access token owner
            User user = facebookClient.fetchObject("me", User.class);

            StreamUtils.concat(
                StreamUtils.concat(
                    getAllCommentsFromPostsAfterThan(facebookClient, startDate, user),
                    getAllCommentsFromAlbumsAfterThan(facebookClient, startDate, user)
                )
                .map(comment -> facebookCommentMapper.facebookCommentToCommentEntity(comment)),
                getAllCommentsFromConversationsAfterThan(facebookClient, startDate, user)
            )
            .collect(Collectors.toList());

            logger.debug("Total Facebook comments : " + comments.size());
        } catch (FacebookOAuthException e) {
            logger.error(e.getErrorMessage());
            throw new InvalidAccessTokenException(SocialMediaTypeEnum.FACEBOOK, accessToken);
        } catch (Exception e) {
            throw new GetCommentsProcessException(e);
        }
        return comments;
    }

The getAllCommentsFromConversationsAfterThan method is explained below:

private Stream<CommentEntity> getAllCommentsFromConversationsAfterThan(FacebookClient facebookClient, Date startDate, User user) {
           Connection<Conversation> conversations = facebookClient.fetchConnection("me/conversations", Conversation.class);
        return StreamUtils.asStream(conversations.iterator())
                .flatMap(List::stream)
                .flatMap(conversation -> {
                    Connection<Message> messages = facebookClient.fetchConnection(
                            conversation.getId() + "/messages", Message.class, Parameter.with("fields", "message,created_time,from,id"));
                    return StreamUtils.asStream(messages.iterator());
                })
                .flatMap(List::stream)
                .filter(message
                        -> !message.getFrom().getId().equals(user.getId())
                && (startDate != null ? message.getCreatedTime().after(startDate) : true))
                .map(message -> facebookMessageMapper.facebookMessageToCommentEntity(message));
    }

I have obtained an access token through the Graph API Explorer Tool. Granting all permissions. And an exception occurs with this error message.

  (# 298) Requires extended permission: read_mailbox "

The documentation states that the read_mailbox permission is obsolete.

I'm using version 2.8. Is it possible to get the messages from the conversations?

Thanks in advance


问:
Java insists on changing both variables
When running my MVC model application, a Singleton instance of ApplicationModel is created.

When running the main method in my controller class, a List<Shop> is assigned to shops using setShops(). If shopsDefault = null, setShops() will also continue to assign the same List<Shop> to shopsDefault.

So far, so good.

However, when I call sortShopsByName() - which as you can see below uses setShops() - both shops AND shopsDefault become sorted! Why does it not just sort shops as intended?

My ApplicationModel Class...

```<code>import java.util.*;

public class ApplicationModel {

    //static variables
    private static ApplicationModel instance = null;

    //instance variables
    private List<Shop> shops;
    private List<Shop> shopsDefault;

    //constructors
    private ApplicationModel() {}

    //getInstance method
    public static ApplicationModel getInstance() {
        if (instance == null) {
            instance = new ApplicationModel();
        }

        return instance;
    }

    //getters and setters
    public List<Shop> getShops() {
        return shops;
    }

    public void setShops(List<Shop> shops) {
        this.shops = shops;
        if (this.shopsDefault == null) {
            this.shopsDefault = shops;
        }
    }

    public List<Shop> getShopsDefault() {
        return this.shopsDefault;
    }

    //Shop methods
    public void sortShopsByName() {
        List<Shop> shops = this.getShops();
        Collections.sort(shops);
        this.setShops(shops);
    }

    public void returnShopsToDefaultOrder() {

        List<Shop> shopsDefault = this.getShopsDefault();
        setShops(shopsDefault);
    }
}
</code>```

答: Because it is the same list. If you want different lists, make a copy when you assign it to shopsDefault (i.e. new ArrayList<Shop>(shops)).

Title: Java insists on changing both variables

Body:
When running my MVC model application, a Singleton instance of ApplicationModel is created.

When running the main method in my controller class, a List<Shop> is assigned to shops using setShops(). If shopsDefault = null, setShops() will also continue to assign the same List<Shop> to shopsDefault.

So far, so good.

However, when I call sortShopsByName() - which as you can see below uses setShops() - both shops AND shopsDefault become sorted! Why does it not just sort shops as intended?

My ApplicationModel Class...

import java.util.*;

public class ApplicationModel {

    //static variables
    private static ApplicationModel instance = null;

    //instance variables
    private List<Shop> shops;
    private List<Shop> shopsDefault;

    //constructors
    private ApplicationModel() {}

    //getInstance method
    public static ApplicationModel getInstance() {
        if (instance == null) {
            instance = new ApplicationModel();
        }

        return instance;
    }

    //getters and setters
    public List<Shop> getShops() {
        return shops;
    }

    public void setShops(List<Shop> shops) {
        this.shops = shops;
        if (this.shopsDefault == null) {
            this.shopsDefault = shops;
        }
    }

    public List<Shop> getShopsDefault() {
        return this.shopsDefault;
    }

    //Shop methods
    public void sortShopsByName() {
        List<Shop> shops = this.getShops();
        Collections.sort(shops);
        this.setShops(shops);
    }

    public void returnShopsToDefaultOrder() {

        List<Shop> shopsDefault = this.getShopsDefault();
        setShops(shopsDefault);
    }
}


问:
How to change class name based on scroll position?
I am using reactjs and need to change the class name of a div based on scroll position. Once this div reaches the top of the page, I need to change its class. Any suggestions on how to do this?</p>

答:
You would get the offsetTop position for your div and then compare it with scrollTop, if it satisfies your condition, you would set a state which in turn would set the className.

Sample code:

```<code>class App extends React.Component {
  state = {
    style: null
  };

  handleScroll = e => {
    if (e.target.scrollTop >= this.bbox.offsetTop) {
      this.setState({
        className: "something",
        divStyle: { backgroundColor: "orange" }
      });
    }
  };
  render() {
    return (
      <div onScroll={this.handleScroll} style={styles}>
        <Ipsum />
        <Ipsum />
        <br />
        <div
          className={this.state.className}
          style={this.state.divStyle}
          ref={ref => (this.bbox = ref)}
        >
          HelloWorld
        </div>
        <br />
        <Ipsum />
        <Ipsum />
        <Ipsum />
        <Ipsum />
        <Ipsum />
      </div>
    );
  }
}
</code>```

P.S. However unlike the sample code, you need to make sure that you
are not setting the state everytime, for performance reasons.

Working DEMO

Body:
You would get the offsetTop position for your div and then compare it with scrollTop, if it satisfies your condition, you would set a state which in turn would set the className.

Sample code:

class App extends React.Component {
  state = {
    style: null
  };

  handleScroll = e => {
    if (e.target.scrollTop >= this.bbox.offsetTop) {
      this.setState({
        className: "something",
        divStyle: { backgroundColor: "orange" }
      });
    }
  };
  render() {
    return (
      <div onScroll={this.handleScroll} style={styles}>
        <Ipsum />
        <Ipsum />
        <br />
        <div
          className={this.state.className}
          style={this.state.divStyle}
          ref={ref => (this.bbox = ref)}
        >
          HelloWorld
        </div>
        <br />
        <Ipsum />
        <Ipsum />
        <Ipsum />
        <Ipsum />
        <Ipsum />
      </div>
    );
  }
}

P.S. However unlike the sample code, you need to make sure that you
are not setting the state everytime, for performance reasons.

Working DEMO

ContentLicense: CC BY-SA 4.0


问:
Hadoop Yarn Stuck at Running Job Ubuntu
I was trying to run single mode pseudo distributed mode hadoop on ec2 ubuntu machine on a simple task. However my program is stuck at running jobs. I attach my linux screen and Resourcemanager page here.Any idea is appreciated. Thanks.

Add1: the other thing I found is the NodeManager disappears when I type jps (It was there the first time I type jps but disappears later)

Add2: I checked nodemanager log and noticed that it was shutdown due to that minimum allocation is not satisfied, though I had changed scheduler minimum mb to 128 and vcore to 1 in yarn-site.xml

答:
Finally solved my own problem above.

Added details to the problem statement above, I later found my node manager didn't start well (first time type jps, node manager is there as shown in the attached picture. However, it disappeared seconds after that. I found this by type jps many times).

I checked the node manager log and found the error is "doesn't satisfy minimum allocations, Sending SHUTDOWN signal to the NodeManager"

I searched on the internet and found the solution is adding the following property into yarn-site.xml file

yarn.nodemanager.resource.memory-mb

value is 1024

yarn.nodemanager.resource.cpu-vcores

value is 1

Then restart start-dfs.sh and start-yarn.sh. If you get into hostname:8088, you would see the memory total and total vcores total should be larger than 0 now and you can run an application

PS: my application was stuck at map 0% and reduce 0% initially. Then I simply change 1024 to 4096, 1 to 2 above. Then I can successfully run a map reduce program on my ec2 instance (single node pseudo distributed)

Title: Hadoop Yarn Stuck at Running Job Ubuntu


问:
R --- filling a matrix by row with undertermined NAs
I am trying to fill a matrix with a sample of data:

```<code>       Pays NbChambre Prix
1    GrÃ¨ce        56  390
2    GrÃ¨ce       471  468
3    GrÃ¨ce        93  427
4    GrÃ¨ce        56  369
5    GrÃ¨ce       286  499
6    GrÃ¨ce       282  526
7    GrÃ¨ce       310  587
8    GrÃ¨ce       300  534
9     Maroc       146  447
10    Maroc       250  482
11    Maroc       196  511
12    Maroc       324  532
13    Maroc       138  450
14    Maroc       400  569
15    Maroc       366  419
16    Maroc       300  421
17    Maroc       144  579
18    Maroc       330  598
19    Maroc       260  495
20    Maroc       170  730
21 Portugal       254  646
22 Portugal       140  652
23 Portugal       273  802
24 Portugal       260  761
25 Portugal       169 1101
26  Tunisie       225  434
27  Tunisie       225  489
28  Tunisie       250  436
29  Tunisie       550  399
30  Tunisie       800  477
31  Tunisie       150  375
32  Tunisie       425  486
33  Tunisie       366  447
34  Tunisie       200  473
35  Tunisie       130  495
36  Turquie       500  617
37  Turquie        50  489
38  Turquie       232  520
39  Turquie       110  534
</code>```

I need to fill a matrix with the values of the vector NbChambre BUT in a way
that each row is used for a certain Pays. The dimensions of the matrix would be

```<code>nrow=length(unique(Pays)),ncol=max(table(Pays))
</code>```

I need to find a way to fill the matrix such as R automatically puts
NA or 0 or whatever to finish a row when there is not enough data and then skip line to continue with the vector NbChambre. I'm relatively new to R so I really can't seem to find any way to do this.

The result should look like

```<code>    [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
[1,]   56  471   93   56  286  282  310  300  NA   NA    NA     NA
[2,]  146 250  196  324  138  400  366  300  144   330   260    170
[3,]  254  140   273  260  169 NA   NA   NA   NA    NA    NA    NA
[4,]
[5,]                             and so on
</code>```

I would really like if someone could help me, thank you!

Body:
I am trying to fill a matrix with a sample of data:

       Pays NbChambre Prix
1    GrÃ¨ce        56  390
2    GrÃ¨ce       471  468
3    GrÃ¨ce        93  427
4    GrÃ¨ce        56  369
5    GrÃ¨ce       286  499
6    GrÃ¨ce       282  526
7    GrÃ¨ce       310  587
8    GrÃ¨ce       300  534
9     Maroc       146  447
10    Maroc       250  482
11    Maroc       196  511
12    Maroc       324  532
13    Maroc       138  450
14    Maroc       400  569
15    Maroc       366  419
16    Maroc       300  421
17    Maroc       144  579
18    Maroc       330  598
19    Maroc       260  495
20    Maroc       170  730
21 Portugal       254  646
22 Portugal       140  652
23 Portugal       273  802
24 Portugal       260  761
25 Portugal       169 1101
26  Tunisie       225  434
27  Tunisie       225  489
28  Tunisie       250  436
29  Tunisie       550  399
30  Tunisie       800  477
31  Tunisie       150  375
32  Tunisie       425  486
33  Tunisie       366  447
34  Tunisie       200  473
35  Tunisie       130  495
36  Turquie       500  617
37  Turquie        50  489
38  Turquie       232  520
39  Turquie       110  534

I need to fill a matrix with the values of the vector NbChambre BUT in a way
that each row is used for a certain Pays. The dimensions of the matrix would be

nrow=length(unique(Pays)),ncol=max(table(Pays))

I need to find a way to fill the matrix such as R automatically puts
NA or 0 or whatever to finish a row when there is not enough data and then skip line to continue with the vector NbChambre. I'm relatively new to R so I really can't seem to find any way to do this.

The result should look like

    [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
[1,]   56  471   93   56  286  282  310  300  NA   NA    NA     NA
[2,]  146 250  196  324  138  400  366  300  144   330   260    170
[3,]  254  140   273  260  169 NA   NA   NA   NA    NA    NA    NA
[4,]
[5,]                             and so on

I would really like if someone could help me, thank you!

Body:
This is another possibility using split and sapply:

l = split(df$NbChambre,df$Pays)
m = max(lengths(l))
res = t(sapply(l,function(x) x[1:m]))
         [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
GrÃ¨ce     56  471   93   56  286  282  310  300   NA    NA    NA    NA
Maroc     146  250  196  324  138  400  366  300  144   330   260   170
Portugal  254  140  273  260  169   NA   NA   NA   NA    NA    NA    NA
Tunisie   225  225  250  550  800  150  425  366  200   130    NA    NA
Turquie   500   50  232  110   NA   NA   NA   NA   NA    NA    NA    NA


答:
OK I found the solution myself (checking if there is at least 1 COUNT()>0 price different than current one p2.price!=p.price within previous 7 days p2.priceDate BETWEEN p.priceDate - INTERVAL 7 DAY AND p.priceDate for that particular product p2.prodId=p.prodId):

```<code>SELECT prodId,priceDate,price,
IF((SELECT COUNT(p2.price)
      FROM prices AS p2
     WHERE p2.prodId=p.prodId AND p2.price!=p.price
       AND p2.priceDate BETWEEN p.priceDate - INTERVAL 7 DAY AND p.priceDate),'Y','N') AS chg
FROM prices p
</code>```

SQL Fiddle

Title: MySQL: price changed within last 7 days

Body:
I have the following data (products with its prices and dates of price):

prodId priceDate  price
======+==========+=====
    1 |2017-11-24|  600
    1 |2017-11-21|  600
    1 |2017-11-18|  600
    1 |2017-11-15|  500
    1 |2017-11-05|  400
    1 |2017-11-03|  400
    2 |2017-11-11|  300

I need an indicator (column chg) which will show if product price had been changed within previous 7 days.

prodId priceDate  price chg
======+==========+=====+===
    1 |2017-11-24|  600| N
    1 |2017-11-21|  600| Y
    1 |2017-11-18|  600| Y
    1 |2017-11-15|  500| N
    1 |2017-11-05|  400| N
    1 |2017-11-03|  400| N
    2 |2017-11-14|  300| N

How this SQL query should look like?

Body:
OK I found the solution myself (checking if there is at least 1 COUNT()>0 price different than current one p2.price!=p.price within previous 7 days p2.priceDate BETWEEN p.priceDate - INTERVAL 7 DAY AND p.priceDate for that particular product p2.prodId=p.prodId):

SELECT prodId,priceDate,price,
IF((SELECT COUNT(p2.price)
      FROM prices AS p2
     WHERE p2.prodId=p.prodId AND p2.price!=p.price
       AND p2.priceDate BETWEEN p.priceDate - INTERVAL 7 DAY AND p.priceDate),'Y','N') AS chg
FROM prices p

SQL Fiddle


问:
Python from Windows to Centos -> import msvcrt getch issue
I have a stupid question I think.

I have a script develloped on Windows but the goal is to run it on a Centos Server 7. The script work well on windows but not on Centos.

I have this error :

```<code>[root@114697 scripts]# python3.6 synch.py
Traceback (most recent call last):
File "synch.py", line 9, in <module>
from msvcrt import getch
ModuleNotFoundError: No module named 'msvcrt'
</code>```

My script start with this :

```<code>from __future__ import division
import websocket
import thread
import time
import random
import sys
import json
import pymysql
import datetime
from time import ctime
from time import sleep
from msvcrt import getch
from pprint import pprint
import os
</code>```

Seems msvcrt import getchcome from Microsoft ...
Can some one help to solve this problem please ?

Note : Python 3.6 is not involved, it was compiled in a clean way on the server.
Seems

Yoki

答:
If you read the documentation for the msvcrt module, the first two sentences have this to say:

These functions provide access to some useful capabilities on Windows platforms. Some higher-level modules use these functions to build the Windows implementations of their services.

Since Centos Server 7 is not Windows, you will need to find an alternative approach to getch.

Body:
I have a stupid question I think.

I have a script develloped on Windows but the goal is to run it on a Centos Server 7. The script work well on windows but not on Centos.

I have this error :

[root@114697 scripts]# python3.6 synch.py
Traceback (most recent call last):
File "synch.py", line 9, in <module>
from msvcrt import getch
ModuleNotFoundError: No module named 'msvcrt'

My script start with this :

from __future__ import division
import websocket
import thread
import time
import random
import sys
import json
import pymysql
import datetime
from time import ctime
from time import sleep
from msvcrt import getch
from pprint import pprint
import os

Seems msvcrt import getchcome from Microsoft ...
Can some one help to solve this problem please ?

Note : Python 3.6 is not involved, it was compiled in a clean way on the server.
Seems

Yoki


问:
Can't submit apps to AppStore: ERROR ITMS-90534: "Invalid Toolchain

ERROR ITMS-90534: "Invalid Toolchain.

New apps and app updates must be built with the public (GM) versions of Xcode 6 or later, macOS, and iOS SDK or later. Don't submit apps built with beta software including beta macOS builds." ERROR ITMS-90534: "Invalid Toolchain. New apps and app updates must be built with the public (GM) versions of Xcode 6 or later, macOS, and iOS SDK or later. Don't submit apps built with beta software including beta macOS builds."

I found this error during upload IPA to account.

答:
I ran into this issue today with the App Store version of Xcode 9.2.

It's definitely not a beta release, I'm guessing there's some delay between when the software is released and iTunes connect gets updated to expect the new version.

I've tried all the suggestions above, and none worked... but here's what (although pretty sketchy) did for work me:

in organizer, right-click your archive and show in finder

show package contents

find Products/Applications/yourApp.app

show package contents

edit Info.plist

change the value for DTXcodeBuild to a previous version (9.2 is 9C40b, 9.1 is 9C40)

save and try again

This worked for me...


问:
Solr post files for deleting documents
I want to delete some documents from solr using bin/post command

I am trying to delete json documents. here is my file tobedeleted.json with ids

```<code>{"delete": ["1219073", "2234630", "120270", "244755", "2275454", "2121923", "2280254", "2261038", "1232067", "1227748"]}
</code>```

I am using
/solr/bin/post -c mycore -d tobedeleted.json

but it is throwing error as

```<code>    java -classpath /opt/solr/dist/solr-core-6.1.0.jar -Dauto=yes -Dc=projects -Ddata=args org.apache.solr.util.SimplePostTool /home/ansible/aws/data/DL_PT_20171205_20.json
SimplePostTool version 5.0.0
POSTing args to http://localhost:8983/solr/projects/update...
SimplePostTool: WARNING: Solr returned an error #400 (Bad Request) for url: http://localhost:8983/solr/projects/update
SimplePostTool: WARNING: Response: <?xml version="1.0" encoding="UTF-8"?>
<response>
<lst name="responseHeader"><int name="status">400</int><int name="QTime">1</int></lst><lst name="error"><lst name="metadata"><str name="error-class">org.apache.solr.common.SolrException</str><str name="root-error-class">com.ctc.wstx.exc.WstxUnexpectedCharException</str></lst><str name="msg">Unexpected character '/' (code 47) in prolog; expected '<'
 at [row,col {unknown-source}]: [1,1]</str><int name="code">400</int></lst>
</response>
SimplePostTool: WARNING: IOException while reading response: java.io.IOException: Server returned HTTP response code: 400 for URL: http://localhost:8983/solr/projects/update
COMMITting Solr index changes to http://localhost:8983/solr/projects/update...
Time spent: 0:00:00.021
You have mail in /var/spool/mail/ansible
</code>```

Please, can you let me know how can I delete documents using post utility?

答: I have done workaround for it using curl. But if anybody have better solution please suggest.</p>

Title: Solr post files for deleting documents

Body:
I want to delete some documents from solr using bin/post command

I am trying to delete json documents. here is my file tobedeleted.json with ids

{"delete": ["1219073", "2234630", "120270", "244755", "2275454", "2121923", "2280254", "2261038", "1232067", "1227748"]}

I am using
/solr/bin/post -c mycore -d tobedeleted.json

but it is throwing error as

    java -classpath /opt/solr/dist/solr-core-6.1.0.jar -Dauto=yes -Dc=projects -Ddata=args org.apache.solr.util.SimplePostTool /home/ansible/aws/data/DL_PT_20171205_20.json
SimplePostTool version 5.0.0
POSTing args to http://localhost:8983/solr/projects/update...
SimplePostTool: WARNING: Solr returned an error #400 (Bad Request) for url: http://localhost:8983/solr/projects/update
SimplePostTool: WARNING: Response: <?xml version="1.0" encoding="UTF-8"?>
<response>
<lst name="responseHeader"><int name="status">400</int><int name="QTime">1</int></lst><lst name="error"><lst name="metadata"><str name="error-class">org.apache.solr.common.SolrException</str><str name="root-error-class">com.ctc.wstx.exc.WstxUnexpectedCharException</str></lst><str name="msg">Unexpected character '/' (code 47) in prolog; expected '<'
 at [row,col {unknown-source}]: [1,1]</str><int name="code">400</int></lst>
</response>
SimplePostTool: WARNING: IOException while reading response: java.io.IOException: Server returned HTTP response code: 400 for URL: http://localhost:8983/solr/projects/update
COMMITting Solr index changes to http://localhost:8983/solr/projects/update...
Time spent: 0:00:00.021
You have mail in /var/spool/mail/ansible

Please, can you let me know how can I delete documents using post utility?


问:
C++ Delayed Load DLL Linker Error
I have a program that calls functions from foo.dll in one configuration, and has a second configuration that does not use foo.dll. I want to require foo.dll to be present only when needed. I currently switch between the two versions using

```<code>#define FLAGVAR 0
</code>```

or

```<code>#define FLAGVAR 1
</code>```

where FLAGVAR==1 means foo.dll is used. I then surround my import statements and function declarations like this:

```<code>#if FLAGVAR == 1
#import "foo_file.h"
#endif
...

#if FLAGVAR == 1
int foobar() {...}
#endif
</code>```

If I include foo.dll in Linker->Input->Additional Dependencies, then my program builds regardless of FLAGVAR's value.

Here's my problem:

I don't want to require foo.dll to be present in versions where FLAGVAR == 0. Thus, I moved foo.dll to Linker->Input->Delay Loaded DLLs. Now if FLAGVAR==0 then it doesn't require foo.dll and builds fine. However, if FLAGVAR==1, then I get a bunch of LNK2019 errors complaining that the functions from foo.dll couldn't be linked.

How can I accomplish my goal of only requiring foo.dll when FLAGVAR==1? Am I using delayed dll loading incorrectly? Thanks in advance for your help!

Using Visual Studio 2017, version 15.4.5

答:
Solved based on this link from the comments: C++ Visual Studio: linking using pragma comment

I didn't put any of the optional libraries in the properties dialog box in visual studio. Instead, in a header file, I put the following few lines:

```<code>#if FLAGVAR == 1
#pragma comment(lib, "foo.lib")
#endif
</code>```

Title: C++ Delayed Load DLL Linker Error

Body:
I have a program that calls functions from foo.dll in one configuration, and has a second configuration that does not use foo.dll. I want to require foo.dll to be present only when needed. I currently switch between the two versions using

#define FLAGVAR 0

or

#define FLAGVAR 1

where FLAGVAR==1 means foo.dll is used. I then surround my import statements and function declarations like this:

#if FLAGVAR == 1
#import "foo_file.h"
#endif
...

#if FLAGVAR == 1
int foobar() {...}
#endif

If I include foo.dll in Linker->Input->Additional Dependencies, then my program builds regardless of FLAGVAR's value.

Here's my problem:

I don't want to require foo.dll to be present in versions where FLAGVAR == 0. Thus, I moved foo.dll to Linker->Input->Delay Loaded DLLs. Now if FLAGVAR==0 then it doesn't require foo.dll and builds fine. However, if FLAGVAR==1, then I get a bunch of LNK2019 errors complaining that the functions from foo.dll couldn't be linked.

How can I accomplish my goal of only requiring foo.dll when FLAGVAR==1? Am I using delayed dll loading incorrectly? Thanks in advance for your help!

Using Visual Studio 2017, version 15.4.5

Body:
Solved based on this link from the comments: C++ Visual Studio: linking using pragma comment

I didn't put any of the optional libraries in the properties dialog box in visual studio. Instead, in a header file, I put the following few lines:

#if FLAGVAR == 1
#pragma comment(lib, "foo.lib")
#endif


答:
Once you assign shops to shposDefault, they both reference the same instance. Changes made to the instance via either instance will, thus, be visible via both references.

If this is not the intended behavior, you could copy the shops list when setting null. E.g.:

```<code>public void setShops(List<Shop> shops) {
    this.shops = shops;
    if (this.shopsDefault == null) {
        this.shopsDefault = new ArrayList<>(shops);
    }
}
</code>```

Title: Java insists on changing both variables

Body:
Once you assign shops to shposDefault, they both reference the same instance. Changes made to the instance via either instance will, thus, be visible via both references.

If this is not the intended behavior, you could copy the shops list when setting null. E.g.:

public void setShops(List<Shop> shops) {
    this.shops = shops;
    if (this.shopsDefault == null) {
        this.shopsDefault = new ArrayList<>(shops);
    }
}


问:
Sort values that come from different instances of the same class
I want to sort the order of the football clubs based on their points. I have created a class that get the values from the internet and process that values to calculate points based on the number of wins, loses and draws. Then I want to write the standings and their data onto a text file in that order. I created 20 different instances for 20 different clubs. However, I don't know what is the best way to sort the order.

```<code>import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CalculateStandings {
    private int matches;
    private int homeWin;
    private int homeDraw;
    private int homeLost;
    private int homeForGoal;
    private int homeAgainstGoal;
    private int awayWin;
    private int awayDraw;
    private int awayLost;
    private int awayForGoal;
    private int awayAgainstGoal;
    private int point;
    private int totalWin;
    private int totalLost;
    private int totalDraw;
    private int totalAgainstGoal;
    private int totalForGoal;

    void getResult(String team_Name) {
        try {
            URL url = new URL("https://raw.githubusercontent.com/openfootball/eng-england/master/2017-18/1-premierleague-i.txt");
        Scanner input = new Scanner(url.openStream());
        while (input.hasNext()) {
            String mydata = input.nextLine();
            if (mydata.contains(team_Name)) {
                Pattern pattern = Pattern.compile("[0-9]{1,2}-[0-9]{1,2}");
                Matcher matcher = pattern.matcher(mydata);
                String result;
                if (matcher.find()) {
                    matches += 1;
                    result = matcher.group();

                    if (mydata.startsWith("  " + team_Name)) {
                        homeForGoal += Integer.parseInt(Character.toString(result.charAt(0)));
                        homeAgainstGoal += Integer.parseInt(Character.toString(result.charAt(2)));
                    } else if (mydata.endsWith("  " + team_Name)) {
                        awayForGoal += Integer.parseInt(Character.toString(result.charAt(2)));
                        awayAgainstGoal += Integer.parseInt(Character.toString(result.charAt(0)));
                    }

                    if (Integer.parseInt(Character.toString(result.charAt(0))) > Integer.parseInt(Character.toString(result.charAt(2)))) {
                        if (mydata.startsWith("  " + team_Name)) {
                            point += 3;
                            homeWin += 1;
                        } else if (mydata.endsWith("  " + team_Name)) {
                            awayLost += 1;
                        }
                    } else if (Integer.parseInt(Character.toString(result.charAt(0))) < Integer.parseInt(Character.toString(result.charAt(2)))) {
                        if (mydata.startsWith("  " + team_Name)) {
                            homeLost += 1;
                        } else if (mydata.endsWith("  " + team_Name)) {
                            point += 3;
                            awayWin += 1;
                        }

                    } else {
                        if (mydata.startsWith("  " + team_Name)) {
                            point += 1;
                            homeDraw += 1;
                        } else if (mydata.endsWith("  " + team_Name)) {
                            point += 1;
                            awayDraw += 1;
                        }
                    }
                }
            }
        }
        totalWin = homeWin + awayWin;
        totalLost = homeLost + awayLost;
        totalDraw = homeDraw + awayDraw;
        totalAgainstGoal = homeAgainstGoal + awayAgainstGoal;
        totalForGoal = homeForGoal + awayForGoal;

    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

}
}
</code>```

And this is my main class:

```<code>public class Launcher {
public static void main(String[] args) {

    CalculateStandings Arsenal = new CalculateStandings();
    CalculateStandings Tottenham = new CalculateStandings();
    CalculateStandings WHam = new CalculateStandings();
    CalculateStandings CPalace = new CalculateStandings();
    CalculateStandings MU = new CalculateStandings();
    CalculateStandings MC = new CalculateStandings();
    CalculateStandings Everton = new CalculateStandings();
    CalculateStandings Liv = new CalculateStandings();
    CalculateStandings WBAlbion = new CalculateStandings();
    CalculateStandings NU = new CalculateStandings();
    CalculateStandings Stoke_City = new CalculateStandings();
    CalculateStandings Southampton = new CalculateStandings();
    CalculateStandings Leicester_City = new CalculateStandings();
    CalculateStandings Bournemouth = new CalculateStandings();
    CalculateStandings Watford = new CalculateStandings();
    CalculateStandings Brighton = new CalculateStandings();
    CalculateStandings Burnley = new CalculateStandings();
    CalculateStandings Huddersfield = new CalculateStandings();
    CalculateStandings Swansea = new CalculateStandings();

    Arsenal.getResult("Arsenal FC");
    Tottenham.getResult("Tottenham Hotspur");
    WHam.getResult("West Ham United");
    CPalace.getResult("Crystal Palace");
    MU.getResult("Manchester United");
    MC.getResult("Manchester City");
    Everton.getResult("Everton FC");
    Liv.getResult("Liverpool FC");
    WBAlbion.getResult("West Bromwich Albion");
    NU.getResult("Newcastle United");
    Stoke_City.getResult("Stoke City");
    Southampton.getResult("Southampton FC");
    Leicester_City.getResult("Leicester City");
    Bournemouth.getResult("AFC Bournemouth");
    Watford.getResult("Watford FC");
    Brighton.getResult("Brighton & Hove Albion");
    Burnley.getResult("Burnley FC");
    Huddersfield.getResult("Huddersfield Town");
    Swansea.getResult("Swansea City");

}

}
</code>```

I am thinking about storing the values into several arrays using Getter then sort them using sorting algorithm such as Bubble sort. However, as you can see, the number of instances and the number of fields inside each instances are numerous and write them into the arrays manually using Getter would take too much time. Therefore, I would like to ask you if there is a more optimized approach for this.

答:
You can have one single Map with club names as keys and points as values, and you can use lambdas to print out the table, here is an example for sorting clubs based on the points (descending):

```<code>package com.company;

import java.util.HashMap;
import java.util.Map;

public class Main {

    private static int rank = 1;

    public static void main(String[] args) {

        Map<String, Integer> teams = new HashMap<>(){{
            put("Arsenal", 54);
            put("Chelsea", 44);
            put("Manchester United", 52);
            put("Liverpool", 49);
            put("Tottenham", 50);
        }};

        teams.entrySet().stream()
                        .sorted((x,y) -> y.getValue().compareTo(x.getValue()))
                        .forEach(n -> System.out.println(rank++ + ". " + n.getKey() + " with " + n.getValue() + " points."
        ));

    }
}
</code>```

And the output would be:

Arsenal with 54 points.

Manchester United with 52 points.

Tottenham with 50 points.

Liverpool with 49 points.

Chelsea with 44 points.

If you have any additional questions, just ask me in the comments.

Body:
I want to sort the order of the football clubs based on their points. I have created a class that get the values from the internet and process that values to calculate points based on the number of wins, loses and draws. Then I want to write the standings and their data onto a text file in that order. I created 20 different instances for 20 different clubs. However, I don't know what is the best way to sort the order.

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CalculateStandings {
    private int matches;
    private int homeWin;
    private int homeDraw;
    private int homeLost;
    private int homeForGoal;
    private int homeAgainstGoal;
    private int awayWin;
    private int awayDraw;
    private int awayLost;
    private int awayForGoal;
    private int awayAgainstGoal;
    private int point;
    private int totalWin;
    private int totalLost;
    private int totalDraw;
    private int totalAgainstGoal;
    private int totalForGoal;

    void getResult(String team_Name) {
        try {
            URL url = new URL("https://raw.githubusercontent.com/openfootball/eng-england/master/2017-18/1-premierleague-i.txt");
        Scanner input = new Scanner(url.openStream());
        while (input.hasNext()) {
            String mydata = input.nextLine();
            if (mydata.contains(team_Name)) {
                Pattern pattern = Pattern.compile("[0-9]{1,2}-[0-9]{1,2}");
                Matcher matcher = pattern.matcher(mydata);
                String result;
                if (matcher.find()) {
                    matches += 1;
                    result = matcher.group();

                    if (mydata.startsWith("  " + team_Name)) {
                        homeForGoal += Integer.parseInt(Character.toString(result.charAt(0)));
                        homeAgainstGoal += Integer.parseInt(Character.toString(result.charAt(2)));
                    } else if (mydata.endsWith("  " + team_Name)) {
                        awayForGoal += Integer.parseInt(Character.toString(result.charAt(2)));
                        awayAgainstGoal += Integer.parseInt(Character.toString(result.charAt(0)));
                    }

                    if (Integer.parseInt(Character.toString(result.charAt(0))) > Integer.parseInt(Character.toString(result.charAt(2)))) {
                        if (mydata.startsWith("  " + team_Name)) {
                            point += 3;
                            homeWin += 1;
                        } else if (mydata.endsWith("  " + team_Name)) {
                            awayLost += 1;
                        }
                    } else if (Integer.parseInt(Character.toString(result.charAt(0))) < Integer.parseInt(Character.toString(result.charAt(2)))) {
                        if (mydata.startsWith("  " + team_Name)) {
                            homeLost += 1;
                        } else if (mydata.endsWith("  " + team_Name)) {
                            point += 3;
                            awayWin += 1;
                        }

                    } else {
                        if (mydata.startsWith("  " + team_Name)) {
                            point += 1;
                            homeDraw += 1;
                        } else if (mydata.endsWith("  " + team_Name)) {
                            point += 1;
                            awayDraw += 1;
                        }
                    }
                }
            }
        }
        totalWin = homeWin + awayWin;
        totalLost = homeLost + awayLost;
        totalDraw = homeDraw + awayDraw;
        totalAgainstGoal = homeAgainstGoal + awayAgainstGoal;
        totalForGoal = homeForGoal + awayForGoal;

    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

}
}

And this is my main class:

public class Launcher {
public static void main(String[] args) {

    CalculateStandings Arsenal = new CalculateStandings();
    CalculateStandings Tottenham = new CalculateStandings();
    CalculateStandings WHam = new CalculateStandings();
    CalculateStandings CPalace = new CalculateStandings();
    CalculateStandings MU = new CalculateStandings();
    CalculateStandings MC = new CalculateStandings();
    CalculateStandings Everton = new CalculateStandings();
    CalculateStandings Liv = new CalculateStandings();
    CalculateStandings WBAlbion = new CalculateStandings();
    CalculateStandings NU = new CalculateStandings();
    CalculateStandings Stoke_City = new CalculateStandings();
    CalculateStandings Southampton = new CalculateStandings();
    CalculateStandings Leicester_City = new CalculateStandings();
    CalculateStandings Bournemouth = new CalculateStandings();
    CalculateStandings Watford = new CalculateStandings();
    CalculateStandings Brighton = new CalculateStandings();
    CalculateStandings Burnley = new CalculateStandings();
    CalculateStandings Huddersfield = new CalculateStandings();
    CalculateStandings Swansea = new CalculateStandings();

    Arsenal.getResult("Arsenal FC");
    Tottenham.getResult("Tottenham Hotspur");
    WHam.getResult("West Ham United");
    CPalace.getResult("Crystal Palace");
    MU.getResult("Manchester United");
    MC.getResult("Manchester City");
    Everton.getResult("Everton FC");
    Liv.getResult("Liverpool FC");
    WBAlbion.getResult("West Bromwich Albion");
    NU.getResult("Newcastle United");
    Stoke_City.getResult("Stoke City");
    Southampton.getResult("Southampton FC");
    Leicester_City.getResult("Leicester City");
    Bournemouth.getResult("AFC Bournemouth");
    Watford.getResult("Watford FC");
    Brighton.getResult("Brighton & Hove Albion");
    Burnley.getResult("Burnley FC");
    Huddersfield.getResult("Huddersfield Town");
    Swansea.getResult("Swansea City");

}

}

I am thinking about storing the values into several arrays using Getter then sort them using sorting algorithm such as Bubble sort. However, as you can see, the number of instances and the number of fields inside each instances are numerous and write them into the arrays manually using Getter would take too much time. Therefore, I would like to ask you if there is a more optimized approach for this.

Body:
You can have one single Map with club names as keys and points as values, and you can use lambdas to print out the table, here is an example for sorting clubs based on the points (descending):

package com.company;

import java.util.HashMap;
import java.util.Map;

public class Main {

    private static int rank = 1;

    public static void main(String[] args) {

        Map<String, Integer> teams = new HashMap<>(){{
            put("Arsenal", 54);
            put("Chelsea", 44);
            put("Manchester United", 52);
            put("Liverpool", 49);
            put("Tottenham", 50);
        }};

        teams.entrySet().stream()
                        .sorted((x,y) -> y.getValue().compareTo(x.getValue()))
                        .forEach(n -> System.out.println(rank++ + ". " + n.getKey() + " with " + n.getValue() + " points."
        ));

    }
}

And the output would be:

Arsenal with 54 points.

Manchester United with 52 points.

Tottenham with 50 points.

Liverpool with 49 points.

Chelsea with 44 points.

If you have any additional questions, just ask me in the comments.


答:
In this code you're setting both members to the same list reference:

```<code>this.shops = shops;
if (this.shopsDefault == null) {
   this.shopsDefault = shops;
}
</code>```

If you want to separate them use the following:

```<code>this.shops = shops;
if (this.shopsDefault == null) {
   this.shopsDefault = new ArrayList<Shop>(shops);
}
</code>```

Title: Java insists on changing both variables

Body:
In this code you're setting both members to the same list reference:

this.shops = shops;
if (this.shopsDefault == null) {
   this.shopsDefault = shops;
}

If you want to separate them use the following:

this.shops = shops;
if (this.shopsDefault == null) {
   this.shopsDefault = new ArrayList<Shop>(shops);
}


问:
Capybara session not preserved with an api callback
I have a rail 5 app with capybara 2.13 feature specs. One spec completes a three-part transaction flow that includes a redirect url to a callback path in the app. I save the transaction ID in the session as session[:current_transaction_id] and then use this to look up the transaction when processing the callback. This works fine in development but when testing with capybara the session hash is missing :current_transaction_id.

I've unsuccessfuly tried wrapping my spec as described in
https://github.com/teamcapybara/capybara#using-sessions

```<code>Capybara.using_session("User session") do
  # log in and complete a transaction
end
</code>```

How can I get the session to persist through the given spec?

答:
Capybaras using_session doesn't refer to the session you're asking about. It refers to a browser or browser/app pair instance used by Capybara and would commonly be used if you needed to simultaneously log two different users into an app while testing it (chat, realtime status updates, etc).

When using cookie based sessions you need to make sure the original (sub)domains originally being visited (where you set the transaction id) and the one redirected to are the same (at least to the level the session cookie is set at). If they aren't the session cookie will no longer be valid for the redirect request (and not sent) and hence the transaction id not available.

Body:
I have a rail 5 app with capybara 2.13 feature specs. One spec completes a three-part transaction flow that includes a redirect url to a callback path in the app. I save the transaction ID in the session as session[:current_transaction_id] and then use this to look up the transaction when processing the callback. This works fine in development but when testing with capybara the session hash is missing :current_transaction_id.

I've unsuccessfuly tried wrapping my spec as described in
https://github.com/teamcapybara/capybara#using-sessions

Capybara.using_session("User session") do
  # log in and complete a transaction
end

How can I get the session to persist through the given spec?


问:
Array indexOf() vs includes() perfomance depending on browser and needle position
Is there any advantage to Array.prototype.includes() over Array.prototype.indexOf() depending on browsers (Chrome, Firefox) and needle item position (at the begging, middle, ending of the array)?

Array.prototype.includes vs. Array.prototype.indexOf
There is no browser specific information, there is no position in the array specific information, and I don't ask about NaN value.

答:
Here is the JSperf to compare includes and indexOf, and they are very close, but includes() will be faster than indexOf in latest chrome.

My understanding is not all browsers support includes though this API is easier to understand and maintain.


问:
Parent not listening to child $emit - VueJS
Here is the child component:

```<code>Vue.component("training-edit", {
    template: "#training-edit-template",
    props: ["show"],
    data: function () {
        return {
            form: new Form(),
            isWorking: false
        }
    },
    watch: {
        show: function (val) {
            if (val) {
                $("#editTrainingModal").modal("show");
            } else {
                $("#editTrainingModal").modal("hide");
            }
        }
    },
    methods: {
        onCancel: function () {
            this.$emit("doneEditing");
        }
    }
});
</code>```

Here is the parent:

```<code>new Vue({
    el: "#trainingEditContainer",
    data: {
        isWorking: false,
        showEditTraining: false
    },
    methods: {
        onEdit: function (e) {
            e.preventDefault();
            this.showEditTraining = true;
        },
        doneEditing: function () {
            this.showEditTraining = false;
        }
    }
});
</code>```

HTML:

```<code><div id="trainingEditContainer" class="row">
    // unrelated stuff here

    <training-edit v-bind:show="showEditTraining"></training-edit>
</div>
<script id="training-edit-template" type="x-template">
    <modal-right modalId="editTrainingModal">
        <div class="modal-header">
            <button type="button" class="close" v-on:click="onCancel" aria-label="close"><span aria-hidden="true">×</span></button>
            <h4 class="modal-title">Edit Training</h4>
        </div>
        @Html.Partial("Partial/_EditPartial")
    </modal-right>
</script>
</code>```

I can see in the Vue Developer Tools in Chrome that the event is being emitted but for some reason, the parent is not hearing it and the doneEditing function is never called. Am I missing something obvious here?

答:
First, I recommend you change the name of the event to done-editing.

```<code>this.$emit("done-editing")
</code>```

This is because attributes in HTML are case insensitive and if you are rendering some of your template to the DOM, it's just better all around to avoid camelCased names. Note that this doesn't apply if you define the templates in strings or if you are using single file components.

Then you need to listen for it on the component.

```<code><training-edit @done-editing="doneEditing" v-bind:show="showEditTraining">
</code>```

When you emit an event from a component, the parent has to explicitly listen for the event.

Body:
Here is the child component:

Vue.component("training-edit", {
    template: "#training-edit-template",
    props: ["show"],
    data: function () {
        return {
            form: new Form(),
            isWorking: false
        }
    },
    watch: {
        show: function (val) {
            if (val) {
                $("#editTrainingModal").modal("show");
            } else {
                $("#editTrainingModal").modal("hide");
            }
        }
    },
    methods: {
        onCancel: function () {
            this.$emit("doneEditing");
        }
    }
});

Here is the parent:

new Vue({
    el: "#trainingEditContainer",
    data: {
        isWorking: false,
        showEditTraining: false
    },
    methods: {
        onEdit: function (e) {
            e.preventDefault();
            this.showEditTraining = true;
        },
        doneEditing: function () {
            this.showEditTraining = false;
        }
    }
});

HTML:

    // unrelated stuff here

    <training-edit v-bind:show="showEditTraining"></training-edit>

I can see in the Vue Developer Tools in Chrome that the event is being emitted but for some reason, the parent is not hearing it and the doneEditing function is never called. Am I missing something obvious here?

Body:
First, I recommend you change the name of the event to done-editing.

this.$emit("done-editing")

This is because attributes in HTML are case insensitive and if you are rendering some of your template to the DOM, it's just better all around to avoid camelCased names. Note that this doesn't apply if you define the templates in strings or if you are using single file components.

Then you need to listen for it on the component.

<training-edit @done-editing="doneEditing" v-bind:show="showEditTraining">

When you emit an event from a component, the parent has to explicitly listen for the event.


问:
Reading a text and put it in an array exactly as shown
I'm trying to read a .txt file like this:

```<code>@Override annotation informs the compiler that the element is meant to
override an element declared in a superclasses.
  // mark method as a superclass method
  // that has been overridden
   @Override
   int overriddenMethod() { }
</code>```

My question is, if i use a simple String can i make so it shows exactly as it's shown above or does it have to be a String array?

答:
Yes, you can read into a single String using \Z delimiter to read until EOF character, java uses some scape sequences to show spaces, slashes, etc.</p>

```<code>String content;
try{
    content = new Scanner(new File("file.txt")).useDelimiter("\\Z").next();
} catch(FileNotFoundException e){
    System.out.println("file.txt not found");
}
</code>```

Body:
I'm trying to read a .txt file like this:

@Override annotation informs the compiler that the element is meant to
override an element declared in a superclasses.
  // mark method as a superclass method
  // that has been overridden
   @Override
   int overriddenMethod() { }

My question is, if i use a simple String can i make so it shows exactly as it's shown above or does it have to be a String array?

Body:
Yes, you can read into a single String using \Z delimiter to read until EOF character, java uses some scape sequences to show spaces, slashes, etc.

String content;
try{
    content = new Scanner(new File("file.txt")).useDelimiter("\\Z").next();
} catch(FileNotFoundException e){
    System.out.println("file.txt not found");
}


问:
Search box not appearing on rails application
I'm attempting to launch an MVC project on ruby on rails. Having a few issues.
Specifically with the search functionality. This is for a rental website. but unable to get the search box to appear and work.

I have 3 models items, profile and users.
Specifically I want to be able to search users by first name and last name.

Within the profile model I have:

```<code>def self.search(search)
  where("lastname LIKE ?", "%#{search}%")
end
</code>```

Within in the profile_controller I have:

```<code>def index
  if params[:search]
    @profiles = Profile.search(params[:search]).order("created_at DESC")
  else
   @profiles = Profile.all.order("created_at DESC")
  end
end
</code>```

The application has the following code layout application.html page:

```<code><p> search </p>
<% form_tag(profiles_path, :method => "get", id: "search-form") do %>
<%= text_field_tag :search, params[:search], placeholder: "Search Profile by Lastname" %>
<%= submit_tag "Search" %>
</code>```

Would also like to extend this further to see what items the users have rented as well.

答:
The form is not displaying because it's being evaluated via <%, which only executes the code without returning a value to print to the page. Try using <%= instead, like so:

```<code><%= form_tag(profiles_path, :method => "get", id: "search-form") do %>
</code>```

Hope this helps!

Body:
I'm attempting to launch an MVC project on ruby on rails. Having a few issues.
Specifically with the search functionality. This is for a rental website. but unable to get the search box to appear and work.

I have 3 models items, profile and users.
Specifically I want to be able to search users by first name and last name.

Within the profile model I have:

def self.search(search)
  where("lastname LIKE ?", "%#{search}%")
end

Within in the profile_controller I have:

def index
  if params[:search]
    @profiles = Profile.search(params[:search]).order("created_at DESC")
  else
   @profiles = Profile.all.order("created_at DESC")
  end
end

The application has the following code layout application.html page:

 search

<% form_tag(profiles_path, :method => "get", id: "search-form") do %>
<%= text_field_tag :search, params[:search], placeholder: "Search Profile by Lastname" %>
<%= submit_tag "Search" %>

Would also like to extend this further to see what items the users have rented as well.

Body:
The form is not displaying because it's being evaluated via <%, which only executes the code without returning a value to print to the page. Try using <%= instead, like so:

<%= form_tag(profiles_path, :method => "get", id: "search-form") do %>

Hope this helps!


问:
Android EditText delete(backspace) key event
How can I detect delete (backspace) key event for a editText? I've tried using TextWatcher, but when the editText is empty, when I press delete key, nothing happens. I want to detect delete key press foe an editText even if it has no text. </p>

答:
This question may be old but the answer is really simple using a TextWatcher.</p>

```<code>int lastSize=0;
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
    //2. compare the old length of the text with the new one
    //3. if the length is shorter, then backspace was clicked
    if (lastSize > charSequence.length()) {
        //4. Backspace was clicked
        //5. perform action
    }
    //1. get the current length of of the text
    lastSize = charSequence.length();
}
</code>```

Body:
This question may be old but the answer is really simple using a TextWatcher.

int lastSize=0;
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
    //2. compare the old length of the text with the new one
    //3. if the length is shorter, then backspace was clicked
    if (lastSize > charSequence.length()) {
        //4. Backspace was clicked
        //5. perform action
    }
    //1. get the current length of of the text
    lastSize = charSequence.length();
}


问:
Create and use CSV data in a subreport

Background

The Jaspersoft community forum is riddled with unanswered questions about how to use a CSV data source with a report.

Problem

When using Jaspersoft Studio, it is convenient to use a local file as a data source. The instructions for accomplishing this task are piecemeal at best and incomplete at worst.

Question

What are the steps required to use a CSV file (data source) with a main report and have that data be used by a subreport?

答:
Note: This solution drops the first result from the list in the subreport, which requires a workaround to resolve, which is not described in this answer.

Overview

The major steps include:

Create CSV file

Create data adapter

Create main report and subreport

Connect data set

Create report fields

Connect subreport

Create CSV file

Create the CSV file as follows:

Open a text editor.

Paste the following:```
city,age,name,address,status
"Dallas",47,"Janet Fuller","445 Upland Pl.","Trial"
"Lyon",38,"Andrew Heiniger","347 College Av.","Active"
"Dallas",43,"Susanne Smith","2 Upland Pl.","Active"
"Berne",22,"Bill Ott","250 - 20th Ave.","Active"
"Boston",32,"Michael Ott","339 College Av.","Trial"
"Dallas",4,"Sylvia Ringer","365 College Av.","Active"
"Boston",23,"Julia Heiniger","358 College Av.","Active"
"Chicago",39,"Mary Karsen","202 College Av.","Active"
"Dallas",40,"Susanne Miller","440 - 20th Ave.","Trial"
"Berne",9,"James Schneider","277 Seventh Av.","Active"
"Dallas",36,"John Steel","276 Upland Pl.","Suspended"
"Chicago",35,"George Karsen","412 College Av.","Suspended"
"Dallas",37,"Michael Clancy","19 Seventh Av.","Deleted"
"Lyon",2,"Anne Miller","20 Upland Pl.","Active"
"Dallas",0,"Laura Steel","429 Seventh Av.","Active"
"Lyon",28,"Susanne White","74 - 20th Ave.","Deleted"
"Paris",5,"Laura Miller","294 Seventh Av.","Active"
"Lyon",17,"Laura Ott","443 Seventh Av.","Active"
"New York",46,"Andrew May","172 Seventh Av.","Active"
"New York",44,"Sylvia Ott","361 College Av.","Active"
"Dallas",19,"Susanne Heiniger","86 - 20th Ave.","Active"
"Chicago",11,"Julia White","412 Upland Pl.","Active"
"Dallas",10,"Anne Fuller","135 Upland Pl.","Active"
"New York",41,"Bill King","546 College Av.","Deleted"
"Oslo",45,"Janet May","396 Seventh Av.","Active"
"Paris",18,"Sylvia Fuller","158 - 20th Ave.","Trial"
"San Francisco",48,"Robert White","549 Seventh Av.","Active"
"Paris",25,"Sylvia Steel","269 College Av.","Suspended"
"San Francisco",7,"James Peterson","231 Upland Pl.","Active"
"Oslo",42,"Robert Ott","503 Sixth Av.","Trial"
```

Save the file as data.csv to a known location.

The CSV file is created.

Create Data Adapter

Create the data adapter as follows:

Start Jaspersoft Studio.

Open the Project Explorer.

Right-click the project.

Click New → Data Adapter.

Set File name to: csv.xml.

Click Next.

Click CSV File.

Click Next.

Set Name to: CSV

Click File.

Browse to and select data.csv created in the previous section.

Click Get column names from the first row of the file.

Click Finish.

The data adapter is created.

Create main report and subreport

This section describes how to create the reports.

Create main report

Create the main report as follows:

Click File → New → Jasper Report.

Select Blank A4.

Click Next.

Set File name to: main.jrxml

Click Next.

Set Data Adapter to: csv.xml.

Click Finish.

The main report is created.

Note that the data source is not yet connected to the main report. This is where people run into trouble. Assigning the CSV data source does not mean that the report can "query" the data. An additional step is required, described later in this answer.

Create subreport

Create the subreport as follows:

Click File → New → Jasper Report.

Select Blank A4.

Click Next.

Set File name to: subreport.jrxml

Click Finish.

The subreport is created.

Create data set

Create a data set from the CSV file as follows:

Switch to the main report tab (main.jrxml) in Jaspersoft Studio.

Click the Design tab.

In the Outline tab, right-click main.

Click Create Dataset.

Set Dataset name to: csv

Click Next.

Set Data Adapter to: csv.xml.

Click Next.

Click >> to shuttle the fields to the right.

Click Finish.

The CSV data set is created.

Connect data set

In the Outline, expand main.

Right-click csv.

Click Connect to Main Dataset.

Set Data Adapter Language to: csv

Click Finish.

The data set is connected.

Create report fields

Create fields in the main report as follows:

In the Outline, right-click main.

Click Dataset and Query.

Set Language to: csv.

Click Read Fields.

Click OK.

The report fields are created.

Connect subreport

Connect the subreport to the main report as follows:

Drag a Subreport basic element onto the Title band of the main report.

Click Select a report file.

Choose Workspace resource.

Click Browse

Select subreport.jrxml.

Click OK.

Click OK again.

Click Finish.

Right-click the subreport element.

Click Size to Container → Fit Both.

In the Properties tab, click Subreport.

Switch to the main.jrxml report.

In the Outline tab, expand main → Fields

Select the fields (do not use the CSV fields)

Right-click the selected fields.

Click Copy.

Switch to the subreport.jrxml report.

Right-click Fields.

Click Paste.

Select the fields.

Drag and drop the fields onto the Detail band.

Return to the main.jrxml report.

Set Data Source Expression on the subreport to: $P{REPORT_DATA_SOURCE}.

Click Save All.

Click Build.

Run the main report.

The output resembles:

Title: Create and use CSV data in a subreport

Body:
Note: This solution drops the first result from the list in the subreport, which requires a workaround to resolve, which is not described in this answer.

Overview

The major steps include:

Create CSV file

Create data adapter

Create main report and subreport

Connect data set

Create report fields

Connect subreport

Create CSV file

Create the CSV file as follows:

Open a text editor.

Paste the following:

city,age,name,address,status
"Dallas",47,"Janet Fuller","445 Upland Pl.","Trial"
"Lyon",38,"Andrew Heiniger","347 College Av.","Active"
"Dallas",43,"Susanne Smith","2 Upland Pl.","Active"
"Berne",22,"Bill Ott","250 - 20th Ave.","Active"
"Boston",32,"Michael Ott","339 College Av.","Trial"
"Dallas",4,"Sylvia Ringer","365 College Av.","Active"
"Boston",23,"Julia Heiniger","358 College Av.","Active"
"Chicago",39,"Mary Karsen","202 College Av.","Active"
"Dallas",40,"Susanne Miller","440 - 20th Ave.","Trial"
"Berne",9,"James Schneider","277 Seventh Av.","Active"
"Dallas",36,"John Steel","276 Upland Pl.","Suspended"
"Chicago",35,"George Karsen","412 College Av.","Suspended"
"Dallas",37,"Michael Clancy","19 Seventh Av.","Deleted"
"Lyon",2,"Anne Miller","20 Upland Pl.","Active"
"Dallas",0,"Laura Steel","429 Seventh Av.","Active"
"Lyon",28,"Susanne White","74 - 20th Ave.","Deleted"
"Paris",5,"Laura Miller","294 Seventh Av.","Active"
"Lyon",17,"Laura Ott","443 Seventh Av.","Active"
"New York",46,"Andrew May","172 Seventh Av.","Active"
"New York",44,"Sylvia Ott","361 College Av.","Active"
"Dallas",19,"Susanne Heiniger","86 - 20th Ave.","Active"
"Chicago",11,"Julia White","412 Upland Pl.","Active"
"Dallas",10,"Anne Fuller","135 Upland Pl.","Active"
"New York",41,"Bill King","546 College Av.","Deleted"
"Oslo",45,"Janet May","396 Seventh Av.","Active"
"Paris",18,"Sylvia Fuller","158 - 20th Ave.","Trial"
"San Francisco",48,"Robert White","549 Seventh Av.","Active"
"Paris",25,"Sylvia Steel","269 College Av.","Suspended"
"San Francisco",7,"James Peterson","231 Upland Pl.","Active"
"Oslo",42,"Robert Ott","503 Sixth Av.","Trial"

Save the file as data.csv to a known location.

The CSV file is created.

Create Data Adapter

Create the data adapter as follows:

Start Jaspersoft Studio.

Open the Project Explorer.

Right-click the project.

Click New → Data Adapter.

Set File name to: csv.xml.

Click Next.

Click CSV File.

Click Next.

Set Name to: CSV

Click File.

Browse to and select data.csv created in the previous section.

Click Get column names from the first row of the file.

Click Finish.

The data adapter is created.

Create main report and subreport

This section describes how to create the reports.

Create main report

Create the main report as follows:

Click File → New → Jasper Report.

Select Blank A4.

Click Next.

Set File name to: main.jrxml

Click Next.

Set Data Adapter to: csv.xml.

Click Finish.

The main report is created.

Note that the data source is not yet connected to the main report. This is where people run into trouble. Assigning the CSV data source does not mean that the report can "query" the data. An additional step is required, described later in this answer.

Create subreport

Create the subreport as follows:

Click File → New → Jasper Report.

Select Blank A4.

Click Next.

Set File name to: subreport.jrxml

Click Finish.

The subreport is created.

Create data set

Create a data set from the CSV file as follows:

Switch to the main report tab (main.jrxml) in Jaspersoft Studio.

Click the Design tab.

In the Outline tab, right-click main.

Click Create Dataset.

Set Dataset name to: csv

Click Next.

Set Data Adapter to: csv.xml.

Click Next.

Click >> to shuttle the fields to the right.

Click Finish.

The CSV data set is created.

Connect data set

In the Outline, expand main.

Right-click csv.

Click Connect to Main Dataset.

Set Data Adapter Language to: csv

Click Finish.

The data set is connected.

Create report fields

Create fields in the main report as follows:

In the Outline, right-click main.

Click Dataset and Query.

Set Language to: csv.

Click Read Fields.

Click OK.

The report fields are created.

Connect subreport

Connect the subreport to the main report as follows:

Drag a Subreport basic element onto the Title band of the main report.

Click Select a report file.

Choose Workspace resource.

Click Browse

Select subreport.jrxml.

Click OK.

Click OK again.

Click Finish.

Right-click the subreport element.

Click Size to Container → Fit Both.

In the Properties tab, click Subreport.

Switch to the main.jrxml report.

In the Outline tab, expand main → Fields

Select the fields (do not use the CSV fields)

Right-click the selected fields.

Click Copy.

Switch to the subreport.jrxml report.

Right-click Fields.

Click Paste.

Select the fields.

Drag and drop the fields onto the Detail band.

Return to the main.jrxml report.

Set Data Source Expression on the subreport to: $P{REPORT_DATA_SOURCE}.

Click Save All.

Click Build.

Run the main report.

The output resembles: